diff --git a/.idea/modules.xml b/.idea/modules.xml index f23cddeafc3c..73e287cf10b9 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -122,6 +122,8 @@ + + diff --git a/community-main.iml b/community-main.iml index a42803e931d2..98b163ed9224 100644 --- a/community-main.iml +++ b/community-main.iml @@ -105,6 +105,8 @@ + + diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CreateConstructorParameterFromFieldFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CreateConstructorParameterFromFieldFix.java index 14e7478ad025..12cf5d8aae2f 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CreateConstructorParameterFromFieldFix.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CreateConstructorParameterFromFieldFix.java @@ -326,7 +326,7 @@ public class CreateConstructorParameterFromFieldFix implements IntentionAction { if (parameter == null) { continue; } - notNull(project, field, parameter); + notNull(field, parameter); cleanupElements.add(parameter); final PsiElement assignmentStatement = AssignFieldFromParameterAction.addFieldAssignmentStatement(project, field, parameter, editor); if (assignmentStatement != null) { @@ -338,11 +338,10 @@ public class CreateConstructorParameterFromFieldFix implements IntentionAction { return created; } - private static void notNull(Project project, PsiField field, PsiParameter parameter) { - final String notNull = NullableNotNullManager.getInstance(field.getProject()).getNotNull(field); + private static void notNull(PsiField field, PsiParameter parameter) { + final PsiAnnotation notNull = NullableNotNullManager.getInstance(field.getProject()).copyNotNullAnnotation(field); if (notNull != null) { - final PsiAnnotation annotation = JavaPsiFacade.getElementFactory(project).createAnnotationFromText("@" + notNull, field); - parameter.getModifierList().addBefore(annotation, null); + parameter.getModifierList().addBefore(notNull, null); } } diff --git a/java/java-impl/src/com/intellij/codeInsight/generation/GenerateConstructorHandler.java b/java/java-impl/src/com/intellij/codeInsight/generation/GenerateConstructorHandler.java index 7b36bdc33c9d..7990c5872b16 100644 --- a/java/java-impl/src/com/intellij/codeInsight/generation/GenerateConstructorHandler.java +++ b/java/java-impl/src/com/intellij/codeInsight/generation/GenerateConstructorHandler.java @@ -265,9 +265,9 @@ public class GenerateConstructorHandler extends GenerateMembersHandlerBase { PsiParameter parm = factory.createParameter(parmName, field.getType(), aClass); final NullableNotNullManager nullableManager = NullableNotNullManager.getInstance(field.getProject()); - final String notNull = nullableManager.getNotNull(field); + final PsiAnnotation notNull = nullableManager.copyNotNullAnnotation(field); if (notNull != null) { - parm.getModifierList().addAfter(factory.createAnnotationFromText("@" + notNull, field), null); + parm.getModifierList().addAfter(notNull, null); } constructor.getParameterList().add(parm); diff --git a/java/java-impl/src/com/intellij/codeInsight/intention/impl/FieldFromParameterUtils.java b/java/java-impl/src/com/intellij/codeInsight/intention/impl/FieldFromParameterUtils.java index 37fb882e5e66..27ca12630905 100644 --- a/java/java-impl/src/com/intellij/codeInsight/intention/impl/FieldFromParameterUtils.java +++ b/java/java-impl/src/com/intellij/codeInsight/intention/impl/FieldFromParameterUtils.java @@ -187,14 +187,14 @@ public final class FieldFromParameterUtils { modifierList.setModifierProperty(PsiModifier.FINAL, isFinal); final NullableNotNullManager manager = NullableNotNullManager.getInstance(project); - final PsiAnnotation nullable = manager.getNullableAnnotation(parameter, false); - if (nullable != null && !manager.isContainerAnnotation(nullable)) { - modifierList.addAfter(factory.createAnnotationFromText("@" + nullable.getQualifiedName(), field), null); + final PsiAnnotation nullable = manager.copyNullableAnnotation(parameter); + if (nullable != null) { + modifierList.addAfter(nullable, null); } else if (isFinal) { - final PsiAnnotation notNull = manager.getNotNullAnnotation(parameter, false); - if (notNull != null && !manager.isContainerAnnotation(notNull)) { - modifierList.addAfter(factory.createAnnotationFromText("@" + notNull.getQualifiedName(), field), null); + final PsiAnnotation notNull = manager.copyNotNullAnnotation(parameter); + if (notNull != null) { + modifierList.addAfter(notNull, null); } } diff --git a/java/java-impl/src/com/intellij/refactoring/inheritanceToDelegation/InheritanceToDelegationProcessor.java b/java/java-impl/src/com/intellij/refactoring/inheritanceToDelegation/InheritanceToDelegationProcessor.java index 6c74e8a60193..f75eb25cb4c5 100644 --- a/java/java-impl/src/com/intellij/refactoring/inheritanceToDelegation/InheritanceToDelegationProcessor.java +++ b/java/java-impl/src/com/intellij/refactoring/inheritanceToDelegation/InheritanceToDelegationProcessor.java @@ -518,14 +518,14 @@ public class InheritanceToDelegationProcessor extends BaseRefactoringProcessor { final PsiModifierList modifierList = methodToAdd.getModifierList(); final NullableNotNullManager manager = NullableNotNullManager.getInstance(myProject); modifierList.setModifierProperty(PsiModifier.ABSTRACT, false); - final String nullable = manager.getNullable(method); + final PsiAnnotation nullable = manager.copyNullableAnnotation(method); if (nullable != null) { - modifierList.addAfter(myFactory.createAnnotationFromText("@" + nullable, methodToAdd), null); + modifierList.addAfter(nullable, null); } else { - final String notNull = manager.getNotNull(method); + final PsiAnnotation notNull = manager.copyNotNullAnnotation(method); if (notNull != null) { - modifierList.addAfter(myFactory.createAnnotationFromText("@" + notNull, methodToAdd), null); + modifierList.addAfter(notNull, null); } } diff --git a/java/java-psi-api/src/com/intellij/codeInsight/NullableNotNullManager.java b/java/java-psi-api/src/com/intellij/codeInsight/NullableNotNullManager.java index 0f19a245ff18..00d76638cc97 100644 --- a/java/java-psi-api/src/com/intellij/codeInsight/NullableNotNullManager.java +++ b/java/java-psi-api/src/com/intellij/codeInsight/NullableNotNullManager.java @@ -95,13 +95,25 @@ public class NullableNotNullManager implements PersistentStateComponent public String getDefaultNullable() { return myDefaultNullable; } - + @Nullable public String getNullable(@NotNull PsiModifierListOwner owner) { PsiAnnotation annotation = getNullableAnnotation(owner, false); return annotation == null ? null : annotation.getQualifiedName(); } + private String checkContainer(PsiAnnotation annotation, boolean acceptContainer) { + if (annotation == null) { + return null; + } + else { + if (!acceptContainer && isContainerAnnotation(annotation)) { + return null; + } + return annotation.getQualifiedName(); + } + } + @Nullable public PsiAnnotation getNullableAnnotation(@NotNull PsiModifierListOwner owner, boolean checkBases) { return findNullabilityAnnotation(owner, checkBases, true); @@ -127,6 +139,23 @@ public class NullableNotNullManager implements PersistentStateComponent return findNullabilityAnnotation(owner, checkBases, false); } + public PsiAnnotation copyNotNullAnnotation(PsiModifierListOwner owner) { + return copyAnnotation(owner, getNotNullAnnotation(owner, false)); + } + + public PsiAnnotation copyNullableAnnotation(PsiModifierListOwner owner) { + return copyAnnotation(owner, getNullableAnnotation(owner, false)); + } + + private PsiAnnotation copyAnnotation(PsiModifierListOwner owner, + PsiAnnotation annotation) { + final String notNull = checkContainer(annotation, false); + if (notNull != null) { + return JavaPsiFacade.getElementFactory(owner.getProject()).createAnnotationFromText("@" + notNull, owner); + } + return null; + } + @Nullable public String getNotNull(@NotNull PsiModifierListOwner owner) { PsiAnnotation annotation = getNotNullAnnotation(owner, false); diff --git a/java/java-psi-api/src/com/intellij/psi/util/PropertyUtil.java b/java/java-psi-api/src/com/intellij/psi/util/PropertyUtil.java index 5a50bc8ea4ae..4b7de701c325 100644 --- a/java/java-psi-api/src/com/intellij/psi/util/PropertyUtil.java +++ b/java/java-psi-api/src/com/intellij/psi/util/PropertyUtil.java @@ -413,7 +413,7 @@ public class PropertyUtil { PsiUtil.setModifierProperty(getMethod, PsiModifier.STATIC, true); } - annotateWithNullableStuff(field, factory, getMethod); + annotateWithNullableStuff(field, getMethod); PsiCodeBlock body = factory.createCodeBlockFromText("{\nreturn " + name + ";\n}", null); getMethod.getBody().replace(body); @@ -466,7 +466,7 @@ public class PropertyUtil { String parameterName = codeStyleManager.propertyNameToVariableName(propertyName, VariableKind.PARAMETER); PsiParameter param = factory.createParameter(parameterName, field.getType()); - annotateWithNullableStuff(field, factory, param); + annotateWithNullableStuff(field, param); setMethod.getParameterList().add(param); PsiUtil.setModifierProperty(setMethod, PsiModifier.PUBLIC, true); @@ -506,27 +506,26 @@ public class PropertyUtil { } private static void annotateWithNullableStuff(final PsiModifierListOwner field, - final PsiElementFactory factory, final PsiModifierListOwner listOwner) throws IncorrectOperationException { final NullableNotNullManager manager = NullableNotNullManager.getInstance(field.getProject()); - final String notNull = manager.getNotNull(field); + final PsiAnnotation notNull = manager.copyNotNullAnnotation(field); if (notNull != null) { - annotate(factory, listOwner, notNull); + annotate(listOwner, notNull); } else { - final String nullable = manager.getNullable(field); + final PsiAnnotation nullable = manager.copyNullableAnnotation(field); if (nullable != null) { - annotate(factory, listOwner, nullable); + annotate(listOwner, nullable); } } } - private static void annotate(final PsiElementFactory factory, final PsiModifierListOwner listOwner, final String annotationQName) + private static void annotate(final PsiModifierListOwner listOwner, final PsiAnnotation annotation) throws IncorrectOperationException { final PsiModifierList modifierList = listOwner.getModifierList(); LOG.assertTrue(modifierList != null); - modifierList.addAfter(factory.createAnnotationFromText("@" + annotationQName, listOwner), null); + modifierList.addAfter(annotation, null); } public static String suggestPropertyName(@NotNull PsiField field) { diff --git a/json/gen/com/intellij/json/JsonElementTypes.java b/json/gen/com/intellij/json/JsonElementTypes.java new file mode 100644 index 000000000000..cbff0ab596f9 --- /dev/null +++ b/json/gen/com/intellij/json/JsonElementTypes.java @@ -0,0 +1,74 @@ +// This is a generated file. Not intended for manual editing. +package com.intellij.json; + +import com.intellij.psi.tree.IElementType; +import com.intellij.psi.PsiElement; +import com.intellij.lang.ASTNode; +import com.intellij.json.psi.impl.*; + +public interface JsonElementTypes { + + IElementType ARRAY = new JsonElementType("ARRAY"); + IElementType BOOLEAN_LITERAL = new JsonElementType("BOOLEAN_LITERAL"); + IElementType LITERAL = new JsonElementType("LITERAL"); + IElementType NULL_LITERAL = new JsonElementType("NULL_LITERAL"); + IElementType NUMBER_LITERAL = new JsonElementType("NUMBER_LITERAL"); + IElementType OBJECT = new JsonElementType("OBJECT"); + IElementType PROPERTY = new JsonElementType("PROPERTY"); + IElementType REFERENCE_EXPRESSION = new JsonElementType("REFERENCE_EXPRESSION"); + IElementType STRING_LITERAL = new JsonElementType("STRING_LITERAL"); + IElementType VALUE = new JsonElementType("VALUE"); + + IElementType BLOCK_COMMENT = new JsonTokenType("BLOCK_COMMENT"); + IElementType COLON = new JsonTokenType(":"); + IElementType COMMA = new JsonTokenType(","); + IElementType DOUBLE_QUOTED_STRING = new JsonTokenType("DOUBLE_QUOTED_STRING"); + IElementType FALSE = new JsonTokenType("false"); + IElementType INDENTIFIER = new JsonTokenType("INDENTIFIER"); + IElementType LINE_COMMENT = new JsonTokenType("LINE_COMMENT"); + IElementType L_BRACKET = new JsonTokenType("["); + IElementType L_CURLY = new JsonTokenType("{"); + IElementType NULL = new JsonTokenType("null"); + IElementType NUMBER = new JsonTokenType("NUMBER"); + IElementType R_BRACKET = new JsonTokenType("]"); + IElementType R_CURLY = new JsonTokenType("}"); + IElementType SINGLE_QUOTED_STRING = new JsonTokenType("SINGLE_QUOTED_STRING"); + IElementType TRUE = new JsonTokenType("true"); + + class Factory { + public static PsiElement createElement(ASTNode node) { + IElementType type = node.getElementType(); + if (type == ARRAY) { + return new JsonArrayImpl(node); + } + else if (type == BOOLEAN_LITERAL) { + return new JsonBooleanLiteralImpl(node); + } + else if (type == LITERAL) { + return new JsonLiteralImpl(node); + } + else if (type == NULL_LITERAL) { + return new JsonNullLiteralImpl(node); + } + else if (type == NUMBER_LITERAL) { + return new JsonNumberLiteralImpl(node); + } + else if (type == OBJECT) { + return new JsonObjectImpl(node); + } + else if (type == PROPERTY) { + return new JsonPropertyImpl(node); + } + else if (type == REFERENCE_EXPRESSION) { + return new JsonReferenceExpressionImpl(node); + } + else if (type == STRING_LITERAL) { + return new JsonStringLiteralImpl(node); + } + else if (type == VALUE) { + return new JsonValueImpl(node); + } + throw new AssertionError("Unknown element type: " + type); + } + } +} diff --git a/json/gen/com/intellij/json/JsonLexer.java b/json/gen/com/intellij/json/JsonLexer.java new file mode 100644 index 000000000000..ce771b9d23b5 --- /dev/null +++ b/json/gen/com/intellij/json/JsonLexer.java @@ -0,0 +1,12 @@ +package com.intellij.json; + +import com.intellij.lexer.FlexAdapter; + +/** + * @author Mikhail Golubev + */ +public class JsonLexer extends FlexAdapter { + public JsonLexer() { + super(new _JsonLexer()); + } +} diff --git a/json/gen/com/intellij/json/JsonParser.java b/json/gen/com/intellij/json/JsonParser.java new file mode 100644 index 000000000000..954a71a7dfc2 --- /dev/null +++ b/json/gen/com/intellij/json/JsonParser.java @@ -0,0 +1,387 @@ +// This is a generated file. Not intended for manual editing. +package com.intellij.json; + +import com.intellij.lang.PsiBuilder; +import com.intellij.lang.PsiBuilder.Marker; +import com.intellij.openapi.diagnostic.Logger; +import static com.intellij.json.JsonElementTypes.*; +import static com.intellij.json.psi.JsonParserUtil.*; +import com.intellij.psi.tree.IElementType; +import com.intellij.lang.ASTNode; +import com.intellij.psi.tree.TokenSet; +import com.intellij.lang.PsiParser; + +@SuppressWarnings({"SimplifiableIfStatement", "UnusedAssignment"}) +public class JsonParser implements PsiParser { + + public static final Logger LOG_ = Logger.getInstance("com.intellij.json.JsonParser"); + + public ASTNode parse(IElementType root_, PsiBuilder builder_) { + boolean result_; + builder_ = adapt_builder_(root_, builder_, this, EXTENDS_SETS_); + Marker marker_ = enter_section_(builder_, 0, _COLLAPSE_, null); + if (root_ == ARRAY) { + result_ = array(builder_, 0); + } + else if (root_ == BOOLEAN_LITERAL) { + result_ = boolean_literal(builder_, 0); + } + else if (root_ == LITERAL) { + result_ = literal(builder_, 0); + } + else if (root_ == NULL_LITERAL) { + result_ = null_literal(builder_, 0); + } + else if (root_ == NUMBER_LITERAL) { + result_ = number_literal(builder_, 0); + } + else if (root_ == OBJECT) { + result_ = object(builder_, 0); + } + else if (root_ == PROPERTY) { + result_ = property(builder_, 0); + } + else if (root_ == REFERENCE_EXPRESSION) { + result_ = reference_expression(builder_, 0); + } + else if (root_ == STRING_LITERAL) { + result_ = string_literal(builder_, 0); + } + else if (root_ == VALUE) { + result_ = value(builder_, 0); + } + else { + result_ = parse_root_(root_, builder_, 0); + } + exit_section_(builder_, 0, marker_, root_, result_, true, TRUE_CONDITION); + return builder_.getTreeBuilt(); + } + + protected boolean parse_root_(final IElementType root_, final PsiBuilder builder_, final int level_) { + return json(builder_, level_ + 1); + } + + public static final TokenSet[] EXTENDS_SETS_ = new TokenSet[] { + create_token_set_(BOOLEAN_LITERAL, LITERAL, NULL_LITERAL, NUMBER_LITERAL, + STRING_LITERAL), + create_token_set_(ARRAY, BOOLEAN_LITERAL, LITERAL, NULL_LITERAL, + NUMBER_LITERAL, OBJECT, REFERENCE_EXPRESSION, STRING_LITERAL, + VALUE), + }; + + /* ********************************************************** */ + // '[' array_element* ']' + public static boolean array(PsiBuilder builder_, int level_) { + if (!recursion_guard_(builder_, level_, "array")) return false; + if (!nextTokenIs(builder_, L_BRACKET)) return false; + boolean result_; + boolean pinned_; + Marker marker_ = enter_section_(builder_, level_, _NONE_, null); + result_ = consumeToken(builder_, L_BRACKET); + pinned_ = result_; // pin = 1 + result_ = result_ && report_error_(builder_, array_1(builder_, level_ + 1)); + result_ = pinned_ && consumeToken(builder_, R_BRACKET) && result_; + exit_section_(builder_, level_, marker_, ARRAY, result_, pinned_, null); + return result_ || pinned_; + } + + // array_element* + private static boolean array_1(PsiBuilder builder_, int level_) { + if (!recursion_guard_(builder_, level_, "array_1")) return false; + int pos_ = current_position_(builder_); + while (true) { + if (!array_element(builder_, level_ + 1)) break; + if (!empty_element_parsed_guard_(builder_, "array_1", pos_)) break; + pos_ = current_position_(builder_); + } + return true; + } + + /* ********************************************************** */ + // value (','|&']') + static boolean array_element(PsiBuilder builder_, int level_) { + if (!recursion_guard_(builder_, level_, "array_element")) return false; + boolean result_; + boolean pinned_; + Marker marker_ = enter_section_(builder_, level_, _NONE_, null); + result_ = value(builder_, level_ + 1); + pinned_ = result_; // pin = 1 + result_ = result_ && array_element_1(builder_, level_ + 1); + exit_section_(builder_, level_, marker_, null, result_, pinned_, not_bracket_or_next_value_parser_); + return result_ || pinned_; + } + + // ','|&']' + private static boolean array_element_1(PsiBuilder builder_, int level_) { + if (!recursion_guard_(builder_, level_, "array_element_1")) return false; + boolean result_; + Marker marker_ = enter_section_(builder_); + result_ = consumeToken(builder_, COMMA); + if (!result_) result_ = array_element_1_1(builder_, level_ + 1); + exit_section_(builder_, marker_, null, result_); + return result_; + } + + // &']' + private static boolean array_element_1_1(PsiBuilder builder_, int level_) { + if (!recursion_guard_(builder_, level_, "array_element_1_1")) return false; + boolean result_; + Marker marker_ = enter_section_(builder_, level_, _AND_, null); + result_ = consumeToken(builder_, R_BRACKET); + exit_section_(builder_, level_, marker_, null, result_, false, null); + return result_; + } + + /* ********************************************************** */ + // TRUE | FALSE + public static boolean boolean_literal(PsiBuilder builder_, int level_) { + if (!recursion_guard_(builder_, level_, "boolean_literal")) return false; + if (!nextTokenIs(builder_, "", FALSE, TRUE)) return false; + boolean result_; + Marker marker_ = enter_section_(builder_, level_, _COLLAPSE_, ""); + result_ = consumeToken(builder_, TRUE); + if (!result_) result_ = consumeToken(builder_, FALSE); + exit_section_(builder_, level_, marker_, BOOLEAN_LITERAL, result_, false, null); + return result_; + } + + /* ********************************************************** */ + // value + static boolean json(PsiBuilder builder_, int level_) { + return value(builder_, level_ + 1); + } + + /* ********************************************************** */ + // string_literal | number_literal | boolean_literal | null_literal + public static boolean literal(PsiBuilder builder_, int level_) { + if (!recursion_guard_(builder_, level_, "literal")) return false; + boolean result_; + Marker marker_ = enter_section_(builder_, level_, _COLLAPSE_, ""); + result_ = string_literal(builder_, level_ + 1); + if (!result_) result_ = number_literal(builder_, level_ + 1); + if (!result_) result_ = boolean_literal(builder_, level_ + 1); + if (!result_) result_ = null_literal(builder_, level_ + 1); + exit_section_(builder_, level_, marker_, LITERAL, result_, false, null); + return result_; + } + + /* ********************************************************** */ + // !('}'|value) + static boolean not_brace_or_next_value(PsiBuilder builder_, int level_) { + if (!recursion_guard_(builder_, level_, "not_brace_or_next_value")) return false; + boolean result_; + Marker marker_ = enter_section_(builder_, level_, _NOT_, null); + result_ = !not_brace_or_next_value_0(builder_, level_ + 1); + exit_section_(builder_, level_, marker_, null, result_, false, null); + return result_; + } + + // '}'|value + private static boolean not_brace_or_next_value_0(PsiBuilder builder_, int level_) { + if (!recursion_guard_(builder_, level_, "not_brace_or_next_value_0")) return false; + boolean result_; + Marker marker_ = enter_section_(builder_); + result_ = consumeToken(builder_, R_CURLY); + if (!result_) result_ = value(builder_, level_ + 1); + exit_section_(builder_, marker_, null, result_); + return result_; + } + + /* ********************************************************** */ + // !(']'|value) + static boolean not_bracket_or_next_value(PsiBuilder builder_, int level_) { + if (!recursion_guard_(builder_, level_, "not_bracket_or_next_value")) return false; + boolean result_; + Marker marker_ = enter_section_(builder_, level_, _NOT_, null); + result_ = !not_bracket_or_next_value_0(builder_, level_ + 1); + exit_section_(builder_, level_, marker_, null, result_, false, null); + return result_; + } + + // ']'|value + private static boolean not_bracket_or_next_value_0(PsiBuilder builder_, int level_) { + if (!recursion_guard_(builder_, level_, "not_bracket_or_next_value_0")) return false; + boolean result_; + Marker marker_ = enter_section_(builder_); + result_ = consumeToken(builder_, R_BRACKET); + if (!result_) result_ = value(builder_, level_ + 1); + exit_section_(builder_, marker_, null, result_); + return result_; + } + + /* ********************************************************** */ + // NULL + public static boolean null_literal(PsiBuilder builder_, int level_) { + if (!recursion_guard_(builder_, level_, "null_literal")) return false; + if (!nextTokenIs(builder_, NULL)) return false; + boolean result_; + Marker marker_ = enter_section_(builder_); + result_ = consumeToken(builder_, NULL); + exit_section_(builder_, marker_, NULL_LITERAL, result_); + return result_; + } + + /* ********************************************************** */ + // NUMBER + public static boolean number_literal(PsiBuilder builder_, int level_) { + if (!recursion_guard_(builder_, level_, "number_literal")) return false; + if (!nextTokenIs(builder_, NUMBER)) return false; + boolean result_; + Marker marker_ = enter_section_(builder_); + result_ = consumeToken(builder_, NUMBER); + exit_section_(builder_, marker_, NUMBER_LITERAL, result_); + return result_; + } + + /* ********************************************************** */ + // '{' object_element* '}' + public static boolean object(PsiBuilder builder_, int level_) { + if (!recursion_guard_(builder_, level_, "object")) return false; + if (!nextTokenIs(builder_, L_CURLY)) return false; + boolean result_; + boolean pinned_; + Marker marker_ = enter_section_(builder_, level_, _NONE_, null); + result_ = consumeToken(builder_, L_CURLY); + pinned_ = result_; // pin = 1 + result_ = result_ && report_error_(builder_, object_1(builder_, level_ + 1)); + result_ = pinned_ && consumeToken(builder_, R_CURLY) && result_; + exit_section_(builder_, level_, marker_, OBJECT, result_, pinned_, null); + return result_ || pinned_; + } + + // object_element* + private static boolean object_1(PsiBuilder builder_, int level_) { + if (!recursion_guard_(builder_, level_, "object_1")) return false; + int pos_ = current_position_(builder_); + while (true) { + if (!object_element(builder_, level_ + 1)) break; + if (!empty_element_parsed_guard_(builder_, "object_1", pos_)) break; + pos_ = current_position_(builder_); + } + return true; + } + + /* ********************************************************** */ + // property (','|&'}') + static boolean object_element(PsiBuilder builder_, int level_) { + if (!recursion_guard_(builder_, level_, "object_element")) return false; + boolean result_; + boolean pinned_; + Marker marker_ = enter_section_(builder_, level_, _NONE_, null); + result_ = property(builder_, level_ + 1); + pinned_ = result_; // pin = 1 + result_ = result_ && object_element_1(builder_, level_ + 1); + exit_section_(builder_, level_, marker_, null, result_, pinned_, not_brace_or_next_value_parser_); + return result_ || pinned_; + } + + // ','|&'}' + private static boolean object_element_1(PsiBuilder builder_, int level_) { + if (!recursion_guard_(builder_, level_, "object_element_1")) return false; + boolean result_; + Marker marker_ = enter_section_(builder_); + result_ = consumeToken(builder_, COMMA); + if (!result_) result_ = object_element_1_1(builder_, level_ + 1); + exit_section_(builder_, marker_, null, result_); + return result_; + } + + // &'}' + private static boolean object_element_1_1(PsiBuilder builder_, int level_) { + if (!recursion_guard_(builder_, level_, "object_element_1_1")) return false; + boolean result_; + Marker marker_ = enter_section_(builder_, level_, _AND_, null); + result_ = consumeToken(builder_, R_CURLY); + exit_section_(builder_, level_, marker_, null, result_, false, null); + return result_; + } + + /* ********************************************************** */ + // property_name (':' value) + public static boolean property(PsiBuilder builder_, int level_) { + if (!recursion_guard_(builder_, level_, "property")) return false; + boolean result_; + boolean pinned_; + Marker marker_ = enter_section_(builder_, level_, _NONE_, ""); + result_ = property_name(builder_, level_ + 1); + pinned_ = result_; // pin = 1 + result_ = result_ && property_1(builder_, level_ + 1); + exit_section_(builder_, level_, marker_, PROPERTY, result_, pinned_, null); + return result_ || pinned_; + } + + // ':' value + private static boolean property_1(PsiBuilder builder_, int level_) { + if (!recursion_guard_(builder_, level_, "property_1")) return false; + boolean result_; + boolean pinned_; + Marker marker_ = enter_section_(builder_, level_, _NONE_, null); + result_ = consumeToken(builder_, COLON); + pinned_ = result_; // pin = 1 + result_ = result_ && value(builder_, level_ + 1); + exit_section_(builder_, level_, marker_, null, result_, pinned_, null); + return result_ || pinned_; + } + + /* ********************************************************** */ + // literal | reference_expression + static boolean property_name(PsiBuilder builder_, int level_) { + if (!recursion_guard_(builder_, level_, "property_name")) return false; + boolean result_; + Marker marker_ = enter_section_(builder_); + result_ = literal(builder_, level_ + 1); + if (!result_) result_ = reference_expression(builder_, level_ + 1); + exit_section_(builder_, marker_, null, result_); + return result_; + } + + /* ********************************************************** */ + // INDENTIFIER + public static boolean reference_expression(PsiBuilder builder_, int level_) { + if (!recursion_guard_(builder_, level_, "reference_expression")) return false; + if (!nextTokenIs(builder_, INDENTIFIER)) return false; + boolean result_; + Marker marker_ = enter_section_(builder_); + result_ = consumeToken(builder_, INDENTIFIER); + exit_section_(builder_, marker_, REFERENCE_EXPRESSION, result_); + return result_; + } + + /* ********************************************************** */ + // SINGLE_QUOTED_STRING | DOUBLE_QUOTED_STRING + public static boolean string_literal(PsiBuilder builder_, int level_) { + if (!recursion_guard_(builder_, level_, "string_literal")) return false; + if (!nextTokenIs(builder_, "", DOUBLE_QUOTED_STRING, SINGLE_QUOTED_STRING)) return false; + boolean result_; + Marker marker_ = enter_section_(builder_, level_, _COLLAPSE_, ""); + result_ = consumeToken(builder_, SINGLE_QUOTED_STRING); + if (!result_) result_ = consumeToken(builder_, DOUBLE_QUOTED_STRING); + exit_section_(builder_, level_, marker_, STRING_LITERAL, result_, false, null); + return result_; + } + + /* ********************************************************** */ + // object | array | literal | reference_expression + public static boolean value(PsiBuilder builder_, int level_) { + if (!recursion_guard_(builder_, level_, "value")) return false; + boolean result_; + Marker marker_ = enter_section_(builder_, level_, _COLLAPSE_, ""); + result_ = object(builder_, level_ + 1); + if (!result_) result_ = array(builder_, level_ + 1); + if (!result_) result_ = literal(builder_, level_ + 1); + if (!result_) result_ = reference_expression(builder_, level_ + 1); + exit_section_(builder_, level_, marker_, VALUE, result_, false, null); + return result_; + } + + final static Parser not_brace_or_next_value_parser_ = new Parser() { + public boolean parse(PsiBuilder builder_, int level_) { + return not_brace_or_next_value(builder_, level_ + 1); + } + }; + final static Parser not_bracket_or_next_value_parser_ = new Parser() { + public boolean parse(PsiBuilder builder_, int level_) { + return not_bracket_or_next_value(builder_, level_ + 1); + } + }; +} diff --git a/json/gen/com/intellij/json/_JsonLexer.flex b/json/gen/com/intellij/json/_JsonLexer.flex new file mode 100644 index 000000000000..eb11beb9674d --- /dev/null +++ b/json/gen/com/intellij/json/_JsonLexer.flex @@ -0,0 +1,54 @@ +package com.intellij.json; +import com.intellij.lexer.*; +import com.intellij.psi.tree.IElementType; +import static com.intellij.json.JsonElementTypes.*; + +%% + +%{ + public _JsonLexer() { + this((java.io.Reader)null); + } +%} + +%public +%class _JsonLexer +%implements FlexLexer +%function advance +%type IElementType +%unicode + +EOL="\r"|"\n"|"\r\n" +LINE_WS=[\ \t\f] +WHITE_SPACE=({LINE_WS}|{EOL})+ + +LINE_COMMENT="//".* +BLOCK_COMMENT="/"\*([^*]|\*+[^*/])*(\*+"/")? +DOUBLE_QUOTED_STRING=\"([^\\\"\r\n]|\\[^\r\n])*\"? +SINGLE_QUOTED_STRING='([^\\'\r\n]|\\[^\r\n])*'? +NUMBER=-?[0-9]+(\.[0-9]+([eE][+-]?[0-9]+)?)? +INDENTIFIER=[:jletter:] [:jletterdigit:]* + +%% + { + {WHITE_SPACE} { return com.intellij.psi.TokenType.WHITE_SPACE; } + + "{" { return L_CURLY; } + "}" { return R_CURLY; } + "[" { return L_BRACKET; } + "]" { return R_BRACKET; } + "," { return COMMA; } + ":" { return COLON; } + "true" { return TRUE; } + "false" { return FALSE; } + "null" { return NULL; } + + {LINE_COMMENT} { return LINE_COMMENT; } + {BLOCK_COMMENT} { return BLOCK_COMMENT; } + {DOUBLE_QUOTED_STRING} { return DOUBLE_QUOTED_STRING; } + {SINGLE_QUOTED_STRING} { return SINGLE_QUOTED_STRING; } + {NUMBER} { return NUMBER; } + {INDENTIFIER} { return INDENTIFIER; } + + [^] { return com.intellij.psi.TokenType.BAD_CHARACTER; } +} diff --git a/json/gen/com/intellij/json/_JsonLexer.java b/json/gen/com/intellij/json/_JsonLexer.java new file mode 100644 index 000000000000..e8eccd6b85f0 --- /dev/null +++ b/json/gen/com/intellij/json/_JsonLexer.java @@ -0,0 +1,670 @@ +/* The following code was generated by JFlex 1.4.3 on 9/8/14 10:09 PM */ + +package com.intellij.json; +import com.intellij.lexer.*; +import com.intellij.psi.tree.IElementType; +import static com.intellij.json.JsonElementTypes.*; + + +/** + * This class is a scanner generated by + * JFlex 1.4.3 + * on 9/8/14 10:09 PM from the specification file + * /home/east825/develop/repos/IDEA/community/json/gen/com/intellij/json/_JsonLexer.flex + */ +public class _JsonLexer implements FlexLexer { + /** initial size of the lookahead buffer */ + private static final int ZZ_BUFFERSIZE = 16384; + + /** lexical states */ + public static final int YYINITIAL = 0; + + /** + * ZZ_LEXSTATE[l] is the state in the DFA for the lexical state l + * ZZ_LEXSTATE[l+1] is the state in the DFA for the lexical state l + * at the beginning of a line + * l is of the form l = 2*k, k a non negative integer + */ + private static final int ZZ_LEXSTATE[] = { + 0, 0 + }; + + /** + * Translates characters to character classes + */ + private static final String ZZ_CMAP_PACKED = + "\11\17\1\3\1\2\1\0\1\3\1\1\16\17\4\0\1\3\1\0"+ + "\1\6\1\0\1\16\2\0\1\10\2\0\1\5\1\15\1\24\1\11"+ + "\1\13\1\4\12\12\1\25\6\0\4\16\1\14\25\16\1\22\1\7"+ + "\1\23\1\0\1\16\1\0\1\33\3\16\1\31\1\32\5\16\1\34"+ + "\1\16\1\36\3\16\1\27\1\35\1\26\1\30\5\16\1\20\1\0"+ + "\1\21\1\0\41\17\2\0\4\16\4\0\1\16\2\0\1\17\7\0"+ + "\1\16\4\0\1\16\5\0\27\16\1\0\37\16\1\0\u01ca\16\4\0"+ + "\14\16\16\0\5\16\7\0\1\16\1\0\1\16\21\0\160\17\5\16"+ + "\1\0\2\16\2\0\4\16\10\0\1\16\1\0\3\16\1\0\1\16"+ + "\1\0\24\16\1\0\123\16\1\0\213\16\1\0\5\17\2\0\236\16"+ + "\11\0\46\16\2\0\1\16\7\0\47\16\7\0\1\16\1\0\55\17"+ + "\1\0\1\17\1\0\2\17\1\0\2\17\1\0\1\17\10\0\33\16"+ + "\5\0\3\16\15\0\5\17\6\0\1\16\4\0\13\17\5\0\53\16"+ + "\37\17\4\0\2\16\1\17\143\16\1\0\1\16\10\17\1\0\6\17"+ + "\2\16\2\17\1\0\4\17\2\16\12\17\3\16\2\0\1\16\17\0"+ + "\1\17\1\16\1\17\36\16\33\17\2\0\131\16\13\17\1\16\16\0"+ + "\12\17\41\16\11\17\2\16\4\0\1\16\5\0\26\16\4\17\1\16"+ + "\11\17\1\16\3\17\1\16\5\17\22\0\31\16\3\17\104\0\1\16"+ + "\1\0\13\16\67\0\33\17\1\0\4\17\66\16\3\17\1\16\22\17"+ + "\1\16\7\17\12\16\2\17\2\0\12\17\1\0\7\16\1\0\7\16"+ + "\1\0\3\17\1\0\10\16\2\0\2\16\2\0\26\16\1\0\7\16"+ + "\1\0\1\16\3\0\4\16\2\0\1\17\1\16\7\17\2\0\2\17"+ + "\2\0\3\17\1\16\10\0\1\17\4\0\2\16\1\0\3\16\2\17"+ + "\2\0\12\17\4\16\7\0\1\16\5\0\3\17\1\0\6\16\4\0"+ + "\2\16\2\0\26\16\1\0\7\16\1\0\2\16\1\0\2\16\1\0"+ + "\2\16\2\0\1\17\1\0\5\17\4\0\2\17\2\0\3\17\3\0"+ + "\1\17\7\0\4\16\1\0\1\16\7\0\14\17\3\16\1\17\13\0"+ + "\3\17\1\0\11\16\1\0\3\16\1\0\26\16\1\0\7\16\1\0"+ + "\2\16\1\0\5\16\2\0\1\17\1\16\10\17\1\0\3\17\1\0"+ + "\3\17\2\0\1\16\17\0\2\16\2\17\2\0\12\17\1\0\1\16"+ + "\17\0\3\17\1\0\10\16\2\0\2\16\2\0\26\16\1\0\7\16"+ + "\1\0\2\16\1\0\5\16\2\0\1\17\1\16\7\17\2\0\2\17"+ + "\2\0\3\17\10\0\2\17\4\0\2\16\1\0\3\16\2\17\2\0"+ + "\12\17\1\0\1\16\20\0\1\17\1\16\1\0\6\16\3\0\3\16"+ + "\1\0\4\16\3\0\2\16\1\0\1\16\1\0\2\16\3\0\2\16"+ + "\3\0\3\16\3\0\14\16\4\0\5\17\3\0\3\17\1\0\4\17"+ + "\2\0\1\16\6\0\1\17\16\0\12\17\11\0\1\16\7\0\3\17"+ + "\1\0\10\16\1\0\3\16\1\0\27\16\1\0\12\16\1\0\5\16"+ + "\3\0\1\16\7\17\1\0\3\17\1\0\4\17\7\0\2\17\1\0"+ + "\2\16\6\0\2\16\2\17\2\0\12\17\22\0\2\17\1\0\10\16"+ + "\1\0\3\16\1\0\27\16\1\0\12\16\1\0\5\16\2\0\1\17"+ + "\1\16\7\17\1\0\3\17\1\0\4\17\7\0\2\17\7\0\1\16"+ + "\1\0\2\16\2\17\2\0\12\17\1\0\2\16\17\0\2\17\1\0"+ + "\10\16\1\0\3\16\1\0\51\16\2\0\1\16\7\17\1\0\3\17"+ + "\1\0\4\17\1\16\10\0\1\17\10\0\2\16\2\17\2\0\12\17"+ + "\12\0\6\16\2\0\2\17\1\0\22\16\3\0\30\16\1\0\11\16"+ + "\1\0\1\16\2\0\7\16\3\0\1\17\4\0\6\17\1\0\1\17"+ + "\1\0\10\17\22\0\2\17\15\0\60\16\1\17\2\16\7\17\4\0"+ + "\10\16\10\17\1\0\12\17\47\0\2\16\1\0\1\16\2\0\2\16"+ + "\1\0\1\16\2\0\1\16\6\0\4\16\1\0\7\16\1\0\3\16"+ + "\1\0\1\16\1\0\1\16\2\0\2\16\1\0\4\16\1\17\2\16"+ + "\6\17\1\0\2\17\1\16\2\0\5\16\1\0\1\16\1\0\6\17"+ + "\2\0\12\17\2\0\4\16\40\0\1\16\27\0\2\17\6\0\12\17"+ + "\13\0\1\17\1\0\1\17\1\0\1\17\4\0\2\17\10\16\1\0"+ + "\44\16\4\0\24\17\1\0\2\17\5\16\13\17\1\0\44\17\11\0"+ + "\1\17\71\0\53\16\24\17\1\16\12\17\6\0\6\16\4\17\4\16"+ + "\3\17\1\16\3\17\2\16\7\17\3\16\4\17\15\16\14\17\1\16"+ + "\17\17\2\0\46\16\1\0\1\16\5\0\1\16\2\0\53\16\1\0"+ + "\u014d\16\1\0\4\16\2\0\7\16\1\0\1\16\1\0\4\16\2\0"+ + "\51\16\1\0\4\16\2\0\41\16\1\0\4\16\2\0\7\16\1\0"+ + "\1\16\1\0\4\16\2\0\17\16\1\0\71\16\1\0\4\16\2\0"+ + "\103\16\2\0\3\17\40\0\20\16\20\0\125\16\14\0\u026c\16\2\0"+ + "\21\16\1\0\32\16\5\0\113\16\3\0\3\16\17\0\15\16\1\0"+ + "\4\16\3\17\13\0\22\16\3\17\13\0\22\16\2\17\14\0\15\16"+ + "\1\0\3\16\1\0\2\17\14\0\64\16\40\17\3\0\1\16\3\0"+ + "\2\16\1\17\2\0\12\17\41\0\3\17\2\0\12\17\6\0\130\16"+ + "\10\0\51\16\1\17\1\16\5\0\106\16\12\0\35\16\3\0\14\17"+ + "\4\0\14\17\12\0\12\17\36\16\2\0\5\16\13\0\54\16\4\0"+ + "\21\17\7\16\2\17\6\0\12\17\46\0\27\16\5\17\4\0\65\16"+ + "\12\17\1\0\35\17\2\0\13\17\6\0\12\17\15\0\1\16\130\0"+ + "\5\17\57\16\21\17\7\16\4\0\12\17\21\0\11\17\14\0\3\17"+ + "\36\16\15\17\2\16\12\17\54\16\16\17\14\0\44\16\24\17\10\0"+ + "\12\17\3\0\3\16\12\17\44\16\122\0\3\17\1\0\25\17\4\16"+ + "\1\17\4\16\3\17\2\16\11\0\300\16\47\17\25\0\4\17\u0116\16"+ + "\2\0\6\16\2\0\46\16\2\0\6\16\2\0\10\16\1\0\1\16"+ + "\1\0\1\16\1\0\1\16\1\0\37\16\2\0\65\16\1\0\7\16"+ + "\1\0\1\16\3\0\3\16\1\0\7\16\3\0\4\16\2\0\6\16"+ + "\4\0\15\16\5\0\3\16\1\0\7\16\16\0\5\17\32\0\5\17"+ + "\20\0\2\16\23\0\1\16\13\0\5\17\5\0\6\17\1\0\1\16"+ + "\15\0\1\16\20\0\15\16\3\0\33\16\25\0\15\17\4\0\1\17"+ + "\3\0\14\17\21\0\1\16\4\0\1\16\2\0\12\16\1\0\1\16"+ + "\3\0\5\16\6\0\1\16\1\0\1\16\1\0\1\16\1\0\4\16"+ + "\1\0\13\16\2\0\4\16\5\0\5\16\4\0\1\16\21\0\51\16"+ + "\u0a77\0\57\16\1\0\57\16\1\0\205\16\6\0\4\16\3\17\2\16"+ + "\14\0\46\16\1\0\1\16\5\0\1\16\2\0\70\16\7\0\1\16"+ + "\17\0\1\17\27\16\11\0\7\16\1\0\7\16\1\0\7\16\1\0"+ + "\7\16\1\0\7\16\1\0\7\16\1\0\7\16\1\0\7\16\1\0"+ + "\40\17\57\0\1\16\u01d5\0\3\16\31\0\11\16\6\17\1\0\5\16"+ + "\2\0\5\16\4\0\126\16\2\0\2\17\2\0\3\16\1\0\132\16"+ + "\1\0\4\16\5\0\51\16\3\0\136\16\21\0\33\16\65\0\20\16"+ + "\u0200\0\u19b6\16\112\0\u51cd\16\63\0\u048d\16\103\0\56\16\2\0\u010d\16"+ + "\3\0\20\16\12\17\2\16\24\0\57\16\1\17\4\0\12\17\1\0"+ + "\31\16\7\0\1\17\120\16\2\17\45\0\11\16\2\0\147\16\2\0"+ + "\4\16\1\0\4\16\14\0\13\16\115\0\12\16\1\17\3\16\1\17"+ + "\4\16\1\17\27\16\5\17\20\0\1\16\7\0\64\16\14\0\2\17"+ + "\62\16\21\17\13\0\12\17\6\0\22\17\6\16\3\0\1\16\4\0"+ + "\12\17\34\16\10\17\2\0\27\16\15\17\14\0\35\16\3\0\4\17"+ + "\57\16\16\17\16\0\1\16\12\17\46\0\51\16\16\17\11\0\3\16"+ + "\1\17\10\16\2\17\2\0\12\17\6\0\27\16\3\0\1\16\1\17"+ + "\4\0\60\16\1\17\1\16\3\17\2\16\2\17\5\16\2\17\1\16"+ + "\1\17\1\16\30\0\3\16\2\0\13\16\5\17\2\0\3\16\2\17"+ + "\12\0\6\16\2\0\6\16\2\0\6\16\11\0\7\16\1\0\7\16"+ + "\221\0\43\16\10\17\1\0\2\17\2\0\12\17\6\0\u2ba4\16\14\0"+ + "\27\16\4\0\61\16\u2104\0\u016e\16\2\0\152\16\46\0\7\16\14\0"+ + "\5\16\5\0\1\16\1\17\12\16\1\0\15\16\1\0\5\16\1\0"+ + "\1\16\1\0\2\16\1\0\2\16\1\0\154\16\41\0\u016b\16\22\0"+ + "\100\16\2\0\66\16\50\0\15\16\3\0\20\17\20\0\7\17\14\0"+ + "\2\16\30\0\3\16\31\0\1\16\6\0\5\16\1\0\207\16\2\0"+ + "\1\17\4\0\1\16\13\0\12\17\7\0\32\16\4\0\1\16\1\0"+ + "\32\16\13\0\131\16\3\0\6\16\2\0\6\16\2\0\6\16\2\0"+ + "\3\16\3\0\2\16\3\0\2\16\22\0\3\17\4\0"; + + /** + * Translates characters to character classes + */ + private static final char [] ZZ_CMAP = zzUnpackCMap(ZZ_CMAP_PACKED); + + /** + * Translates DFA states to action switch labels. + */ + private static final int [] ZZ_ACTION = zzUnpackAction(); + + private static final String ZZ_ACTION_PACKED_0 = + "\1\0\1\1\1\2\1\1\1\3\1\4\1\1\1\5"+ + "\1\6\1\7\1\10\1\11\1\12\1\13\1\14\3\6"+ + "\1\15\1\16\1\3\2\0\1\4\1\0\3\6\1\0"+ + "\1\5\3\6\1\16\1\0\1\17\1\6\1\20\1\0"+ + "\1\5\1\21"; + + private static int [] zzUnpackAction() { + int [] result = new int[41]; + int offset = 0; + offset = zzUnpackAction(ZZ_ACTION_PACKED_0, offset, result); + return result; + } + + private static int zzUnpackAction(String packed, int offset, int [] result) { + int i = 0; /* index in packed string */ + int j = offset; /* index in unpacked array */ + int l = packed.length(); + while (i < l) { + int count = packed.charAt(i++); + int value = packed.charAt(i++); + do result[j++] = value; while (--count > 0); + } + return j; + } + + + /** + * Translates a state to a row index in the transition table + */ + private static final int [] ZZ_ROWMAP = zzUnpackRowMap(); + + private static final String ZZ_ROWMAP_PACKED_0 = + "\0\0\0\37\0\76\0\135\0\174\0\233\0\272\0\331"+ + "\0\370\0\37\0\37\0\37\0\37\0\37\0\37\0\u0117"+ + "\0\u0136\0\u0155\0\u0174\0\u0193\0\37\0\u01b2\0\u01d1\0\37"+ + "\0\u01f0\0\u020f\0\u022e\0\u024d\0\u026c\0\u028b\0\u02aa\0\u02c9"+ + "\0\u02e8\0\37\0\u0307\0\370\0\u0326\0\370\0\u0345\0\u0345"+ + "\0\370"; + + private static int [] zzUnpackRowMap() { + int [] result = new int[41]; + int offset = 0; + offset = zzUnpackRowMap(ZZ_ROWMAP_PACKED_0, offset, result); + return result; + } + + private static int zzUnpackRowMap(String packed, int offset, int [] result) { + int i = 0; /* index in packed string */ + int j = offset; /* index in unpacked array */ + int l = packed.length(); + while (i < l) { + int high = packed.charAt(i++) << 16; + result[j++] = high | packed.charAt(i++); + } + return j; + } + + /** + * The transition table of the DFA + */ + private static final int [] ZZ_TRANS = zzUnpackTrans(); + + private static final String ZZ_TRANS_PACKED_0 = + "\1\2\3\3\1\4\1\2\1\5\1\2\1\6\1\7"+ + "\1\10\1\2\1\11\1\2\1\11\1\2\1\12\1\13"+ + "\1\14\1\15\1\16\1\17\1\20\3\11\1\21\3\11"+ + "\1\22\40\0\3\3\37\0\1\23\1\24\31\0\1\5"+ + "\2\0\3\5\1\25\1\26\27\5\1\6\2\0\4\6"+ + "\1\27\1\30\26\6\12\0\1\10\36\0\1\10\1\31"+ + "\35\0\1\11\1\0\1\11\1\0\2\11\6\0\11\11"+ + "\12\0\1\11\1\0\1\11\1\0\2\11\6\0\1\11"+ + "\1\32\7\11\12\0\1\11\1\0\1\11\1\0\2\11"+ + "\6\0\5\11\1\33\3\11\12\0\1\11\1\0\1\11"+ + "\1\0\2\11\6\0\2\11\1\34\6\11\2\23\1\0"+ + "\34\23\5\24\1\35\31\24\1\5\2\0\34\5\1\6"+ + "\2\0\34\6\12\0\1\36\36\0\1\11\1\0\1\11"+ + "\1\0\2\11\6\0\2\11\1\37\6\11\12\0\1\11"+ + "\1\0\1\11\1\0\2\11\6\0\6\11\1\40\2\11"+ + "\12\0\1\11\1\0\1\11\1\0\2\11\6\0\6\11"+ + "\1\41\2\11\4\24\1\42\1\35\31\24\12\0\1\36"+ + "\1\0\1\43\14\0\1\43\17\0\1\11\1\0\1\11"+ + "\1\0\2\11\6\0\3\11\1\44\5\11\12\0\1\11"+ + "\1\0\1\11\1\0\2\11\6\0\7\11\1\45\1\11"+ + "\12\0\1\11\1\0\1\11\1\0\2\11\6\0\6\11"+ + "\1\46\2\11\11\0\1\47\1\50\2\0\1\47\33\0"+ + "\1\11\1\0\1\11\1\0\2\11\6\0\3\11\1\51"+ + "\5\11\12\0\1\50\24\0"; + + private static int [] zzUnpackTrans() { + int [] result = new int[868]; + int offset = 0; + offset = zzUnpackTrans(ZZ_TRANS_PACKED_0, offset, result); + return result; + } + + private static int zzUnpackTrans(String packed, int offset, int [] result) { + int i = 0; /* index in packed string */ + int j = offset; /* index in unpacked array */ + int l = packed.length(); + while (i < l) { + int count = packed.charAt(i++); + int value = packed.charAt(i++); + value--; + do result[j++] = value; while (--count > 0); + } + return j; + } + + + /* error codes */ + private static final int ZZ_UNKNOWN_ERROR = 0; + private static final int ZZ_NO_MATCH = 1; + private static final int ZZ_PUSHBACK_2BIG = 2; + private static final char[] EMPTY_BUFFER = new char[0]; + private static final int YYEOF = -1; + private static java.io.Reader zzReader = null; // Fake + + /* error messages for the codes above */ + private static final String ZZ_ERROR_MSG[] = { + "Unkown internal scanner error", + "Error: could not match input", + "Error: pushback value was too large" + }; + + /** + * ZZ_ATTRIBUTE[aState] contains the attributes of state aState + */ + private static final int [] ZZ_ATTRIBUTE = zzUnpackAttribute(); + + private static final String ZZ_ATTRIBUTE_PACKED_0 = + "\1\0\1\11\7\1\6\11\5\1\1\11\2\0\1\11"+ + "\1\0\3\1\1\0\4\1\1\11\1\0\3\1\1\0"+ + "\2\1"; + + private static int [] zzUnpackAttribute() { + int [] result = new int[41]; + int offset = 0; + offset = zzUnpackAttribute(ZZ_ATTRIBUTE_PACKED_0, offset, result); + return result; + } + + private static int zzUnpackAttribute(String packed, int offset, int [] result) { + int i = 0; /* index in packed string */ + int j = offset; /* index in unpacked array */ + int l = packed.length(); + while (i < l) { + int count = packed.charAt(i++); + int value = packed.charAt(i++); + do result[j++] = value; while (--count > 0); + } + return j; + } + + /** the current state of the DFA */ + private int zzState; + + /** the current lexical state */ + private int zzLexicalState = YYINITIAL; + + /** this buffer contains the current text to be matched and is + the source of the yytext() string */ + private CharSequence zzBuffer = ""; + + /** this buffer may contains the current text array to be matched when it is cheap to acquire it */ + private char[] zzBufferArray; + + /** the textposition at the last accepting state */ + private int zzMarkedPos; + + /** the textposition at the last state to be included in yytext */ + private int zzPushbackPos; + + /** the current text position in the buffer */ + private int zzCurrentPos; + + /** startRead marks the beginning of the yytext() string in the buffer */ + private int zzStartRead; + + /** endRead marks the last character in the buffer, that has been read + from input */ + private int zzEndRead; + + /** + * zzAtBOL == true <=> the scanner is currently at the beginning of a line + */ + private boolean zzAtBOL = true; + + /** zzAtEOF == true <=> the scanner is at the EOF */ + private boolean zzAtEOF; + + /* user code: */ + public _JsonLexer() { + this((java.io.Reader)null); + } + + + public _JsonLexer(java.io.Reader in) { + this.zzReader = in; + } + + /** + * Creates a new scanner. + * There is also java.io.Reader version of this constructor. + * + * @param in the java.io.Inputstream to read input from. + */ + public _JsonLexer(java.io.InputStream in) { + this(new java.io.InputStreamReader(in)); + } + + /** + * Unpacks the compressed character translation table. + * + * @param packed the packed character translation table + * @return the unpacked character translation table + */ + private static char [] zzUnpackCMap(String packed) { + char [] map = new char[0x10000]; + int i = 0; /* index in packed string */ + int j = 0; /* index in unpacked array */ + while (i < 2216) { + int count = packed.charAt(i++); + char value = packed.charAt(i++); + do map[j++] = value; while (--count > 0); + } + return map; + } + + public final int getTokenStart(){ + return zzStartRead; + } + + public final int getTokenEnd(){ + return getTokenStart() + yylength(); + } + + public void reset(CharSequence buffer, int start, int end,int initialState){ + zzBuffer = buffer; + zzBufferArray = com.intellij.util.text.CharArrayUtil.fromSequenceWithoutCopying(buffer); + zzCurrentPos = zzMarkedPos = zzStartRead = start; + zzPushbackPos = 0; + zzAtEOF = false; + zzAtBOL = true; + zzEndRead = end; + yybegin(initialState); + } + + /** + * Refills the input buffer. + * + * @return false, iff there was new input. + * + * @exception java.io.IOException if any I/O-Error occurs + */ + private boolean zzRefill() throws java.io.IOException { + return true; + } + + + /** + * Returns the current lexical state. + */ + public final int yystate() { + return zzLexicalState; + } + + + /** + * Enters a new lexical state + * + * @param newState the new lexical state + */ + public final void yybegin(int newState) { + zzLexicalState = newState; + } + + + /** + * Returns the text matched by the current regular expression. + */ + public final CharSequence yytext() { + return zzBuffer.subSequence(zzStartRead, zzMarkedPos); + } + + + /** + * Returns the character at position pos from the + * matched text. + * + * It is equivalent to yytext().charAt(pos), but faster + * + * @param pos the position of the character to fetch. + * A value from 0 to yylength()-1. + * + * @return the character at position pos + */ + public final char yycharat(int pos) { + return zzBufferArray != null ? zzBufferArray[zzStartRead+pos]:zzBuffer.charAt(zzStartRead+pos); + } + + + /** + * Returns the length of the matched text region. + */ + public final int yylength() { + return zzMarkedPos-zzStartRead; + } + + + /** + * Reports an error that occured while scanning. + * + * In a wellformed scanner (no or only correct usage of + * yypushback(int) and a match-all fallback rule) this method + * will only be called with things that "Can't Possibly Happen". + * If this method is called, something is seriously wrong + * (e.g. a JFlex bug producing a faulty scanner etc.). + * + * Usual syntax/scanner level error handling should be done + * in error fallback rules. + * + * @param errorCode the code of the errormessage to display + */ + private void zzScanError(int errorCode) { + String message; + try { + message = ZZ_ERROR_MSG[errorCode]; + } + catch (ArrayIndexOutOfBoundsException e) { + message = ZZ_ERROR_MSG[ZZ_UNKNOWN_ERROR]; + } + + throw new Error(message); + } + + + /** + * Pushes the specified amount of characters back into the input stream. + * + * They will be read again by then next call of the scanning method + * + * @param number the number of characters to be read again. + * This number must not be greater than yylength()! + */ + public void yypushback(int number) { + if ( number > yylength() ) + zzScanError(ZZ_PUSHBACK_2BIG); + + zzMarkedPos -= number; + } + + + /** + * Resumes scanning until the next regular expression is matched, + * the end of input is encountered or an I/O-Error occurs. + * + * @return the next token + * @exception java.io.IOException if any I/O-Error occurs + */ + public IElementType advance() throws java.io.IOException { + int zzInput; + int zzAction; + + // cached fields: + int zzCurrentPosL; + int zzMarkedPosL; + int zzEndReadL = zzEndRead; + CharSequence zzBufferL = zzBuffer; + char[] zzBufferArrayL = zzBufferArray; + char [] zzCMapL = ZZ_CMAP; + + int [] zzTransL = ZZ_TRANS; + int [] zzRowMapL = ZZ_ROWMAP; + int [] zzAttrL = ZZ_ATTRIBUTE; + + while (true) { + zzMarkedPosL = zzMarkedPos; + + zzAction = -1; + + zzCurrentPosL = zzCurrentPos = zzStartRead = zzMarkedPosL; + + zzState = ZZ_LEXSTATE[zzLexicalState]; + + + zzForAction: { + while (true) { + + if (zzCurrentPosL < zzEndReadL) + zzInput = (zzBufferArrayL != null ? zzBufferArrayL[zzCurrentPosL++] : zzBufferL.charAt(zzCurrentPosL++)); + else if (zzAtEOF) { + zzInput = YYEOF; + break zzForAction; + } + else { + // store back cached positions + zzCurrentPos = zzCurrentPosL; + zzMarkedPos = zzMarkedPosL; + boolean eof = zzRefill(); + // get translated positions and possibly new buffer + zzCurrentPosL = zzCurrentPos; + zzMarkedPosL = zzMarkedPos; + zzBufferL = zzBuffer; + zzEndReadL = zzEndRead; + if (eof) { + zzInput = YYEOF; + break zzForAction; + } + else { + zzInput = (zzBufferArrayL != null ? zzBufferArrayL[zzCurrentPosL++] : zzBufferL.charAt(zzCurrentPosL++)); + } + } + int zzNext = zzTransL[ zzRowMapL[zzState] + zzCMapL[zzInput] ]; + if (zzNext == -1) break zzForAction; + zzState = zzNext; + + int zzAttributes = zzAttrL[zzState]; + if ( (zzAttributes & 1) == 1 ) { + zzAction = zzState; + zzMarkedPosL = zzCurrentPosL; + if ( (zzAttributes & 8) == 8 ) break zzForAction; + } + + } + } + + // store back cached position + zzMarkedPos = zzMarkedPosL; + + switch (zzAction < 0 ? zzAction : ZZ_ACTION[zzAction]) { + case 8: + { return R_CURLY; + } + case 18: break; + case 7: + { return L_CURLY; + } + case 19: break; + case 16: + { return NULL; + } + case 20: break; + case 6: + { return INDENTIFIER; + } + case 21: break; + case 9: + { return L_BRACKET; + } + case 22: break; + case 13: + { return LINE_COMMENT; + } + case 23: break; + case 11: + { return COMMA; + } + case 24: break; + case 2: + { return com.intellij.psi.TokenType.WHITE_SPACE; + } + case 25: break; + case 3: + { return DOUBLE_QUOTED_STRING; + } + case 26: break; + case 1: + { return com.intellij.psi.TokenType.BAD_CHARACTER; + } + case 27: break; + case 15: + { return TRUE; + } + case 28: break; + case 12: + { return COLON; + } + case 29: break; + case 4: + { return SINGLE_QUOTED_STRING; + } + case 30: break; + case 5: + { return NUMBER; + } + case 31: break; + case 17: + { return FALSE; + } + case 32: break; + case 14: + { return BLOCK_COMMENT; + } + case 33: break; + case 10: + { return R_BRACKET; + } + case 34: break; + default: + if (zzInput == YYEOF && zzStartRead == zzCurrentPos) { + zzAtEOF = true; + return null; + } + else { + zzScanError(ZZ_NO_MATCH); + } + } + } + } + + +} diff --git a/json/gen/com/intellij/json/psi/JsonArray.java b/json/gen/com/intellij/json/psi/JsonArray.java new file mode 100644 index 000000000000..868b1fbf8bff --- /dev/null +++ b/json/gen/com/intellij/json/psi/JsonArray.java @@ -0,0 +1,13 @@ +// This is a generated file. Not intended for manual editing. +package com.intellij.json.psi; + +import java.util.List; +import org.jetbrains.annotations.*; +import com.intellij.psi.PsiElement; + +public interface JsonArray extends JsonContainer { + + @NotNull + List getValueList(); + +} diff --git a/json/gen/com/intellij/json/psi/JsonBooleanLiteral.java b/json/gen/com/intellij/json/psi/JsonBooleanLiteral.java new file mode 100644 index 000000000000..684d79897ff7 --- /dev/null +++ b/json/gen/com/intellij/json/psi/JsonBooleanLiteral.java @@ -0,0 +1,10 @@ +// This is a generated file. Not intended for manual editing. +package com.intellij.json.psi; + +import java.util.List; +import org.jetbrains.annotations.*; +import com.intellij.psi.PsiElement; + +public interface JsonBooleanLiteral extends JsonLiteral { + +} diff --git a/json/gen/com/intellij/json/psi/JsonContainer.java b/json/gen/com/intellij/json/psi/JsonContainer.java new file mode 100644 index 000000000000..a69c0a8bf683 --- /dev/null +++ b/json/gen/com/intellij/json/psi/JsonContainer.java @@ -0,0 +1,10 @@ +// This is a generated file. Not intended for manual editing. +package com.intellij.json.psi; + +import java.util.List; +import org.jetbrains.annotations.*; +import com.intellij.psi.PsiElement; + +public interface JsonContainer extends JsonValue { + +} diff --git a/json/gen/com/intellij/json/psi/JsonElementVisitor.java b/json/gen/com/intellij/json/psi/JsonElementVisitor.java new file mode 100644 index 000000000000..c027a3bfcddf --- /dev/null +++ b/json/gen/com/intellij/json/psi/JsonElementVisitor.java @@ -0,0 +1,60 @@ +// This is a generated file. Not intended for manual editing. +package com.intellij.json.psi; + +import com.intellij.psi.PsiElement; +import org.jetbrains.annotations.*; +import com.intellij.psi.PsiElementVisitor; +import com.intellij.psi.PsiNamedElement; + +public class JsonElementVisitor extends PsiElementVisitor { + + public void visitArray(@NotNull JsonArray o) { + visitContainer(o); + } + + public void visitBooleanLiteral(@NotNull JsonBooleanLiteral o) { + visitLiteral(o); + } + + public void visitContainer(@NotNull JsonContainer o) { + visitValue(o); + } + + public void visitLiteral(@NotNull JsonLiteral o) { + visitValue(o); + } + + public void visitNullLiteral(@NotNull JsonNullLiteral o) { + visitLiteral(o); + } + + public void visitNumberLiteral(@NotNull JsonNumberLiteral o) { + visitLiteral(o); + } + + public void visitObject(@NotNull JsonObject o) { + visitContainer(o); + } + + public void visitProperty(@NotNull JsonProperty o) { + visitElement(o); + // visitPsiNamedElement(o); + } + + public void visitReferenceExpression(@NotNull JsonReferenceExpression o) { + visitValue(o); + } + + public void visitStringLiteral(@NotNull JsonStringLiteral o) { + visitLiteral(o); + } + + public void visitValue(@NotNull JsonValue o) { + visitElement(o); + } + + public void visitElement(@NotNull JsonElement o) { + visitElement((PsiElement)o); + } + +} diff --git a/json/gen/com/intellij/json/psi/JsonLiteral.java b/json/gen/com/intellij/json/psi/JsonLiteral.java new file mode 100644 index 000000000000..6e0eb7c7c32e --- /dev/null +++ b/json/gen/com/intellij/json/psi/JsonLiteral.java @@ -0,0 +1,12 @@ +// This is a generated file. Not intended for manual editing. +package com.intellij.json.psi; + +import java.util.List; +import org.jetbrains.annotations.*; +import com.intellij.psi.PsiElement; + +public interface JsonLiteral extends JsonValue { + + boolean isQuotedString(); + +} diff --git a/json/gen/com/intellij/json/psi/JsonNullLiteral.java b/json/gen/com/intellij/json/psi/JsonNullLiteral.java new file mode 100644 index 000000000000..a57448e643f6 --- /dev/null +++ b/json/gen/com/intellij/json/psi/JsonNullLiteral.java @@ -0,0 +1,10 @@ +// This is a generated file. Not intended for manual editing. +package com.intellij.json.psi; + +import java.util.List; +import org.jetbrains.annotations.*; +import com.intellij.psi.PsiElement; + +public interface JsonNullLiteral extends JsonLiteral { + +} diff --git a/json/gen/com/intellij/json/psi/JsonNumberLiteral.java b/json/gen/com/intellij/json/psi/JsonNumberLiteral.java new file mode 100644 index 000000000000..c189357829ef --- /dev/null +++ b/json/gen/com/intellij/json/psi/JsonNumberLiteral.java @@ -0,0 +1,13 @@ +// This is a generated file. Not intended for manual editing. +package com.intellij.json.psi; + +import java.util.List; +import org.jetbrains.annotations.*; +import com.intellij.psi.PsiElement; + +public interface JsonNumberLiteral extends JsonLiteral { + + @NotNull + PsiElement getNumber(); + +} diff --git a/json/gen/com/intellij/json/psi/JsonObject.java b/json/gen/com/intellij/json/psi/JsonObject.java new file mode 100644 index 000000000000..0f01d14651e1 --- /dev/null +++ b/json/gen/com/intellij/json/psi/JsonObject.java @@ -0,0 +1,16 @@ +// This is a generated file. Not intended for manual editing. +package com.intellij.json.psi; + +import java.util.List; +import org.jetbrains.annotations.*; +import com.intellij.psi.PsiElement; + +public interface JsonObject extends JsonContainer { + + @NotNull + List getPropertyList(); + + @Nullable + JsonProperty findProperty(String name); + +} diff --git a/json/gen/com/intellij/json/psi/JsonProperty.java b/json/gen/com/intellij/json/psi/JsonProperty.java new file mode 100644 index 000000000000..2f59e0c022ed --- /dev/null +++ b/json/gen/com/intellij/json/psi/JsonProperty.java @@ -0,0 +1,20 @@ +// This is a generated file. Not intended for manual editing. +package com.intellij.json.psi; + +import java.util.List; +import org.jetbrains.annotations.*; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiNamedElement; + +public interface JsonProperty extends JsonElement, PsiNamedElement { + + @NotNull + String getName(); + + @NotNull + JsonValue getNameElement(); + + @Nullable + JsonValue getValue(); + +} diff --git a/json/gen/com/intellij/json/psi/JsonReferenceExpression.java b/json/gen/com/intellij/json/psi/JsonReferenceExpression.java new file mode 100644 index 000000000000..7b0fbb6103af --- /dev/null +++ b/json/gen/com/intellij/json/psi/JsonReferenceExpression.java @@ -0,0 +1,13 @@ +// This is a generated file. Not intended for manual editing. +package com.intellij.json.psi; + +import java.util.List; +import org.jetbrains.annotations.*; +import com.intellij.psi.PsiElement; + +public interface JsonReferenceExpression extends JsonValue { + + @NotNull + PsiElement getIndentifier(); + +} diff --git a/json/gen/com/intellij/json/psi/JsonStringLiteral.java b/json/gen/com/intellij/json/psi/JsonStringLiteral.java new file mode 100644 index 000000000000..00fb1fde661c --- /dev/null +++ b/json/gen/com/intellij/json/psi/JsonStringLiteral.java @@ -0,0 +1,16 @@ +// This is a generated file. Not intended for manual editing. +package com.intellij.json.psi; + +import java.util.List; +import org.jetbrains.annotations.*; +import com.intellij.psi.PsiElement; + +public interface JsonStringLiteral extends JsonLiteral { + + @Nullable + PsiElement getDoubleQuotedString(); + + @Nullable + PsiElement getSingleQuotedString(); + +} diff --git a/json/gen/com/intellij/json/psi/JsonValue.java b/json/gen/com/intellij/json/psi/JsonValue.java new file mode 100644 index 000000000000..66c8d38ba844 --- /dev/null +++ b/json/gen/com/intellij/json/psi/JsonValue.java @@ -0,0 +1,10 @@ +// This is a generated file. Not intended for manual editing. +package com.intellij.json.psi; + +import java.util.List; +import org.jetbrains.annotations.*; +import com.intellij.psi.PsiElement; + +public interface JsonValue extends JsonElement { + +} diff --git a/json/gen/com/intellij/json/psi/impl/JsonArrayImpl.java b/json/gen/com/intellij/json/psi/impl/JsonArrayImpl.java new file mode 100644 index 000000000000..9dadae1c0e4d --- /dev/null +++ b/json/gen/com/intellij/json/psi/impl/JsonArrayImpl.java @@ -0,0 +1,30 @@ +// This is a generated file. Not intended for manual editing. +package com.intellij.json.psi.impl; + +import java.util.List; +import org.jetbrains.annotations.*; +import com.intellij.lang.ASTNode; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiElementVisitor; +import com.intellij.psi.util.PsiTreeUtil; +import static com.intellij.json.JsonElementTypes.*; +import com.intellij.json.psi.*; + +public class JsonArrayImpl extends JsonContainerImpl implements JsonArray { + + public JsonArrayImpl(ASTNode node) { + super(node); + } + + public void accept(@NotNull PsiElementVisitor visitor) { + if (visitor instanceof JsonElementVisitor) ((JsonElementVisitor)visitor).visitArray(this); + else super.accept(visitor); + } + + @Override + @NotNull + public List getValueList() { + return PsiTreeUtil.getChildrenOfTypeAsList(this, JsonValue.class); + } + +} diff --git a/json/gen/com/intellij/json/psi/impl/JsonBooleanLiteralImpl.java b/json/gen/com/intellij/json/psi/impl/JsonBooleanLiteralImpl.java new file mode 100644 index 000000000000..3e8d340aab82 --- /dev/null +++ b/json/gen/com/intellij/json/psi/impl/JsonBooleanLiteralImpl.java @@ -0,0 +1,24 @@ +// This is a generated file. Not intended for manual editing. +package com.intellij.json.psi.impl; + +import java.util.List; +import org.jetbrains.annotations.*; +import com.intellij.lang.ASTNode; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiElementVisitor; +import com.intellij.psi.util.PsiTreeUtil; +import static com.intellij.json.JsonElementTypes.*; +import com.intellij.json.psi.*; + +public class JsonBooleanLiteralImpl extends JsonLiteralImpl implements JsonBooleanLiteral { + + public JsonBooleanLiteralImpl(ASTNode node) { + super(node); + } + + public void accept(@NotNull PsiElementVisitor visitor) { + if (visitor instanceof JsonElementVisitor) ((JsonElementVisitor)visitor).visitBooleanLiteral(this); + else super.accept(visitor); + } + +} diff --git a/json/gen/com/intellij/json/psi/impl/JsonContainerImpl.java b/json/gen/com/intellij/json/psi/impl/JsonContainerImpl.java new file mode 100644 index 000000000000..765b2c06a107 --- /dev/null +++ b/json/gen/com/intellij/json/psi/impl/JsonContainerImpl.java @@ -0,0 +1,24 @@ +// This is a generated file. Not intended for manual editing. +package com.intellij.json.psi.impl; + +import java.util.List; +import org.jetbrains.annotations.*; +import com.intellij.lang.ASTNode; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiElementVisitor; +import com.intellij.psi.util.PsiTreeUtil; +import static com.intellij.json.JsonElementTypes.*; +import com.intellij.json.psi.*; + +public class JsonContainerImpl extends JsonValueImpl implements JsonContainer { + + public JsonContainerImpl(ASTNode node) { + super(node); + } + + public void accept(@NotNull PsiElementVisitor visitor) { + if (visitor instanceof JsonElementVisitor) ((JsonElementVisitor)visitor).visitContainer(this); + else super.accept(visitor); + } + +} diff --git a/json/gen/com/intellij/json/psi/impl/JsonLiteralImpl.java b/json/gen/com/intellij/json/psi/impl/JsonLiteralImpl.java new file mode 100644 index 000000000000..da4d688e4873 --- /dev/null +++ b/json/gen/com/intellij/json/psi/impl/JsonLiteralImpl.java @@ -0,0 +1,28 @@ +// This is a generated file. Not intended for manual editing. +package com.intellij.json.psi.impl; + +import java.util.List; +import org.jetbrains.annotations.*; +import com.intellij.lang.ASTNode; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiElementVisitor; +import com.intellij.psi.util.PsiTreeUtil; +import static com.intellij.json.JsonElementTypes.*; +import com.intellij.json.psi.*; + +public class JsonLiteralImpl extends JsonLiteralMixin implements JsonLiteral { + + public JsonLiteralImpl(ASTNode node) { + super(node); + } + + public void accept(@NotNull PsiElementVisitor visitor) { + if (visitor instanceof JsonElementVisitor) ((JsonElementVisitor)visitor).visitLiteral(this); + else super.accept(visitor); + } + + public boolean isQuotedString() { + return JsonPsiImplUtils.isQuotedString(this); + } + +} diff --git a/json/gen/com/intellij/json/psi/impl/JsonNullLiteralImpl.java b/json/gen/com/intellij/json/psi/impl/JsonNullLiteralImpl.java new file mode 100644 index 000000000000..c74a4616e5b8 --- /dev/null +++ b/json/gen/com/intellij/json/psi/impl/JsonNullLiteralImpl.java @@ -0,0 +1,24 @@ +// This is a generated file. Not intended for manual editing. +package com.intellij.json.psi.impl; + +import java.util.List; +import org.jetbrains.annotations.*; +import com.intellij.lang.ASTNode; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiElementVisitor; +import com.intellij.psi.util.PsiTreeUtil; +import static com.intellij.json.JsonElementTypes.*; +import com.intellij.json.psi.*; + +public class JsonNullLiteralImpl extends JsonLiteralImpl implements JsonNullLiteral { + + public JsonNullLiteralImpl(ASTNode node) { + super(node); + } + + public void accept(@NotNull PsiElementVisitor visitor) { + if (visitor instanceof JsonElementVisitor) ((JsonElementVisitor)visitor).visitNullLiteral(this); + else super.accept(visitor); + } + +} diff --git a/json/gen/com/intellij/json/psi/impl/JsonNumberLiteralImpl.java b/json/gen/com/intellij/json/psi/impl/JsonNumberLiteralImpl.java new file mode 100644 index 000000000000..84fe96bebdaf --- /dev/null +++ b/json/gen/com/intellij/json/psi/impl/JsonNumberLiteralImpl.java @@ -0,0 +1,30 @@ +// This is a generated file. Not intended for manual editing. +package com.intellij.json.psi.impl; + +import java.util.List; +import org.jetbrains.annotations.*; +import com.intellij.lang.ASTNode; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiElementVisitor; +import com.intellij.psi.util.PsiTreeUtil; +import static com.intellij.json.JsonElementTypes.*; +import com.intellij.json.psi.*; + +public class JsonNumberLiteralImpl extends JsonLiteralImpl implements JsonNumberLiteral { + + public JsonNumberLiteralImpl(ASTNode node) { + super(node); + } + + public void accept(@NotNull PsiElementVisitor visitor) { + if (visitor instanceof JsonElementVisitor) ((JsonElementVisitor)visitor).visitNumberLiteral(this); + else super.accept(visitor); + } + + @Override + @NotNull + public PsiElement getNumber() { + return findNotNullChildByType(NUMBER); + } + +} diff --git a/json/gen/com/intellij/json/psi/impl/JsonObjectImpl.java b/json/gen/com/intellij/json/psi/impl/JsonObjectImpl.java new file mode 100644 index 000000000000..1e239de1cc16 --- /dev/null +++ b/json/gen/com/intellij/json/psi/impl/JsonObjectImpl.java @@ -0,0 +1,35 @@ +// This is a generated file. Not intended for manual editing. +package com.intellij.json.psi.impl; + +import java.util.List; +import org.jetbrains.annotations.*; +import com.intellij.lang.ASTNode; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiElementVisitor; +import com.intellij.psi.util.PsiTreeUtil; +import static com.intellij.json.JsonElementTypes.*; +import com.intellij.json.psi.*; + +public class JsonObjectImpl extends JsonContainerImpl implements JsonObject { + + public JsonObjectImpl(ASTNode node) { + super(node); + } + + public void accept(@NotNull PsiElementVisitor visitor) { + if (visitor instanceof JsonElementVisitor) ((JsonElementVisitor)visitor).visitObject(this); + else super.accept(visitor); + } + + @Override + @NotNull + public List getPropertyList() { + return PsiTreeUtil.getChildrenOfTypeAsList(this, JsonProperty.class); + } + + @Nullable + public JsonProperty findProperty(String name) { + return JsonPsiImplUtils.findProperty(this, name); + } + +} diff --git a/json/gen/com/intellij/json/psi/impl/JsonPropertyImpl.java b/json/gen/com/intellij/json/psi/impl/JsonPropertyImpl.java new file mode 100644 index 000000000000..b371f66af8db --- /dev/null +++ b/json/gen/com/intellij/json/psi/impl/JsonPropertyImpl.java @@ -0,0 +1,39 @@ +// This is a generated file. Not intended for manual editing. +package com.intellij.json.psi.impl; + +import java.util.List; +import org.jetbrains.annotations.*; +import com.intellij.lang.ASTNode; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiElementVisitor; +import com.intellij.psi.util.PsiTreeUtil; +import static com.intellij.json.JsonElementTypes.*; +import com.intellij.json.psi.*; + +public class JsonPropertyImpl extends JsonPropertyMixin implements JsonProperty { + + public JsonPropertyImpl(ASTNode node) { + super(node); + } + + public void accept(@NotNull PsiElementVisitor visitor) { + if (visitor instanceof JsonElementVisitor) ((JsonElementVisitor)visitor).visitProperty(this); + else super.accept(visitor); + } + + @NotNull + public String getName() { + return JsonPsiImplUtils.getName(this); + } + + @NotNull + public JsonValue getNameElement() { + return JsonPsiImplUtils.getNameElement(this); + } + + @Nullable + public JsonValue getValue() { + return JsonPsiImplUtils.getValue(this); + } + +} diff --git a/json/gen/com/intellij/json/psi/impl/JsonReferenceExpressionImpl.java b/json/gen/com/intellij/json/psi/impl/JsonReferenceExpressionImpl.java new file mode 100644 index 000000000000..c4d2ec080b8a --- /dev/null +++ b/json/gen/com/intellij/json/psi/impl/JsonReferenceExpressionImpl.java @@ -0,0 +1,30 @@ +// This is a generated file. Not intended for manual editing. +package com.intellij.json.psi.impl; + +import java.util.List; +import org.jetbrains.annotations.*; +import com.intellij.lang.ASTNode; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiElementVisitor; +import com.intellij.psi.util.PsiTreeUtil; +import static com.intellij.json.JsonElementTypes.*; +import com.intellij.json.psi.*; + +public class JsonReferenceExpressionImpl extends JsonValueImpl implements JsonReferenceExpression { + + public JsonReferenceExpressionImpl(ASTNode node) { + super(node); + } + + public void accept(@NotNull PsiElementVisitor visitor) { + if (visitor instanceof JsonElementVisitor) ((JsonElementVisitor)visitor).visitReferenceExpression(this); + else super.accept(visitor); + } + + @Override + @NotNull + public PsiElement getIndentifier() { + return findNotNullChildByType(INDENTIFIER); + } + +} diff --git a/json/gen/com/intellij/json/psi/impl/JsonStringLiteralImpl.java b/json/gen/com/intellij/json/psi/impl/JsonStringLiteralImpl.java new file mode 100644 index 000000000000..b704f4eb0784 --- /dev/null +++ b/json/gen/com/intellij/json/psi/impl/JsonStringLiteralImpl.java @@ -0,0 +1,36 @@ +// This is a generated file. Not intended for manual editing. +package com.intellij.json.psi.impl; + +import java.util.List; +import org.jetbrains.annotations.*; +import com.intellij.lang.ASTNode; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiElementVisitor; +import com.intellij.psi.util.PsiTreeUtil; +import static com.intellij.json.JsonElementTypes.*; +import com.intellij.json.psi.*; + +public class JsonStringLiteralImpl extends JsonLiteralImpl implements JsonStringLiteral { + + public JsonStringLiteralImpl(ASTNode node) { + super(node); + } + + public void accept(@NotNull PsiElementVisitor visitor) { + if (visitor instanceof JsonElementVisitor) ((JsonElementVisitor)visitor).visitStringLiteral(this); + else super.accept(visitor); + } + + @Override + @Nullable + public PsiElement getDoubleQuotedString() { + return findChildByType(DOUBLE_QUOTED_STRING); + } + + @Override + @Nullable + public PsiElement getSingleQuotedString() { + return findChildByType(SINGLE_QUOTED_STRING); + } + +} diff --git a/json/gen/com/intellij/json/psi/impl/JsonValueImpl.java b/json/gen/com/intellij/json/psi/impl/JsonValueImpl.java new file mode 100644 index 000000000000..c8fe9a4682fe --- /dev/null +++ b/json/gen/com/intellij/json/psi/impl/JsonValueImpl.java @@ -0,0 +1,24 @@ +// This is a generated file. Not intended for manual editing. +package com.intellij.json.psi.impl; + +import java.util.List; +import org.jetbrains.annotations.*; +import com.intellij.lang.ASTNode; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiElementVisitor; +import com.intellij.psi.util.PsiTreeUtil; +import static com.intellij.json.JsonElementTypes.*; +import com.intellij.json.psi.*; + +public class JsonValueImpl extends JsonElementImpl implements JsonValue { + + public JsonValueImpl(ASTNode node) { + super(node); + } + + public void accept(@NotNull PsiElementVisitor visitor) { + if (visitor instanceof JsonElementVisitor) ((JsonElementVisitor)visitor).visitValue(this); + else super.accept(visitor); + } + +} diff --git a/json/json.iml b/json/json.iml new file mode 100644 index 000000000000..aaaf40c50ffd --- /dev/null +++ b/json/json.iml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/json/resources/inspectionDescriptions/JsonStandardCompliance.html b/json/resources/inspectionDescriptions/JsonStandardCompliance.html new file mode 100644 index 000000000000..9627ff1e0faf --- /dev/null +++ b/json/resources/inspectionDescriptions/JsonStandardCompliance.html @@ -0,0 +1,6 @@ + + +This inspection checks that *.json files comply with JSON specification (RFC-4627).
+
+ + \ No newline at end of file diff --git a/json/src/com/intellij/json/JsonBraceMatcher.java b/json/src/com/intellij/json/JsonBraceMatcher.java new file mode 100644 index 000000000000..2504f3040ba8 --- /dev/null +++ b/json/src/com/intellij/json/JsonBraceMatcher.java @@ -0,0 +1,33 @@ +package com.intellij.json; + +import com.intellij.lang.BracePair; +import com.intellij.lang.PairedBraceMatcher; +import com.intellij.psi.PsiFile; +import com.intellij.psi.tree.IElementType; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author Mikhail Golubev + */ +public class JsonBraceMatcher implements PairedBraceMatcher { + private static BracePair[] PAIRS = { + new BracePair(JsonElementTypes.L_BRACKET, JsonElementTypes.R_BRACKET, true), + new BracePair(JsonElementTypes.L_CURLY, JsonElementTypes.R_CURLY, true) + }; + + @Override + public BracePair[] getPairs() { + return PAIRS; + } + + @Override + public boolean isPairedBracesAllowedBeforeType(@NotNull IElementType lbraceType, @Nullable IElementType contextType) { + return true; + } + + @Override + public int getCodeConstructStart(PsiFile file, int openingBraceOffset) { + return openingBraceOffset; + } +} diff --git a/json/src/com/intellij/json/JsonBundle.java b/json/src/com/intellij/json/JsonBundle.java new file mode 100644 index 000000000000..231acde78d14 --- /dev/null +++ b/json/src/com/intellij/json/JsonBundle.java @@ -0,0 +1,36 @@ +package com.intellij.json; + +import com.intellij.CommonBundle; +import com.intellij.reference.SoftReference; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.PropertyKey; + +import java.lang.ref.Reference; +import java.util.ResourceBundle; + +/** + * @author Mikhail Golubev + */ +public class JsonBundle { + + private static Reference ourBundle; + @NonNls private static final String BUNDLE = "com.intellij.json.JsonBundle"; + + private JsonBundle() { + // empty + } + + public static String message(@NotNull @PropertyKey(resourceBundle = BUNDLE) String key, @NotNull Object... params) { + return CommonBundle.message(getBundle(), key, params); + } + + private static ResourceBundle getBundle() { + ResourceBundle bundle = SoftReference.dereference(ourBundle); + if (bundle == null) { + bundle = ResourceBundle.getBundle(BUNDLE); + ourBundle = new SoftReference(bundle); + } + return bundle; + } +} diff --git a/json/src/com/intellij/json/JsonBundle.properties b/json/src/com/intellij/json/JsonBundle.properties new file mode 100644 index 000000000000..426a506ff5a4 --- /dev/null +++ b/json/src/com/intellij/json/JsonBundle.properties @@ -0,0 +1,18 @@ +msg.missing.closing.quote=Missing closing quote +msg.illegal.escape.sequence=Illegal escape sequence +msg.illegal.unicode.escape.sequence=Illegal unicode escape sequence + +msg.compliance.problem.comments=JSON standard does not allow comments. Use JSMin or similar tool to remove comments before parsing. +msg.compliance.problem.single.quoted.strings=JSON standard does not allow single quoted strings +msg.compliance.problem.illegal.top.level.value=JSON standard allows only object or array as top-level value +msg.compliance.problem.identifier=JSON standard does not allow identifiers +msg.compliance.problem.illegal.property.key=JSON standard allows only double quoted string as property key +msg.compliance.problem.trailing.comma=JSON standard does not allow trailing comma + + +name.json.inspection.group=JSON +name.standard.compliance.inspection=Compliance with JSON standard +name.add.double.quotes.quickfix=Wrap with double quotes +name.surround.with.object.literal=object literal + +option.warn.about.comments.name=Warn about comments in JSON diff --git a/json/src/com/intellij/json/JsonElementType.java b/json/src/com/intellij/json/JsonElementType.java new file mode 100644 index 000000000000..54e05dfc65c6 --- /dev/null +++ b/json/src/com/intellij/json/JsonElementType.java @@ -0,0 +1,11 @@ +package com.intellij.json; + +import com.intellij.psi.tree.IElementType; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; + +public class JsonElementType extends IElementType { + public JsonElementType(@NotNull @NonNls String debugName) { + super(debugName, JsonLanguage.INSTANCE); + } +} diff --git a/json/src/com/intellij/json/JsonFileType.java b/json/src/com/intellij/json/JsonFileType.java new file mode 100644 index 000000000000..3a2d0a448ad7 --- /dev/null +++ b/json/src/com/intellij/json/JsonFileType.java @@ -0,0 +1,45 @@ +package com.intellij.json; + +import com.intellij.icons.AllIcons; +import com.intellij.openapi.fileTypes.LanguageFileType; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.Icon; + +/** + * @author Mikhail Golubev + */ +public class JsonFileType extends LanguageFileType{ + public static final JsonFileType INSTANCE = new JsonFileType(); + public static final String DEFAULT_EXTENSION = "json"; + + public JsonFileType() { + super(JsonLanguage.INSTANCE); + } + + @NotNull + @Override + public String getName() { + return "JSON"; + } + + @NotNull + @Override + public String getDescription() { + return "JSON language"; + } + + @NotNull + @Override + public String getDefaultExtension() { + return DEFAULT_EXTENSION; + } + + @Nullable + @Override + public Icon getIcon() { + // TODO: add JSON icon instead + return AllIcons.FileTypes.Json; + } +} diff --git a/json/src/com/intellij/json/JsonFileTypeFactory.java b/json/src/com/intellij/json/JsonFileTypeFactory.java new file mode 100644 index 000000000000..2cfaa2f1466b --- /dev/null +++ b/json/src/com/intellij/json/JsonFileTypeFactory.java @@ -0,0 +1,15 @@ +package com.intellij.json; + +import com.intellij.openapi.fileTypes.FileTypeConsumer; +import com.intellij.openapi.fileTypes.FileTypeFactory; +import org.jetbrains.annotations.NotNull; + +/** + * @author Mikhail Golubev + */ +public class JsonFileTypeFactory extends FileTypeFactory { + @Override + public void createFileTypes(@NotNull FileTypeConsumer consumer) { + consumer.consume(JsonFileType.INSTANCE, JsonFileType.DEFAULT_EXTENSION); + } +} diff --git a/json/src/com/intellij/json/JsonLanguage.java b/json/src/com/intellij/json/JsonLanguage.java new file mode 100644 index 000000000000..09e2807c1cfd --- /dev/null +++ b/json/src/com/intellij/json/JsonLanguage.java @@ -0,0 +1,16 @@ +package com.intellij.json; + +import com.intellij.lang.Language; + +public class JsonLanguage extends Language { + public static final JsonLanguage INSTANCE = new JsonLanguage(); + + private JsonLanguage() { + super("JSON", "application/json"); + } + + @Override + public boolean isCaseSensitive() { + return true; + } +} diff --git a/json/src/com/intellij/json/JsonNamesValidator.java b/json/src/com/intellij/json/JsonNamesValidator.java new file mode 100644 index 000000000000..98a5bb34f2d4 --- /dev/null +++ b/json/src/com/intellij/json/JsonNamesValidator.java @@ -0,0 +1,38 @@ +/* + * @author max + */ +package com.intellij.json; + +import com.intellij.lang.refactoring.NamesValidator; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.tree.IElementType; +import org.jetbrains.annotations.NotNull; + +public class JsonNamesValidator implements NamesValidator { + + private final JsonLexer myLexer = new JsonLexer(); + + @Override + public synchronized boolean isKeyword(@NotNull String name, Project project) { + myLexer.start(name); + return JsonParserDefinition.JSON_KEYWORDS.contains( myLexer.getTokenType() ) && myLexer.getTokenEnd() == name.length(); + } + @Override + public synchronized boolean isIdentifier(@NotNull String name, final Project project) { + if (!StringUtil.startsWithChar(name,'\'') && !StringUtil.startsWithChar(name,'\"')) { + name = "\"" + name; + } + + if (!StringUtil.endsWithChar(name,'"') && !StringUtil.endsWithChar(name,'\"')) { + name += "\""; + } + + myLexer.start(name); + IElementType type = myLexer.getTokenType(); + + return myLexer.getTokenEnd() == name.length() && (type == JsonElementTypes.DOUBLE_QUOTED_STRING || + type == JsonElementTypes.SINGLE_QUOTED_STRING); + } + +} diff --git a/json/src/com/intellij/json/JsonParserDefinition.java b/json/src/com/intellij/json/JsonParserDefinition.java new file mode 100644 index 000000000000..2eca5d3d2119 --- /dev/null +++ b/json/src/com/intellij/json/JsonParserDefinition.java @@ -0,0 +1,84 @@ +package com.intellij.json; + +import com.intellij.json.psi.impl.JsonFileImpl; +import com.intellij.lang.ASTNode; +import com.intellij.lang.ParserDefinition; +import com.intellij.lang.PsiParser; +import com.intellij.lexer.Lexer; +import com.intellij.openapi.project.Project; +import com.intellij.psi.FileViewProvider; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.TokenType; +import com.intellij.psi.tree.IFileElementType; +import com.intellij.psi.tree.TokenSet; +import org.jetbrains.annotations.NotNull; + +import static com.intellij.json.JsonElementTypes.*; + +public class JsonParserDefinition implements ParserDefinition { + public static final TokenSet WHITE_SPACES = TokenSet.create(TokenType.WHITE_SPACE); + public static final TokenSet STRING_LITERALS = TokenSet.create(SINGLE_QUOTED_STRING, DOUBLE_QUOTED_STRING); + + public static final IFileElementType FILE = new IFileElementType(JsonLanguage.INSTANCE); + + public static final TokenSet JSON_BRACES = TokenSet.create(L_CURLY, R_CURLY); + public static final TokenSet JSON_BRACKETS = TokenSet.create(L_BRACKET, R_BRACKET); + public static final TokenSet JSON_CONTAINERS = TokenSet.create(OBJECT, ARRAY); + public static final TokenSet JSON_BOOLEANS = TokenSet.create(TRUE, FALSE); + public static final TokenSet JSON_KEYWORDS = TokenSet.create(TRUE, FALSE, NULL); + public static final TokenSet JSON_LITERALS = TokenSet.create(STRING_LITERAL, NUMBER_LITERAL, NULL_LITERAL, TRUE, FALSE); + public static final TokenSet JSON_VALUES = TokenSet.orSet(JSON_CONTAINERS, JSON_LITERALS); + public static final TokenSet JSON_COMMENTARIES = TokenSet.create(BLOCK_COMMENT, LINE_COMMENT); + + + @NotNull + @Override + public Lexer createLexer(Project project) { + return new JsonLexer(); + } + + @Override + public PsiParser createParser(Project project) { + return new JsonParser(); + } + + @Override + public IFileElementType getFileNodeType() { + return FILE; + } + + @NotNull + @Override + public TokenSet getWhitespaceTokens() { + return WHITE_SPACES; + } + + @NotNull + @Override + public TokenSet getCommentTokens() { + return JSON_COMMENTARIES; + } + + @NotNull + @Override + public TokenSet getStringLiteralElements() { + return STRING_LITERALS; + } + + @NotNull + @Override + public PsiElement createElement(ASTNode astNode) { + return Factory.createElement(astNode); + } + + @Override + public PsiFile createFile(FileViewProvider fileViewProvider) { + return new JsonFileImpl(fileViewProvider); + } + + @Override + public SpaceRequirements spaceExistanceTypeBetweenTokens(ASTNode astNode, ASTNode astNode2) { + return SpaceRequirements.MAY; + } +} diff --git a/json/src/com/intellij/json/JsonQuoteHandler.java b/json/src/com/intellij/json/JsonQuoteHandler.java new file mode 100644 index 000000000000..4ddac1ac07af --- /dev/null +++ b/json/src/com/intellij/json/JsonQuoteHandler.java @@ -0,0 +1,12 @@ +package com.intellij.json; + +import com.intellij.codeInsight.editorActions.SimpleTokenSetQuoteHandler; + +/** + * @author Mikhail Golubev + */ +public class JsonQuoteHandler extends SimpleTokenSetQuoteHandler { + public JsonQuoteHandler() { + super(JsonParserDefinition.STRING_LITERALS); + } +} diff --git a/json/src/com/intellij/json/JsonSyntaxHighlighterFactory.java b/json/src/com/intellij/json/JsonSyntaxHighlighterFactory.java new file mode 100644 index 000000000000..3fa4584f25d3 --- /dev/null +++ b/json/src/com/intellij/json/JsonSyntaxHighlighterFactory.java @@ -0,0 +1,73 @@ +package com.intellij.json; + +import com.intellij.lexer.LayeredLexer; +import com.intellij.lexer.Lexer; +import com.intellij.lexer.StringLiteralLexer; +import com.intellij.openapi.editor.DefaultLanguageHighlighterColors; +import com.intellij.openapi.editor.HighlighterColors; +import com.intellij.openapi.editor.colors.TextAttributesKey; +import com.intellij.openapi.fileTypes.SyntaxHighlighter; +import com.intellij.openapi.fileTypes.SyntaxHighlighterBase; +import com.intellij.openapi.fileTypes.SyntaxHighlighterFactory; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.TokenType; +import com.intellij.psi.tree.IElementType; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.HashMap; +import java.util.Map; + +import static com.intellij.json.JsonElementTypes.*; +import static com.intellij.psi.StringEscapesTokenTypes.*; + +public class JsonSyntaxHighlighterFactory extends SyntaxHighlighterFactory { + @NotNull + @Override + public SyntaxHighlighter getSyntaxHighlighter(@Nullable Project project, @Nullable VirtualFile virtualFile) { + return new MyHighlighter(); + } + + private static class MyHighlighter extends SyntaxHighlighterBase { + private static final Map ourAttributes = new HashMap(); + + static { + fillMap(ourAttributes, DefaultLanguageHighlighterColors.BRACES, L_CURLY, R_CURLY); + fillMap(ourAttributes, DefaultLanguageHighlighterColors.BRACKETS, L_BRACKET, R_BRACKET); + fillMap(ourAttributes, DefaultLanguageHighlighterColors.COMMA, COMMA); + fillMap(ourAttributes, DefaultLanguageHighlighterColors.SEMICOLON, COLON); + fillMap(ourAttributes, DefaultLanguageHighlighterColors.STRING, DOUBLE_QUOTED_STRING); + fillMap(ourAttributes, DefaultLanguageHighlighterColors.STRING, SINGLE_QUOTED_STRING); + fillMap(ourAttributes, DefaultLanguageHighlighterColors.NUMBER, NUMBER); + fillMap(ourAttributes, DefaultLanguageHighlighterColors.KEYWORD, TRUE, FALSE, NULL); + fillMap(ourAttributes, DefaultLanguageHighlighterColors.LINE_COMMENT, LINE_COMMENT); + fillMap(ourAttributes, DefaultLanguageHighlighterColors.BLOCK_COMMENT, BLOCK_COMMENT); + // TODO may be it's worth to add more sensible highlighting for identifiers + fillMap(ourAttributes, HighlighterColors.TEXT, INDENTIFIER); + fillMap(ourAttributes, HighlighterColors.BAD_CHARACTER, TokenType.BAD_CHARACTER); + + // StringLexer's tokens + fillMap(ourAttributes, DefaultLanguageHighlighterColors.VALID_STRING_ESCAPE, VALID_STRING_ESCAPE_TOKEN); + fillMap(ourAttributes, DefaultLanguageHighlighterColors.INVALID_STRING_ESCAPE, INVALID_CHARACTER_ESCAPE_TOKEN); + fillMap(ourAttributes, DefaultLanguageHighlighterColors.INVALID_STRING_ESCAPE, INVALID_UNICODE_ESCAPE_TOKEN); + } + + @NotNull + @Override + public Lexer getHighlightingLexer() { + LayeredLexer layeredLexer = new LayeredLexer(new JsonLexer()); + layeredLexer.registerSelfStoppingLayer(new StringLiteralLexer('\"', DOUBLE_QUOTED_STRING, false, "/", false, false), + new IElementType[]{DOUBLE_QUOTED_STRING}, IElementType.EMPTY_ARRAY); + layeredLexer.registerSelfStoppingLayer(new StringLiteralLexer('\'', SINGLE_QUOTED_STRING, false, "/", false, false), + new IElementType[]{SINGLE_QUOTED_STRING}, IElementType.EMPTY_ARRAY); + return layeredLexer; + } + + @NotNull + @Override + public TextAttributesKey[] getTokenHighlights(IElementType type) { + return pack(ourAttributes.get(type)); + } + } +} diff --git a/json/src/com/intellij/json/JsonTokenType.java b/json/src/com/intellij/json/JsonTokenType.java new file mode 100644 index 000000000000..a7752cd8a4e2 --- /dev/null +++ b/json/src/com/intellij/json/JsonTokenType.java @@ -0,0 +1,11 @@ +package com.intellij.json; + +import com.intellij.psi.tree.IElementType; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; + +public class JsonTokenType extends IElementType { + public JsonTokenType(@NotNull @NonNls String debugName) { + super(debugName, JsonLanguage.INSTANCE); + } +} diff --git a/json/src/com/intellij/json/JsonUtil.java b/json/src/com/intellij/json/JsonUtil.java new file mode 100644 index 000000000000..6b8bc98ec809 --- /dev/null +++ b/json/src/com/intellij/json/JsonUtil.java @@ -0,0 +1,37 @@ +package com.intellij.json; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author Mikhail Golubev + */ +public class JsonUtil { + private JsonUtil() { + // empty + } + + /** + * Clone of C# "as" operator. + * Checks if expression has correct type and casts it if it has. Returns null otherwise. + * It saves coder from "instanceof / cast" chains. + * + * Copied from PyCharm's {@code PyUtil}. + * + * @param expression expression to check + * @param cls class to cast + * @param class to cast + * @return expression casted to appropriate type (if could be casted). Null otherwise. + */ + @Nullable + @SuppressWarnings("unchecked") + public static T as(@Nullable final Object expression, @NotNull final Class cls) { + if (expression == null) { + return null; + } + if (cls.isAssignableFrom(expression.getClass())) { + return (T)expression; + } + return null; + } +} diff --git a/json/src/com/intellij/json/breadcrumbs/JsonBreadcrumbsProvider.java b/json/src/com/intellij/json/breadcrumbs/JsonBreadcrumbsProvider.java new file mode 100644 index 000000000000..2684bcbe1fb6 --- /dev/null +++ b/json/src/com/intellij/json/breadcrumbs/JsonBreadcrumbsProvider.java @@ -0,0 +1,49 @@ +package com.intellij.json.breadcrumbs; + +import com.intellij.json.JsonLanguage; +import com.intellij.lang.Language; +import com.intellij.psi.PsiElement; +import com.intellij.xml.breadcrumbs.BreadcrumbsInfoProvider; +import com.intellij.json.psi.JsonProperty; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author Mikhail Golubev + */ +public class JsonBreadcrumbsProvider extends BreadcrumbsInfoProvider { + private static final Language[] LANGUAGES = new Language[]{JsonLanguage.INSTANCE}; + + @Override + public Language[] getLanguages() { + return LANGUAGES; + } + + @Override + public boolean acceptElement(@NotNull PsiElement e) { + return e instanceof JsonProperty; + } + + @NotNull + @Override + public String getElementInfo(@NotNull PsiElement e) { + if (e instanceof JsonProperty) { + return ((JsonProperty)e).getName(); + } + //else if (isArrayElement(e)) { + // List elements = ((JsonArray)e.getParent()).getValueList(); + // for (int i = 0; i < elements.size(); i++) { + // if (e == elements.get(i)) { + // return String.valueOf(i); + // } + // } + //} + throw new AssertionError("Breadcrumbs can be extracted only from JsonProperty elements"); + } + + @Nullable + @Override + public String getElementTooltip(@NotNull PsiElement e) { + return null; + } +} diff --git a/json/src/com/intellij/json/codeinsight/JsonCompletionContributor.java b/json/src/com/intellij/json/codeinsight/JsonCompletionContributor.java new file mode 100644 index 000000000000..81d3564f98a0 --- /dev/null +++ b/json/src/com/intellij/json/codeinsight/JsonCompletionContributor.java @@ -0,0 +1,52 @@ +package com.intellij.json.codeinsight; + +import com.intellij.codeInsight.completion.*; +import com.intellij.codeInsight.lookup.LookupElementBuilder; +import com.intellij.json.psi.JsonArray; +import com.intellij.json.psi.JsonProperty; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.patterns.PsiElementPattern; +import com.intellij.psi.PsiElement; +import com.intellij.psi.impl.DebugUtil; +import com.intellij.util.ProcessingContext; +import org.jetbrains.annotations.NotNull; + +import static com.intellij.patterns.PlatformPatterns.psiElement; + +/** + * @author Mikhail Golubev + */ +public class JsonCompletionContributor extends CompletionContributor { + private static final Logger LOG = Logger.getInstance(JsonCompletionContributor.class); + + private static final PsiElementPattern.Capture AFTER_COLON_IN_PROPERTY = psiElement() + .afterLeaf(":").withSuperParent(2, JsonProperty.class); + private static final PsiElementPattern.Capture AFTER_COMMA_OR_BRACKET_IN_ARRAY = psiElement() + .afterLeaf("[", ",").withSuperParent(2, JsonArray.class); + @Override + public void fillCompletionVariants(CompletionParameters parameters, CompletionResultSet result) { + LOG.debug(DebugUtil.psiToString(parameters.getPosition().getContainingFile(), true)); + super.fillCompletionVariants(parameters, result); + } + + + public JsonCompletionContributor() { + extend(CompletionType.BASIC, AFTER_COLON_IN_PROPERTY, MyKeywordsCompletionProvider.INSTANCE); + extend(CompletionType.BASIC, AFTER_COMMA_OR_BRACKET_IN_ARRAY, MyKeywordsCompletionProvider.INSTANCE); + } + + + private static class MyKeywordsCompletionProvider extends CompletionProvider { + private static final MyKeywordsCompletionProvider INSTANCE = new MyKeywordsCompletionProvider(); + private static final String[] KEYWORDS = new String[]{"null", "true", "false"}; + + @Override + protected void addCompletions(@NotNull CompletionParameters parameters, + ProcessingContext context, + @NotNull CompletionResultSet result) { + for (String keyword : KEYWORDS) { + result.addElement(LookupElementBuilder.create(keyword).bold()); + } + } + } +} diff --git a/json/src/com/intellij/json/codeinsight/JsonStandardComplianceInspection.java b/json/src/com/intellij/json/codeinsight/JsonStandardComplianceInspection.java new file mode 100644 index 000000000000..0124c48fa7fe --- /dev/null +++ b/json/src/com/intellij/json/codeinsight/JsonStandardComplianceInspection.java @@ -0,0 +1,141 @@ +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.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 + *
    + *
  • Usage of line and block commentaries
  • + *
  • Usage of single quoted strings
  • + *
  • Usage of identifiers (unqouted words)
  • + *
  • Not double quoted string literal is used as property key
  • + *
+ * + * @author Mikhail Golubev + */ +public class JsonStandardComplianceInspection extends LocalInspectionTool { + 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) { + 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()); + } + 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()); + } + + @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(); + assert element instanceof JsonLiteral || element instanceof JsonReferenceExpression; + final String content = StringUtil.stripQuotesAroundValue(element.getText()); + element.replace(new JsonElementGenerator(project).createStringLiteral(content)); + } + } +} diff --git a/json/src/com/intellij/json/codeinsight/JsonStringLiteralAnnotator.java b/json/src/com/intellij/json/codeinsight/JsonStringLiteralAnnotator.java new file mode 100644 index 000000000000..a61758cd1867 --- /dev/null +++ b/json/src/com/intellij/json/codeinsight/JsonStringLiteralAnnotator.java @@ -0,0 +1,79 @@ +package com.intellij.json.codeinsight; + +import com.intellij.json.JsonBundle; +import com.intellij.json.psi.JsonStringLiteral; +import com.intellij.lang.annotation.AnnotationHolder; +import com.intellij.lang.annotation.Annotator; +import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.PsiElement; +import org.jetbrains.annotations.NotNull; + +/** + * @author Mikhail Golubev + */ +public class JsonStringLiteralAnnotator implements Annotator { + + @Override + public void annotate(@NotNull PsiElement element, @NotNull AnnotationHolder holder) { + if (element instanceof JsonStringLiteral) { + String text = element.getText(); + int offset = element.getTextOffset(); + int length = text.length(); + // Check that string literal closed properly + if (length <= 1 || text.charAt(0) != text.charAt(length - 1) || quoteEscaped(text, length - 1)) { + holder.createErrorAnnotation(element.getTextRange(), JsonBundle.message("msg.missing.closing.quote")); + } + // Check escape sequences validity + int pos = 1; + while (pos < length) { + if (text.charAt(pos) == '\\') { + if (pos >= length - 1) { + TextRange range = new TextRange(offset + pos, offset + pos + 1); + holder.createErrorAnnotation(range, JsonBundle.message("msg.illegal.escape.sequence")); + break; + } + char next = text.charAt(pos + 1); + switch (next) { + case '"': + case '\\': + case '/': + case 'b': + case 'f': + case 'n': + case 'r': + case 't': + pos += 2; + break; + case 'u': + int i = pos + 2; + for (; i < pos + 6; i++) { + if (i == length || !StringUtil.isHexDigit(text.charAt(i))) { + TextRange range = new TextRange(offset + pos, offset + i); + holder.createErrorAnnotation(range, JsonBundle.message("msg.illegal.unicode.escape.sequence")); + break; + } + } + pos = i; + break; + default: + TextRange range = new TextRange(offset + pos, offset + pos + 2); + holder.createErrorAnnotation(range, JsonBundle.message("msg.illegal.escape.sequence")); + pos += 2; + } + } + else { + pos++; + } + } + } + } + + private static boolean quoteEscaped(String text, int quotePos) { + int count = 0; + for (int i = quotePos - 1; i >= 0 && text.charAt(i) == '\\'; i--) { + count++; + } + return count % 2 != 0; + } +} diff --git a/json/src/com/intellij/json/editor/JsonCommenter.java b/json/src/com/intellij/json/editor/JsonCommenter.java new file mode 100644 index 000000000000..699f7006f985 --- /dev/null +++ b/json/src/com/intellij/json/editor/JsonCommenter.java @@ -0,0 +1,41 @@ +package com.intellij.json.editor; + +import com.intellij.lang.Commenter; +import org.jetbrains.annotations.Nullable; + +/** + * JSON standard (RFC 4627) doesn't allow comments in documents, but they are added for compatibility with legacy JSON integration. + * + * @author Mikhail Golubev + */ +public class JsonCommenter implements Commenter { + @Nullable + @Override + public String getLineCommentPrefix() { + return "//"; + } + + @Nullable + @Override + public String getBlockCommentPrefix() { + return "/*"; + } + + @Nullable + @Override + public String getBlockCommentSuffix() { + return "*/"; + } + + @Nullable + @Override + public String getCommentedBlockCommentPrefix() { + return null; + } + + @Nullable + @Override + public String getCommentedBlockCommentSuffix() { + return null; + } +} diff --git a/json/src/com/intellij/json/editor/folding/JsonFoldingBuilder.java b/json/src/com/intellij/json/editor/folding/JsonFoldingBuilder.java new file mode 100644 index 000000000000..fb7a6b6017bd --- /dev/null +++ b/json/src/com/intellij/json/editor/folding/JsonFoldingBuilder.java @@ -0,0 +1,87 @@ +package com.intellij.json.editor.folding; + +import com.intellij.json.JsonElementTypes; +import com.intellij.json.psi.JsonLiteral; +import com.intellij.json.psi.JsonObject; +import com.intellij.json.psi.JsonProperty; +import com.intellij.json.psi.JsonValue; +import com.intellij.lang.ASTNode; +import com.intellij.lang.folding.FoldingBuilder; +import com.intellij.lang.folding.FoldingDescriptor; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.project.DumbAware; +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.tree.IElementType; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Mikhail Golubev + */ +public class JsonFoldingBuilder implements FoldingBuilder, DumbAware { + @NotNull + @Override + public FoldingDescriptor[] buildFoldRegions(@NotNull ASTNode node, @NotNull Document document) { + final List descriptors = new ArrayList(); + collectDescriptorsRecursively(node, document, descriptors); + return descriptors.toArray(new FoldingDescriptor[descriptors.size()]); + } + + private static void collectDescriptorsRecursively(@NotNull ASTNode node, + @NotNull Document document, + @NotNull List descriptors) { + final IElementType type = node.getElementType(); + if ((type == JsonElementTypes.OBJECT || type == JsonElementTypes.ARRAY) && spanMultipleLines(node, document)) { + descriptors.add(new FoldingDescriptor(node, node.getTextRange())); + } + for (ASTNode child : node.getChildren(null)) { + collectDescriptorsRecursively(child, document, descriptors); + } + } + + @Nullable + @Override + public String getPlaceholderText(@NotNull ASTNode node) { + final IElementType type = node.getElementType(); + if (type == JsonElementTypes.OBJECT) { + final JsonObject object = node.getPsi(JsonObject.class); + final List properties = object.getPropertyList(); + JsonProperty candidate = null; + for (JsonProperty property : properties) { + final String name = property.getName(); + final JsonValue value = property.getValue(); + if (value instanceof JsonLiteral) { + if ("id".equals(name) || "name".equals(name)) { + candidate = property; + break; + } + if (candidate == null) { + candidate = property; + } + } + } + if (candidate != null) { + //noinspection ConstantConditions + return "{\"" + candidate.getName() + "\": " + candidate.getValue().getText() + "...}"; + } + return "{...}"; + } + else if (type == JsonElementTypes.ARRAY) { + return "[...]"; + } + return "..."; + } + + @Override + public boolean isCollapsedByDefault(@NotNull ASTNode node) { + return false; + } + + private static boolean spanMultipleLines(@NotNull ASTNode node, @NotNull Document document) { + final TextRange range = node.getTextRange(); + return document.getLineNumber(range.getStartOffset()) < document.getLineNumber(range.getEndOffset()); + } +} diff --git a/json/src/com/intellij/json/editor/lineMover/JsonLineMover.java b/json/src/com/intellij/json/editor/lineMover/JsonLineMover.java new file mode 100644 index 000000000000..7c18f8609e91 --- /dev/null +++ b/json/src/com/intellij/json/editor/lineMover/JsonLineMover.java @@ -0,0 +1,88 @@ +package com.intellij.json.editor.lineMover; + +import com.intellij.codeInsight.editorActions.moveUpDown.LineMover; +import com.intellij.json.JsonElementTypes; +import com.intellij.json.psi.*; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.PsiDocumentManager; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.impl.source.tree.TreeUtil; +import com.intellij.psi.util.PsiTreeUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author Mikhail Golubev + */ +public class JsonLineMover extends LineMover { + + private boolean myShouldAddComma = false; + + @Override + public boolean checkAvailable(@NotNull Editor editor, @NotNull PsiFile file, @NotNull MoveInfo info, boolean down) { + myShouldAddComma = false; + + if (!(file instanceof JsonFile) || !super.checkAvailable(editor, file, info, down)) { + return false; + } + + final Pair movedElementRange = getElementRange(editor, file, info.toMove); + final Pair destElementRange = getElementRange(editor, file, info.toMove2); + + if (!isValidElementRange(movedElementRange) || !isValidElementRange(destElementRange)) { + return false; + } + + if (movedElementRange.getFirst().getParent() != destElementRange.getSecond().getParent()) { + return false; + } + + final PsiElement commonParent = movedElementRange.getFirst().getParent(); + final PsiElement lowerRightElement = down ? destElementRange.getSecond() : movedElementRange.getSecond(); + // Destination rightmost element is not closing brace or bracket + if (lowerRightElement instanceof JsonElement) { + if (commonParent instanceof JsonArray && notFollowedByNextElementOrComma(lowerRightElement, JsonValue.class) || + commonParent instanceof JsonObject && notFollowedByNextElementOrComma(lowerRightElement, JsonProperty.class)) { + myShouldAddComma = true; + } + } + + return true; + } + + @Override + public void beforeMove(@NotNull Editor editor, @NotNull MoveInfo info, boolean down) { + if (myShouldAddComma) { + final Document document = editor.getDocument(); + final int lineBelow = down ? info.toMove2.endLine - 1 : info.toMove.endLine - 1; + document.insertString(document.getLineEndOffset(lineBelow), ","); + + final int lineAbove = down ? info.toMove.endLine - 1 : info.toMove2.endLine - 1; + final int lineAboveEndOffset = document.getLineEndOffset(lineAbove); + final String aboveLineEnding = document.getText(new TextRange(lineAboveEndOffset - 1, lineAboveEndOffset)); + if (aboveLineEnding.equals(",")) { + document.deleteString(lineAboveEndOffset - 1, lineAboveEndOffset); + } + final Project project = editor.getProject(); + assert project != null; + PsiDocumentManager.getInstance(project).commitDocument(document); + } + } + + private static boolean notFollowedByNextElementOrComma(@NotNull PsiElement anchor, @NotNull Class nextElementType) { + return PsiTreeUtil.getNextSiblingOfType(anchor, nextElementType) == null && + TreeUtil.findSibling(anchor.getNode(), JsonElementTypes.COMMA) == null; + } + + private static boolean isValidElementRange(@Nullable Pair elementRange) { + if (elementRange == null) { + return false; + } + return elementRange.getFirst().getParent() == elementRange.getSecond().getParent(); + } +} diff --git a/json/src/com/intellij/json/editor/smartEnter/JsonSmartEnterProcessor.java b/json/src/com/intellij/json/editor/smartEnter/JsonSmartEnterProcessor.java new file mode 100644 index 000000000000..ed695d7425fc --- /dev/null +++ b/json/src/com/intellij/json/editor/smartEnter/JsonSmartEnterProcessor.java @@ -0,0 +1,123 @@ +package com.intellij.json.editor.smartEnter; + +import com.intellij.json.psi.JsonArray; +import com.intellij.json.psi.JsonFile; +import com.intellij.json.psi.JsonProperty; +import com.intellij.json.psi.JsonValue; +import com.intellij.lang.SmartEnterProcessorWithFixers; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.Editor; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiWhiteSpace; +import com.intellij.psi.tree.IElementType; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +import static com.intellij.json.JsonElementTypes.COLON; +import static com.intellij.json.JsonElementTypes.COMMA; + +/** + * This processor allows + *
    + *
  • Insert colon after key inside object property
  • + *
  • Insert comma after array element or object property
  • + *
+ * + * @author Mikhail Golubev + */ +public class JsonSmartEnterProcessor extends SmartEnterProcessorWithFixers { + public static final Logger LOG = Logger.getInstance(JsonSmartEnterProcessor.class); + + private boolean myShouldAddNewline = false; + + public JsonSmartEnterProcessor() { + addFixers(new JsonObjectPropertyFixer(), new JsonArrayElementFixer()); + addEnterProcessors(new JsonEnterProcessor()); + } + + @Override + protected void collectAdditionalElements(@NotNull PsiElement element, @NotNull List result) { + // include all parents as well + PsiElement parent = element.getParent(); + while (parent != null && !(parent instanceof JsonFile)) { + result.add(parent); + parent = parent.getParent(); + } + } + + private static boolean terminatedOnCurrentLine(@NotNull Editor editor, @NotNull PsiElement element) { + final Document document = editor.getDocument(); + final int caretOffset = editor.getCaretModel().getCurrentCaret().getOffset(); + final int elementEndOffset = element.getTextRange().getEndOffset(); + if (document.getLineNumber(elementEndOffset) != document.getLineNumber(caretOffset)) { + return false; + } + // Skip empty PsiError elements if comma is missing + PsiElement nextLeaf = PsiTreeUtil.nextLeaf(element, true); + return nextLeaf == null || (nextLeaf instanceof PsiWhiteSpace && nextLeaf.getText().contains("\n")); + } + + private static boolean isFollowedByTerminal(@NotNull PsiElement element, IElementType type) { + final PsiElement nextLeaf = PsiTreeUtil.nextVisibleLeaf(element); + return nextLeaf != null && nextLeaf.getNode().getElementType() == type; + } + + private static class JsonArrayElementFixer extends SmartEnterProcessorWithFixers.Fixer { + @Override + public void apply(@NotNull Editor editor, @NotNull JsonSmartEnterProcessor processor, @NotNull PsiElement element) + throws IncorrectOperationException { + if (element instanceof JsonValue && element.getParent() instanceof JsonArray) { + final JsonValue arrayElement = (JsonValue)element; + if (terminatedOnCurrentLine(editor, arrayElement) && !isFollowedByTerminal(element, COMMA)) { + editor.getDocument().insertString(arrayElement.getTextRange().getEndOffset(), ","); + processor.myShouldAddNewline = true; + } + } + } + } + + private class JsonObjectPropertyFixer extends SmartEnterProcessorWithFixers.Fixer { + @Override + public void apply(@NotNull Editor editor, @NotNull JsonSmartEnterProcessor processor, @NotNull PsiElement element) + throws IncorrectOperationException { + if (element instanceof JsonProperty) { + final JsonValue propertyValue = ((JsonProperty)element).getValue(); + if (propertyValue != null) { + if (terminatedOnCurrentLine(editor, propertyValue) && !isFollowedByTerminal(propertyValue, COMMA)) { + editor.getDocument().insertString(propertyValue.getTextRange().getEndOffset(), ","); + processor.myShouldAddNewline = true; + } + } + else { + final JsonValue propertyKey = ((JsonProperty)element).getNameElement(); + final int keyEndOffset = propertyKey.getTextRange().getEndOffset(); + //processor.myFirstErrorOffset = keyEndOffset; + if (terminatedOnCurrentLine(editor, propertyKey) && !isFollowedByTerminal(propertyKey, COLON)) { + processor.myFirstErrorOffset = keyEndOffset + 2; + editor.getDocument().insertString(keyEndOffset, ": "); + } + } + } + } + } + + private class JsonEnterProcessor extends SmartEnterProcessorWithFixers.FixEnterProcessor { + @Override + public boolean doEnter(PsiElement atCaret, PsiFile file, @NotNull Editor editor, boolean modified) { + if (myShouldAddNewline) { + try { + plainEnter(editor); + } + finally { + myShouldAddNewline = false; + } + } + return true; + } + } +} diff --git a/json/src/com/intellij/json/findUsages/JsonFindUsagesProvider.java b/json/src/com/intellij/json/findUsages/JsonFindUsagesProvider.java new file mode 100644 index 000000000000..fb7dfd3c26fa --- /dev/null +++ b/json/src/com/intellij/json/findUsages/JsonFindUsagesProvider.java @@ -0,0 +1,54 @@ +package com.intellij.json.findUsages; + +import com.intellij.json.psi.JsonProperty; +import com.intellij.lang.HelpID; +import com.intellij.lang.cacheBuilder.WordsScanner; +import com.intellij.lang.findUsages.FindUsagesProvider; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiNamedElement; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author Mikhail Golubev + */ +public class JsonFindUsagesProvider implements FindUsagesProvider { + @Nullable + @Override + public WordsScanner getWordsScanner() { + return new JsonWordScanner(); + } + + @Override + public boolean canFindUsagesFor(@NotNull PsiElement psiElement) { + return psiElement instanceof PsiNamedElement; + } + + @Nullable + @Override + public String getHelpId(@NotNull PsiElement psiElement) { + return HelpID.FIND_OTHER_USAGES; + } + + @NotNull + @Override + public String getType(@NotNull PsiElement element) { + if (element instanceof JsonProperty) { + return "property"; + } + return ""; + } + + @NotNull + @Override + public String getDescriptiveName(@NotNull PsiElement element) { + final String name = element instanceof PsiNamedElement ? ((PsiNamedElement)element).getName() : null; + return name != null ? name : ""; + } + + @NotNull + @Override + public String getNodeText(@NotNull PsiElement element, boolean useFullName) { + return getDescriptiveName(element); + } +} diff --git a/json/src/com/intellij/json/findUsages/JsonWordScanner.java b/json/src/com/intellij/json/findUsages/JsonWordScanner.java new file mode 100644 index 000000000000..2291d16a4c06 --- /dev/null +++ b/json/src/com/intellij/json/findUsages/JsonWordScanner.java @@ -0,0 +1,19 @@ +package com.intellij.json.findUsages; + +import com.intellij.json.JsonElementTypes; +import com.intellij.json.JsonLexer; +import com.intellij.lang.cacheBuilder.DefaultWordsScanner; +import com.intellij.psi.tree.TokenSet; + +import static com.intellij.json.JsonParserDefinition.JSON_COMMENTARIES; +import static com.intellij.json.JsonParserDefinition.JSON_LITERALS; + +/** + * @author Mikhail Golubev + */ +public class JsonWordScanner extends DefaultWordsScanner { + public JsonWordScanner() { + super(new JsonLexer(), TokenSet.create(JsonElementTypes.INDENTIFIER), JSON_COMMENTARIES, JSON_LITERALS); + setMayHaveFileRefsInLiterals(true); + } +} diff --git a/json/src/com/intellij/json/formatter/JsonBlock.java b/json/src/com/intellij/json/formatter/JsonBlock.java new file mode 100644 index 000000000000..ce2960a2f1a0 --- /dev/null +++ b/json/src/com/intellij/json/formatter/JsonBlock.java @@ -0,0 +1,205 @@ +package com.intellij.json.formatter; + +import com.intellij.formatting.*; +import com.intellij.json.JsonElementTypes; +import com.intellij.json.JsonLanguage; +import com.intellij.json.JsonParserDefinition; +import com.intellij.json.psi.JsonProperty; +import com.intellij.lang.ASTNode; +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.PsiElement; +import com.intellij.psi.TokenType; +import com.intellij.psi.codeStyle.CodeStyleSettings; +import com.intellij.psi.codeStyle.CommonCodeStyleSettings; +import com.intellij.psi.formatter.FormatterUtil; +import com.intellij.psi.tree.IElementType; +import com.intellij.psi.tree.TokenSet; +import com.intellij.util.Function; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +import static com.intellij.json.JsonParserDefinition.JSON_BRACES; +import static com.intellij.json.JsonParserDefinition.JSON_BRACKETS; + +/** + * Mostly based on PyBlock implementation. + * + * @author Mikhail Golubev + */ +public class JsonBlock implements ASTBlock { + private static final TokenSet OPEN_BRACES = TokenSet.create(JsonElementTypes.L_BRACKET, JsonElementTypes.L_CURLY); + private static final TokenSet CLOSE_BRACES = TokenSet.create(JsonElementTypes.R_BRACKET, JsonElementTypes.R_CURLY); + private static final TokenSet BRACES = TokenSet.orSet(OPEN_BRACES, CLOSE_BRACES); + + private JsonBlock myParent; + + private ASTNode myNode; + private PsiElement myPsiElement; + private Alignment myAlignment; + private Indent myIndent; + private Wrap myWrap; + private CodeStyleSettings mySettings; + private SpacingBuilder mySpacingBuilder; + // lazy initialized on first call to #getSubBlocks() + private List mySubBlocks = null; + + private Alignment myChildAlignment = Alignment.createAlignment(); + + public JsonBlock(@Nullable JsonBlock parent, + @NotNull ASTNode node, + @NotNull CodeStyleSettings settings, + @Nullable Alignment alignment, + @NotNull Indent indent, + @Nullable Wrap wrap) { + myParent = parent; + myNode = node; + myPsiElement = node.getPsi(); + myAlignment = alignment; + myIndent = indent; + myWrap = wrap; + mySettings = settings; + + mySpacingBuilder = JsonFormattingBuilderModel.createSpacingBuilder(settings); + } + + @Override + public ASTNode getNode() { + return myNode; + } + + @NotNull + @Override + public TextRange getTextRange() { + return myNode.getTextRange(); + } + + @NotNull + @Override + public List getSubBlocks() { + if (mySubBlocks == null) { + mySubBlocks = ContainerUtil.mapNotNull(myNode.getChildren(null), new Function() { + @Override + public Block fun(ASTNode node) { + if (isWhitespaceOrEmpty(node)) { + return null; + } + return makeSubBlock(node); + } + }); + } + return mySubBlocks; + } + + private Block makeSubBlock(@NotNull ASTNode childNode) { + IElementType childNodeType = childNode.getElementType(); + + Indent indent = Indent.getNoneIndent(); + Alignment alignment = null; + Wrap wrap = null; + + if (isContainer() && childNodeType != JsonElementTypes.COMMA && !BRACES.contains(childNodeType)) { + if (!FormatterUtil.isPrecededBy(childNode, OPEN_BRACES)) { + wrap = Wrap.createWrap(WrapType.ALWAYS, true); + } + alignment = myChildAlignment; + indent = Indent.getNormalIndent(); + } + return new JsonBlock(this, childNode, mySettings, alignment, indent, wrap); + } + + @Nullable + @Override + public Wrap getWrap() { + return myWrap; + } + + @Nullable + @Override + public Indent getIndent() { + return myIndent; + } + + @Nullable + @Override + public Alignment getAlignment() { + return myAlignment; + } + + @Nullable + @Override + public Spacing getSpacing(@Nullable Block child1, @NotNull Block child2) { + final CommonCodeStyleSettings commonSettings = mySettings.getCommonSettings(JsonLanguage.INSTANCE); + final JsonCodeStyleSettings customSettings = mySettings.getCustomSettings(JsonCodeStyleSettings.class); + final IElementType leftChildType = child1 instanceof JsonBlock ? ((JsonBlock)child1).myNode.getElementType() : null; + final IElementType rightChildType = child2 instanceof JsonBlock ? ((JsonBlock)child2).myNode.getElementType() : null; + if (leftChildType != null && rightChildType != null) { + if (JSON_BRACES.contains(leftChildType) ^ JSON_BRACES.contains(rightChildType)) { + final int numSpaces = customSettings.SPACE_WITHIN_BRACES ? 1 : 0; + return Spacing.createDependentLFSpacing(numSpaces, numSpaces, myNode.getTextRange(), + commonSettings.KEEP_LINE_BREAKS, + commonSettings.KEEP_BLANK_LINES_IN_CODE); + } + else if (JSON_BRACKETS.contains(leftChildType) ^ JSON_BRACKETS.contains(rightChildType)) { + final int numSpaces = commonSettings.SPACE_WITHIN_BRACKETS ? 1 : 0; + return Spacing.createDependentLFSpacing(numSpaces, numSpaces, myNode.getTextRange(), + commonSettings.KEEP_LINE_BREAKS, + commonSettings.KEEP_BLANK_LINES_IN_CODE); + } + } + return mySpacingBuilder.getSpacing(this, child1, child2); + } + + @NotNull + @Override + public ChildAttributes getChildAttributes(int newChildIndex) { + JsonBlock prevChildBlock = newChildIndex > 0 ? (JsonBlock)mySubBlocks.get(newChildIndex - 1) : null; + ASTNode prevChildNode = prevChildBlock != null? prevChildBlock.myNode : null; + if (myNode.getElementType() == JsonParserDefinition.FILE) { + return new ChildAttributes(Indent.getNoneIndent(), null); + } + if (isContainer() && prevChildNode != null) { + // correctly indent first element after opening brace + if (OPEN_BRACES.contains(prevChildNode.getElementType()) || prevChildNode.getElementType() == JsonElementTypes.COMMA) { + return new ChildAttributes(Indent.getNormalIndent(), myChildAlignment); + } + } +// // TODO find out why inside object then cursor is after '"a": []', myNode is instance of JsonArray +// if (isContainer() && prevChildBlock != null) { +// return ChildAttributes.DELEGATE_TO_PREV_CHILD; +// } + return new ChildAttributes(Indent.getNormalIndent(), null); + } + + @Override + public boolean isIncomplete() { + IElementType nodeType = myNode.getElementType(); + ASTNode lastChildNode = myNode.getLastChildNode(); + if (nodeType == JsonElementTypes.OBJECT) { + return lastChildNode != null && lastChildNode.getElementType() == JsonElementTypes.R_CURLY; + } + else if (nodeType == JsonElementTypes.ARRAY) { + return lastChildNode != null && lastChildNode.getElementType() == JsonElementTypes.R_BRACKET; + } + else if (myPsiElement instanceof JsonProperty) { + return ((JsonProperty)myPsiElement).getValue() != null; + } + return false; + } + + @Override + public boolean isLeaf() { + return myNode.getFirstChildNode() == null; + } + + private static boolean isWhitespaceOrEmpty(ASTNode node) { + return node.getElementType() == TokenType.WHITE_SPACE || node.getTextLength() == 0; + } + + private boolean isContainer() { + return JsonParserDefinition.JSON_CONTAINERS.contains(myNode.getElementType()); + } + +} diff --git a/json/src/com/intellij/json/formatter/JsonCodeStyleSettings.java b/json/src/com/intellij/json/formatter/JsonCodeStyleSettings.java new file mode 100644 index 000000000000..5bb29b5707bb --- /dev/null +++ b/json/src/com/intellij/json/formatter/JsonCodeStyleSettings.java @@ -0,0 +1,21 @@ +package com.intellij.json.formatter; + +import com.intellij.json.JsonLanguage; +import com.intellij.psi.codeStyle.CodeStyleSettings; +import com.intellij.psi.codeStyle.CustomCodeStyleSettings; + +/** + * @author Mikhail Golubev + */ +public class JsonCodeStyleSettings extends CustomCodeStyleSettings { + + // Standard option has awkward description "Code braces" + public boolean SPACE_WITHIN_BRACES = false; + public boolean SPACE_AFTER_COLON = true; + public boolean SPACE_BEFORE_COLON = false; + + + public JsonCodeStyleSettings(CodeStyleSettings container) { + super(JsonLanguage.INSTANCE.getID(), container); + } +} diff --git a/json/src/com/intellij/json/formatter/JsonCodeStyleSettingsProvider.java b/json/src/com/intellij/json/formatter/JsonCodeStyleSettingsProvider.java new file mode 100644 index 000000000000..712a4cdedb88 --- /dev/null +++ b/json/src/com/intellij/json/formatter/JsonCodeStyleSettingsProvider.java @@ -0,0 +1,56 @@ +package com.intellij.json.formatter; + +import com.intellij.application.options.CodeStyleAbstractConfigurable; +import com.intellij.application.options.CodeStyleAbstractPanel; +import com.intellij.application.options.TabbedLanguageCodeStylePanel; +import com.intellij.json.JsonLanguage; +import com.intellij.lang.Language; +import com.intellij.openapi.options.Configurable; +import com.intellij.psi.codeStyle.CodeStyleSettings; +import com.intellij.psi.codeStyle.CodeStyleSettingsProvider; +import com.intellij.psi.codeStyle.CustomCodeStyleSettings; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author Mikhail Golubev + */ +public class JsonCodeStyleSettingsProvider extends CodeStyleSettingsProvider { + @NotNull + @Override + public Configurable createSettingsPage(CodeStyleSettings settings, CodeStyleSettings originalSettings) { + return new CodeStyleAbstractConfigurable(settings, originalSettings, "JSON") { + @Override + protected CodeStyleAbstractPanel createPanel(CodeStyleSettings settings) { + final Language language = JsonLanguage.INSTANCE; + final CodeStyleSettings currentSettings = getCurrentSettings(); + return new TabbedLanguageCodeStylePanel(language, currentSettings, settings) { + @Override + protected void initTabs(CodeStyleSettings settings) { + addIndentOptionsTab(settings); + addSpacesTab(settings); + addBlankLinesTab(settings); + } + }; + } + + @Nullable + @Override + public String getHelpTopic() { + return null; + } + }; + } + + @Nullable + @Override + public String getConfigurableDisplayName() { + return JsonLanguage.INSTANCE.getDisplayName(); + } + + @Nullable + @Override + public CustomCodeStyleSettings createCustomSettings(CodeStyleSettings settings) { + return new JsonCodeStyleSettings(settings); + } +} diff --git a/json/src/com/intellij/json/formatter/JsonEnterBetweenBracesHandler.java b/json/src/com/intellij/json/formatter/JsonEnterBetweenBracesHandler.java new file mode 100644 index 000000000000..71638e80fba0 --- /dev/null +++ b/json/src/com/intellij/json/formatter/JsonEnterBetweenBracesHandler.java @@ -0,0 +1,13 @@ +package com.intellij.json.formatter; + +import com.intellij.codeInsight.editorActions.enter.EnterBetweenBracesHandler; + +/** + * @author Mikhail Golubev + */ +public class JsonEnterBetweenBracesHandler extends EnterBetweenBracesHandler { + @Override + protected boolean isBracePair(char c1, char c2) { + return (c1 == '{' && c2 == '}') || (c1 == '[' && c2 == ']'); + } +} diff --git a/json/src/com/intellij/json/formatter/JsonFormattingBuilderModel.java b/json/src/com/intellij/json/formatter/JsonFormattingBuilderModel.java new file mode 100644 index 000000000000..1074d32fcb61 --- /dev/null +++ b/json/src/com/intellij/json/formatter/JsonFormattingBuilderModel.java @@ -0,0 +1,65 @@ +package com.intellij.json.formatter; + +import com.intellij.formatting.*; +import com.intellij.lang.ASTNode; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.codeStyle.CodeStyleSettings; +import com.intellij.psi.codeStyle.CommonCodeStyleSettings; +import com.intellij.psi.impl.DebugUtil; +import com.intellij.json.JsonLanguage; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import static com.intellij.json.JsonElementTypes.*; + +/** + * @author Mikhail Golubev + */ +public class JsonFormattingBuilderModel implements FormattingModelBuilder { + private static final Logger LOG = Logger.getInstance(JsonFormattingBuilderModel.class); + + @NotNull + @Override + public FormattingModel createModel(PsiElement element, CodeStyleSettings settings) { + if (LOG.isDebugEnabled()) { + LOG.debug("PSI Tree:\n" + DebugUtil.psiToString(element, false)); + } + LOG.debug("Right margin: " + settings.getRightMargin(JsonLanguage.INSTANCE)); + JsonBlock block = new JsonBlock(null, element.getNode(), settings, null, Indent.getNoneIndent(), null); + if (LOG.isDebugEnabled()) { + StringBuilder builder = new StringBuilder(); + FormattingModelDumper.dumpFormattingModel(block, 2, builder); + LOG.debug("Format Model:\n" + builder.toString()); + } + return FormattingModelProvider.createFormattingModelForPsiFile(element.getContainingFile(), block, settings); + } + + @Nullable + @Override + public TextRange getRangeAffectingIndent(PsiFile file, int offset, ASTNode elementAtOffset) { + return null; + } + + // create spacing model once for all subsequent blocks + static SpacingBuilder createSpacingBuilder(CodeStyleSettings settings) { + JsonCodeStyleSettings jsonSettings = settings.getCustomSettings(JsonCodeStyleSettings.class); + CommonCodeStyleSettings commonSettings = settings.getCommonSettings(JsonLanguage.INSTANCE); + + + int spacesBeforeComma = commonSettings.SPACE_BEFORE_COMMA ? 1 : 0; + int spacesBeforeColon = jsonSettings.SPACE_BEFORE_COLON ? 1 : 0; + int spacesAfterColon = jsonSettings.SPACE_AFTER_COLON ? 1 : 0; + // not allow to keep line breaks before colon/comma, because it looks horrible + + return new SpacingBuilder(settings, JsonLanguage.INSTANCE) + .before(COLON).spacing(spacesBeforeColon, spacesBeforeColon, 0, false, 0) + .after(COLON).spacing(spacesAfterColon, spacesAfterColon, 0, false, 0) + .withinPair(L_BRACKET, R_BRACKET).spaceIf(commonSettings.SPACE_WITHIN_BRACKETS) + .withinPair(L_CURLY, R_CURLY).spaceIf(jsonSettings.SPACE_WITHIN_BRACES) + .before(COMMA).spacing(spacesBeforeComma, spacesBeforeComma, 0, false, 0) + .after(COMMA).spaceIf(commonSettings.SPACE_AFTER_COMMA); + } +} diff --git a/json/src/com/intellij/json/formatter/JsonLanguageCodeStyleSettingsProvider.java b/json/src/com/intellij/json/formatter/JsonLanguageCodeStyleSettingsProvider.java new file mode 100644 index 000000000000..541e54c3828f --- /dev/null +++ b/json/src/com/intellij/json/formatter/JsonLanguageCodeStyleSettingsProvider.java @@ -0,0 +1,75 @@ +package com.intellij.json.formatter; + +import com.intellij.application.options.IndentOptionsEditor; +import com.intellij.application.options.SmartIndentOptionsEditor; +import com.intellij.lang.Language; +import com.intellij.psi.codeStyle.CodeStyleSettingsCustomizable; +import com.intellij.psi.codeStyle.CommonCodeStyleSettings; +import com.intellij.psi.codeStyle.LanguageCodeStyleSettingsProvider; +import com.intellij.json.JsonLanguage; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import static com.intellij.psi.codeStyle.CodeStyleSettingsCustomizable.SPACES_OTHER; +import static com.intellij.psi.codeStyle.CodeStyleSettingsCustomizable.SPACES_WITHIN; + +/** + * @author Mikhail Golubev + */ +public class JsonLanguageCodeStyleSettingsProvider extends LanguageCodeStyleSettingsProvider { + private static final String SAMPLE = "{\n" + + " \"json literals are\": {\n" + + " \"strings\": [\"foo\", \"bar\", \"\\u0062\\u0061\\u0072\"],\n" + + " \"numbers\": [42, 6.62606975e-34],\n" + + " \"boolean values\": [true, false],\n" + + " \"and\": {\"null\": null}\n" + + " }\n" + + "}"; + + + @Override + public void customizeSettings(@NotNull CodeStyleSettingsCustomizable consumer, @NotNull SettingsType settingsType) { + if (settingsType == SettingsType.SPACING_SETTINGS) { + consumer.showStandardOptions("SPACE_WITHIN_BRACKETS", + "SPACE_AFTER_COMMA", + "SPACE_BEFORE_COMMA" + ); + consumer.showCustomOption(JsonCodeStyleSettings.class, "SPACE_WITHIN_BRACES", "Braces", SPACES_WITHIN); + consumer.showCustomOption(JsonCodeStyleSettings.class, "SPACE_BEFORE_COLON", "Before ':'", SPACES_OTHER); + consumer.showCustomOption(JsonCodeStyleSettings.class, "SPACE_AFTER_COLON", "After ':'", SPACES_OTHER); + } + if (settingsType == SettingsType.BLANK_LINES_SETTINGS) { + consumer.showStandardOptions("KEEP_BLANK_LINES_IN_CODE"); + } + super.customizeSettings(consumer, settingsType); + } + + @NotNull + @Override + public Language getLanguage() { + return JsonLanguage.INSTANCE; + } + + @Nullable + @Override + public IndentOptionsEditor getIndentOptionsEditor() { + // TODO: read sources of this one carefully + return new SmartIndentOptionsEditor(); + } + + @Override + public String getCodeSample(@NotNull SettingsType settingsType) { + return SAMPLE; + } + + @Nullable + @Override + public CommonCodeStyleSettings getDefaultCommonSettings() { + CommonCodeStyleSettings commonSettings = new CommonCodeStyleSettings(JsonLanguage.INSTANCE); + CommonCodeStyleSettings.IndentOptions indentOptions = commonSettings.initIndentOptions(); + indentOptions.INDENT_SIZE = 2; + // strip all blank lines by default + commonSettings.KEEP_BLANK_LINES_IN_CODE = 0; + return commonSettings; + } +} diff --git a/json/src/com/intellij/json/psi/JsonElement.java b/json/src/com/intellij/json/psi/JsonElement.java new file mode 100644 index 000000000000..f33452553f02 --- /dev/null +++ b/json/src/com/intellij/json/psi/JsonElement.java @@ -0,0 +1,10 @@ +package com.intellij.json.psi; + +import com.intellij.psi.NavigatablePsiElement; +import com.intellij.psi.PsiElement; + +/** + * @author Mikhail Golubev + */ +public interface JsonElement extends PsiElement, NavigatablePsiElement { +} diff --git a/json/src/com/intellij/json/psi/JsonElementGenerator.java b/json/src/com/intellij/json/psi/JsonElementGenerator.java new file mode 100644 index 000000000000..4e242c4f9f9b --- /dev/null +++ b/json/src/com/intellij/json/psi/JsonElementGenerator.java @@ -0,0 +1,58 @@ +package com.intellij.json.psi; + +import com.intellij.json.JsonFileType; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiFileFactory; +import org.jetbrains.annotations.NotNull; + +/** + * @author Mikhail Golubev + */ +public class JsonElementGenerator { + private final Project myProject; + + public JsonElementGenerator(@NotNull Project project) { + myProject = project; + } + + /** + * Create lightweight in-memory {@link com.intellij.json.psi.JsonFile} filled with {@code content}. + * + * @param content content of the file to be created + * @return created file + */ + @NotNull + public PsiFile createDummyFile(@NotNull String content) { + final PsiFileFactory psiFileFactory = PsiFileFactory.getInstance(myProject); + return psiFileFactory.createFileFromText("dummy." + JsonFileType.INSTANCE.getDefaultExtension(), JsonFileType.INSTANCE, content); + } + + /** + * Create JSON value from supplied content. + * + * @param content properly escaped text of JSON value, e.g. Java literal {@code "\"new\\nline\""} if you want to create string literal + * @param type of the JSON value desired + * @return element created from given text + * + * @see #createStringLiteral(String) + */ + @NotNull + public T createValue(@NotNull String content) { + final PsiFile file = createDummyFile("{\"foo\": " + content + "}"); + //noinspection unchecked,ConstantConditions + return (T)((JsonObject)file.getFirstChild()).getPropertyList().get(0).getValue(); + } + + /** + * Create JSON string literal from supplied unescaped content. + * + * @param unescapedContent unescaped content of string literal, e.g. Java literal {@code "new\nline"} (compare with {@link #createValue(String)}). + * @return JSON string literal created from given text + */ + @NotNull + public JsonStringLiteral createStringLiteral(@NotNull String unescapedContent) { + return createValue('"' + StringUtil.escapeStringCharacters(unescapedContent) + '"'); + } +} diff --git a/json/src/com/intellij/json/psi/JsonFile.java b/json/src/com/intellij/json/psi/JsonFile.java new file mode 100644 index 000000000000..6d5bce02010a --- /dev/null +++ b/json/src/com/intellij/json/psi/JsonFile.java @@ -0,0 +1,17 @@ +package com.intellij.json.psi; + +import com.intellij.psi.PsiFile; +import org.jetbrains.annotations.Nullable; + +/** + * @author Mikhail Golubev + */ +public interface JsonFile extends JsonElement, PsiFile { + /** + * Returns {@link JsonArray} or {@link JsonObject} value according to JSON standard. + * + * @return top-level JSON element if any or {@code null} otherwise + */ + @Nullable + JsonValue getTopLevelValue(); +} diff --git a/json/src/com/intellij/json/psi/JsonParserUtil.java b/json/src/com/intellij/json/psi/JsonParserUtil.java new file mode 100644 index 000000000000..d0f5f4674776 --- /dev/null +++ b/json/src/com/intellij/json/psi/JsonParserUtil.java @@ -0,0 +1,25 @@ +package com.intellij.json.psi; + +import com.intellij.json.JsonElementTypes; +import com.intellij.lang.PsiBuilder; +import com.intellij.lang.parser.GeneratedParserUtilBase; +import com.intellij.psi.tree.IElementType; +import org.jetbrains.annotations.NotNull; + +/** + * @author Mikhail Golubev + */ +public class JsonParserUtil extends GeneratedParserUtilBase { + + public static boolean notTrailingComma(@NotNull PsiBuilder builder, int level) { + if (builder.getTokenType() != JsonElementTypes.COMMA) { + return false; + } + final IElementType afterComma = builder.lookAhead(1); + if (afterComma == JsonElementTypes.R_BRACKET || afterComma == JsonElementTypes.R_CURLY) { + builder.error("trailing comma"); + } + builder.advanceLexer(); + return true; + } +} diff --git a/json/src/com/intellij/json/psi/JsonPsiChangeUtils.java b/json/src/com/intellij/json/psi/JsonPsiChangeUtils.java new file mode 100644 index 000000000000..4c4a9385c742 --- /dev/null +++ b/json/src/com/intellij/json/psi/JsonPsiChangeUtils.java @@ -0,0 +1,42 @@ +package com.intellij.json.psi; + +import com.intellij.json.JsonElementTypes; +import com.intellij.lang.ASTNode; +import com.intellij.psi.TokenType; + +public class JsonPsiChangeUtils { + public static void removeCommaSeparatedFromList(final ASTNode myNode, final ASTNode parent) { + ASTNode from = myNode, to = myNode.getTreeNext(); + + boolean seenComma = false; + + ASTNode toCandidate = to; + while (toCandidate != null && toCandidate.getElementType() == TokenType.WHITE_SPACE) { + toCandidate = toCandidate.getTreeNext(); + } + + if (toCandidate != null && toCandidate.getElementType() == JsonElementTypes.COMMA) { + toCandidate = toCandidate.getTreeNext(); + to = toCandidate; + seenComma = true; + + if (to != null && to.getElementType() == TokenType.WHITE_SPACE) { + to = to.getTreeNext(); + } + } + + if (!seenComma) { + ASTNode treePrev = from.getTreePrev(); + + while (treePrev != null && treePrev.getElementType() == TokenType.WHITE_SPACE) { + from = treePrev; + treePrev = treePrev.getTreePrev(); + } + if (treePrev != null && treePrev.getElementType() == JsonElementTypes.COMMA) { + from = treePrev; + } + } + + parent.removeRange(from, to); + } +} diff --git a/json/src/com/intellij/json/psi/JsonPsiUtil.java b/json/src/com/intellij/json/psi/JsonPsiUtil.java new file mode 100644 index 000000000000..a7aa5414b0e7 --- /dev/null +++ b/json/src/com/intellij/json/psi/JsonPsiUtil.java @@ -0,0 +1,49 @@ +package com.intellij.json.psi; + +import com.intellij.psi.PsiElement; +import org.jetbrains.annotations.NotNull; + +/** + * Various helper methods for working with PSI of JSON language. + * + * @author Mikhail Golubev + */ +@SuppressWarnings("UnusedDeclaration") +public class JsonPsiUtil { + private JsonPsiUtil() { + // empty + } + + + /** + * Checks that PSI element represents item of JSON array. + * + * @param element PSI element to check + * @return whether this PSI element is array element + */ + public static boolean isArrayElement(@NotNull PsiElement element) { + return element instanceof JsonValue && element.getParent() instanceof JsonArray; + } + + /** + * Checks that PSI element represents key of JSON property (key-value pair of JSON object) + * + * @param element PSI element to check + * @return whether this PSI element is property key + */ + public static boolean isPropertyKey(@NotNull PsiElement element) { + final PsiElement parent = element.getParent(); + return parent instanceof JsonProperty && element == ((JsonProperty)parent).getNameElement(); + } + + /** + * Checks that PSI element represents value of JSON property (key-value pair of JSON object) + * + * @param element PSI element to check + * @return whether this PSI element is property value + */ + public static boolean isPropertyValue(@NotNull PsiElement element) { + final PsiElement parent = element.getParent(); + return parent instanceof JsonProperty && element == ((JsonProperty)parent).getValue(); + } +} diff --git a/json/src/com/intellij/json/psi/JsonStringLiteralManipulator.java b/json/src/com/intellij/json/psi/JsonStringLiteralManipulator.java new file mode 100644 index 000000000000..fb3865b84f9b --- /dev/null +++ b/json/src/com/intellij/json/psi/JsonStringLiteralManipulator.java @@ -0,0 +1,32 @@ +package com.intellij.json.psi; + +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.AbstractElementManipulator; +import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.NotNull; + +public class JsonStringLiteralManipulator extends AbstractElementManipulator { + + @Override + public JsonStringLiteral handleContentChange(@NotNull JsonStringLiteral element, @NotNull TextRange range, String newContent) + throws IncorrectOperationException { + assert new TextRange(0, element.getTextLength()).contains(range); + + final String originalContent = element.getText(); + final TextRange withoutQuotes = getRangeInElement(element); + final JsonElementGenerator generator = new JsonElementGenerator(element.getProject()); + final String replacement = originalContent.substring(withoutQuotes.getStartOffset(), range.getStartOffset()) + + newContent + + originalContent.substring(range.getEndOffset(), withoutQuotes.getEndOffset()); + return generator.createStringLiteral(replacement); + } + + @NotNull + @Override + public TextRange getRangeInElement(@NotNull JsonStringLiteral element) { + final String content = element.getText(); + final int startOffset = content.startsWith("'") || content.startsWith("\"") ? 1 : 0; + final int endOffset = content.length() > 0 && (content.endsWith("'") || content.endsWith("\"")) ? -1 : 0; + return new TextRange(startOffset, content.length() + endOffset); + } +} diff --git a/json/src/com/intellij/json/psi/impl/JsonElementImpl.java b/json/src/com/intellij/json/psi/impl/JsonElementImpl.java new file mode 100644 index 000000000000..e65b4b6803dc --- /dev/null +++ b/json/src/com/intellij/json/psi/impl/JsonElementImpl.java @@ -0,0 +1,23 @@ +package com.intellij.json.psi.impl; + +import com.intellij.extapi.psi.ASTWrapperPsiElement; +import com.intellij.json.psi.JsonElement; +import com.intellij.lang.ASTNode; +import com.intellij.openapi.util.text.StringUtil; +import org.jetbrains.annotations.NotNull; + +/** + * @author Mikhail Golubev + */ +public class JsonElementImpl extends ASTWrapperPsiElement implements JsonElement { + + public JsonElementImpl(@NotNull ASTNode node) { + super(node); + } + + @Override + public String toString() { + final String className = getClass().getSimpleName(); + return StringUtil.trimEnd(className, "Impl"); + } +} diff --git a/json/src/com/intellij/json/psi/impl/JsonFileImpl.java b/json/src/com/intellij/json/psi/impl/JsonFileImpl.java new file mode 100644 index 000000000000..26e7f6399a6f --- /dev/null +++ b/json/src/com/intellij/json/psi/impl/JsonFileImpl.java @@ -0,0 +1,38 @@ +package com.intellij.json.psi.impl; + +import com.intellij.extapi.psi.PsiFileBase; +import com.intellij.json.JsonFileType; +import com.intellij.json.JsonLanguage; +import com.intellij.json.psi.JsonFile; +import com.intellij.json.psi.JsonValue; +import com.intellij.openapi.fileTypes.FileType; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.FileViewProvider; +import com.intellij.psi.util.PsiTreeUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class JsonFileImpl extends PsiFileBase implements JsonFile { + + public JsonFileImpl(FileViewProvider fileViewProvider) { + super(fileViewProvider, JsonLanguage.INSTANCE); + } + + @NotNull + @Override + public FileType getFileType() { + return JsonFileType.INSTANCE; + } + + @Nullable + @Override + public JsonValue getTopLevelValue() { + return PsiTreeUtil.getChildOfType(this, JsonValue.class); + } + + @Override + public String toString() { + final VirtualFile virtualFile = getVirtualFile(); + return String.format("JsonFile: " + (virtualFile != null? virtualFile.getName() : "")); + } +} diff --git a/json/src/com/intellij/json/psi/impl/JsonLiteralMixin.java b/json/src/com/intellij/json/psi/impl/JsonLiteralMixin.java new file mode 100644 index 000000000000..0338db3e539b --- /dev/null +++ b/json/src/com/intellij/json/psi/impl/JsonLiteralMixin.java @@ -0,0 +1,33 @@ +package com.intellij.json.psi.impl; + +import com.intellij.json.psi.JsonLiteral; +import com.intellij.lang.ASTNode; +import com.intellij.psi.PsiReference; +import com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistry; +import org.jetbrains.annotations.NotNull; + +abstract class JsonLiteralMixin extends JsonElementImpl implements JsonLiteral { + private final Object myRefLock = new Object(); + private volatile PsiReference[] myRefs; + private volatile long myModCount = -1; + + protected JsonLiteralMixin(ASTNode node) { + super(node); + } + + @NotNull + @Override + public PsiReference[] getReferences() { + final long count = getManager().getModificationTracker().getModificationCount(); + if (count != myModCount) { + synchronized (myRefLock) { + if (count != myModCount) { + myRefs = ReferenceProvidersRegistry.getReferencesFromProviders(this); + myModCount = count; + } + } + } + + return myRefs; + } +} diff --git a/json/src/com/intellij/json/psi/impl/JsonPropertyMixin.java b/json/src/com/intellij/json/psi/impl/JsonPropertyMixin.java new file mode 100644 index 000000000000..8f0d2db7495e --- /dev/null +++ b/json/src/com/intellij/json/psi/impl/JsonPropertyMixin.java @@ -0,0 +1,42 @@ +package com.intellij.json.psi.impl; + +import com.intellij.json.psi.JsonElementGenerator; +import com.intellij.json.psi.JsonProperty; +import com.intellij.lang.ASTNode; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiReference; +import com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistry; +import com.intellij.util.ArrayUtil; +import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; + +/** + * @author Mikhail Golubev + */ +abstract class JsonPropertyMixin extends JsonElementImpl implements JsonProperty { + public JsonPropertyMixin(@NotNull ASTNode node) { + super(node); + } + + @Override + public PsiElement setName(@NonNls @NotNull String name) throws IncorrectOperationException { + final JsonElementGenerator generator = new JsonElementGenerator(getProject()); + // Strip only both quotes in case user wants some exotic name like key' + getNameElement().replace(generator.createStringLiteral(StringUtil.unquoteString(name))); + return this; + } + + @Override + public PsiReference getReference() { + return new JsonPropertyNameReference(this); + } + + @NotNull + @Override + public PsiReference[] getReferences() { + final PsiReference[] fromProviders = ReferenceProvidersRegistry.getReferencesFromProviders(this); + return ArrayUtil.prepend(new JsonPropertyNameReference(this), fromProviders); + } +} diff --git a/json/src/com/intellij/json/psi/impl/JsonPropertyNameReference.java b/json/src/com/intellij/json/psi/impl/JsonPropertyNameReference.java new file mode 100644 index 000000000000..950db8e5afbd --- /dev/null +++ b/json/src/com/intellij/json/psi/impl/JsonPropertyNameReference.java @@ -0,0 +1,79 @@ +package com.intellij.json.psi.impl; + +import com.intellij.json.psi.JsonProperty; +import com.intellij.json.psi.JsonValue; +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.ElementManipulators; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiReference; +import com.intellij.util.ArrayUtil; +import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author Mikhail Golubev + */ +public class JsonPropertyNameReference implements PsiReference { + private final JsonProperty myProperty; + + public JsonPropertyNameReference(@NotNull JsonProperty property) { + myProperty = property; + } + + @Override + public PsiElement getElement() { + return myProperty; + } + + @Override + public TextRange getRangeInElement() { + final JsonValue nameElement = myProperty.getNameElement(); + // Either value of string with quotes stripped or element's text as is + return ElementManipulators.getValueTextRange(nameElement); + } + + @Nullable + @Override + public PsiElement resolve() { + return myProperty; + } + + @NotNull + @Override + public String getCanonicalText() { + return myProperty.getName(); + } + + @Override + public PsiElement handleElementRename(String newElementName) throws IncorrectOperationException { + return myProperty.setName(newElementName); + } + + @Override + public PsiElement bindToElement(@NotNull PsiElement element) throws IncorrectOperationException { + return null; + } + + @Override + public boolean isReferenceTo(PsiElement element) { + if (!(element instanceof JsonProperty)) { + return false; + } + // May reference to the property with the same name for compatibility with JavaScript JSON support + final JsonProperty otherProperty = (JsonProperty)element; + final PsiElement selfResolve = resolve(); + return otherProperty.getName().equals(getCanonicalText()) && selfResolve != otherProperty; + } + + @NotNull + @Override + public Object[] getVariants() { + return ArrayUtil.EMPTY_OBJECT_ARRAY; + } + + @Override + public boolean isSoft() { + return true; + } +} diff --git a/json/src/com/intellij/json/psi/impl/JsonPsiImplUtils.java b/json/src/com/intellij/json/psi/impl/JsonPsiImplUtils.java new file mode 100644 index 000000000000..2a7f2e67f589 --- /dev/null +++ b/json/src/com/intellij/json/psi/impl/JsonPsiImplUtils.java @@ -0,0 +1,58 @@ +package com.intellij.json.psi.impl; + +import com.intellij.json.JsonParserDefinition; +import com.intellij.json.psi.*; +import com.intellij.lang.ASTNode; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.PsiElement; +import com.intellij.psi.util.PsiTreeUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; + +public class JsonPsiImplUtils { + @NotNull + public static String getName(@NotNull JsonProperty property) { + return StringUtil.stripQuotesAroundValue(property.getNameElement().getText()); + } + + /** + * Actually only JSON string literal should be accepted as valid name of property according to standard, + * but for compatibility with JavaScript integration any JSON literals as well as identifiers (unquoted words) + * are possible and highlighted as error later. + * + * @see com.intellij.json.codeinsight.JsonStandardComplianceInspection + */ + @NotNull + public static JsonValue getNameElement(@NotNull JsonProperty property) { + final PsiElement firstChild = property.getFirstChild(); + assert firstChild instanceof JsonLiteral || firstChild instanceof JsonReferenceExpression; + return (JsonValue)firstChild; + } + + @Nullable + public static JsonValue getValue(@NotNull JsonProperty property) { + return PsiTreeUtil.getNextSiblingOfType(getNameElement(property), JsonValue.class); + } + + public static boolean isQuotedString(@NotNull JsonLiteral literal) { + return literal.getNode().findChildByType(JsonParserDefinition.STRING_LITERALS) != null; + } + + public static void delete(@NotNull JsonProperty property) { + final ASTNode myNode = property.getNode(); + JsonPsiChangeUtils.removeCommaSeparatedFromList(myNode, myNode.getTreeParent()); + } + + @Nullable + public static JsonProperty findProperty(@NotNull JsonObject object, @NotNull String name) { + final Collection properties = PsiTreeUtil.findChildrenOfType(object, JsonProperty.class); + for (JsonProperty property : properties) { + if (property.getName().equals(name)) { + return property; + } + } + return null; + } +} diff --git a/json/src/com/intellij/json/psi/impl/JsonRecursiveElementVisitor.java b/json/src/com/intellij/json/psi/impl/JsonRecursiveElementVisitor.java new file mode 100644 index 000000000000..902b4a01a3d3 --- /dev/null +++ b/json/src/com/intellij/json/psi/impl/JsonRecursiveElementVisitor.java @@ -0,0 +1,14 @@ +package com.intellij.json.psi.impl; + +import com.intellij.json.psi.JsonElementVisitor; +import com.intellij.psi.PsiElement; + +/** + * @author Mikhail Golubev + */ +public class JsonRecursiveElementVisitor extends JsonElementVisitor { + + public void visitElement(final PsiElement element) { + element.acceptChildren(this); + } +} diff --git a/json/src/com/intellij/json/structureView/JsonStructureViewBuilderFactory.java b/json/src/com/intellij/json/structureView/JsonStructureViewBuilderFactory.java new file mode 100644 index 000000000000..28387d840aa6 --- /dev/null +++ b/json/src/com/intellij/json/structureView/JsonStructureViewBuilderFactory.java @@ -0,0 +1,27 @@ +package com.intellij.json.structureView; + +import com.intellij.ide.structureView.StructureViewBuilder; +import com.intellij.ide.structureView.StructureViewModel; +import com.intellij.ide.structureView.TreeBasedStructureViewBuilder; +import com.intellij.lang.PsiStructureViewFactory; +import com.intellij.openapi.editor.Editor; +import com.intellij.psi.PsiFile; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author Mikhail Golubev + */ +public class JsonStructureViewBuilderFactory implements PsiStructureViewFactory { + @Nullable + @Override + public StructureViewBuilder getStructureViewBuilder(final PsiFile psiFile) { + return new TreeBasedStructureViewBuilder() { + @NotNull + @Override + public StructureViewModel createStructureViewModel(@Nullable Editor editor) { + return new JsonStructureViewModel(psiFile, editor); + } + }; + } +} diff --git a/json/src/com/intellij/json/structureView/JsonStructureViewElement.java b/json/src/com/intellij/json/structureView/JsonStructureViewElement.java new file mode 100644 index 000000000000..4717694fd945 --- /dev/null +++ b/json/src/com/intellij/json/structureView/JsonStructureViewElement.java @@ -0,0 +1,101 @@ +package com.intellij.json.structureView; + +import com.intellij.ide.structureView.StructureViewTreeElement; +import com.intellij.ide.util.treeView.smartTree.TreeElement; +import com.intellij.json.psi.JsonElement; +import com.intellij.json.psi.JsonFile; +import com.intellij.json.psi.JsonObject; +import com.intellij.json.psi.JsonProperty; +import com.intellij.navigation.ItemPresentation; +import com.intellij.util.Function; +import com.intellij.util.PlatformIcons; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; + +/** + * @author Mikhail Golubev + */ +public class JsonStructureViewElement implements StructureViewTreeElement { + private final JsonElement myElement; + + public JsonStructureViewElement(@NotNull JsonElement element) { + assert element instanceof JsonFile || element instanceof JsonProperty; + myElement = element; + } + + @Override + public JsonElement getValue() { + return myElement; + } + + @Override + public void navigate(boolean requestFocus) { + myElement.navigate(requestFocus); + } + + @Override + public boolean canNavigate() { + return myElement.canNavigate(); + } + + @Override + public boolean canNavigateToSource() { + return myElement.canNavigateToSource(); + } + + @NotNull + @Override + public ItemPresentation getPresentation() { + if (myElement instanceof JsonProperty) { + return new ItemPresentation() { + @Nullable + @Override + public String getPresentableText() { + return ((JsonProperty)myElement).getName(); + } + + @Nullable + @Override + public String getLocationString() { + return null; + } + + @Nullable + @Override + public Icon getIcon(boolean unused) { + return PlatformIcons.PROPERTY_ICON; + } + }; + } + else if (myElement instanceof JsonFile) { + //noinspection ConstantConditions + return myElement.getPresentation(); + } + throw new AssertionError("Attempting to create presentation for illegal element: " + myElement); + } + + @NotNull + @Override + public TreeElement[] getChildren() { + JsonElement value = null; + if (myElement instanceof JsonFile) { + value = ((JsonFile)myElement).getTopLevelValue(); + } + else if (myElement instanceof JsonProperty) { + value = ((JsonProperty)myElement).getValue(); + } + if (value instanceof JsonObject) { + final JsonObject object = ((JsonObject)value); + return ContainerUtil.map2Array(object.getPropertyList(), TreeElement.class, new Function() { + @Override + public TreeElement fun(JsonProperty property) { + return new JsonStructureViewElement(property); + } + }); + } + return EMPTY_ARRAY; + } +} diff --git a/json/src/com/intellij/json/structureView/JsonStructureViewModel.java b/json/src/com/intellij/json/structureView/JsonStructureViewModel.java new file mode 100644 index 000000000000..dee1afb45960 --- /dev/null +++ b/json/src/com/intellij/json/structureView/JsonStructureViewModel.java @@ -0,0 +1,45 @@ +package com.intellij.json.structureView; + +import com.intellij.ide.structureView.StructureViewModel; +import com.intellij.ide.structureView.StructureViewModelBase; +import com.intellij.ide.structureView.StructureViewTreeElement; +import com.intellij.ide.util.treeView.smartTree.Sorter; +import com.intellij.json.psi.JsonFile; +import com.intellij.json.psi.JsonProperty; +import com.intellij.openapi.editor.Editor; +import com.intellij.psi.PsiFile; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author Mikhail Golubev + */ +public class JsonStructureViewModel extends StructureViewModelBase implements StructureViewModel.ElementInfoProvider, StructureViewModel.ExpandInfoProvider{ + + public JsonStructureViewModel(@NotNull PsiFile psiFile, @Nullable Editor editor) { + super(psiFile, editor, new JsonStructureViewElement((JsonFile)psiFile)); + withSorters(Sorter.ALPHA_SORTER); + withSuitableClasses(JsonFile.class, JsonProperty.class); + } + + @Override + public boolean isAlwaysShowsPlus(StructureViewTreeElement element) { + return false; + } + + @Override + public boolean isAlwaysLeaf(StructureViewTreeElement element) { + return false; + } + + @Override + public boolean isAutoExpand(@NotNull StructureViewTreeElement element) { + final Object value = element.getValue(); + return value instanceof PsiFile || value instanceof JsonProperty; + } + + @Override + public boolean isSmartExpand() { + return false; + } +} diff --git a/json/src/com/intellij/json/surroundWith/JsonSurroundDescriptor.java b/json/src/com/intellij/json/surroundWith/JsonSurroundDescriptor.java new file mode 100644 index 000000000000..803b85ac9d48 --- /dev/null +++ b/json/src/com/intellij/json/surroundWith/JsonSurroundDescriptor.java @@ -0,0 +1,78 @@ +package com.intellij.json.surroundWith; + +import com.intellij.json.JsonElementTypes; +import com.intellij.json.psi.JsonElement; +import com.intellij.json.psi.JsonProperty; +import com.intellij.json.psi.JsonValue; +import com.intellij.lang.surroundWith.SurroundDescriptor; +import com.intellij.lang.surroundWith.Surrounder; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiWhiteSpace; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +/** + * @author Mikhail Golubev + */ +public class JsonSurroundDescriptor implements SurroundDescriptor { + private static final Surrounder[] ourSurrounders = new Surrounder[]{ + new JsonWithObjectLiteralSurrounder() + }; + + @NotNull + @Override + public PsiElement[] getElementsToSurround(PsiFile file, int startOffset, int endOffset) { + PsiElement firstElement = file.findElementAt(startOffset); + PsiElement lastElement = file.findElementAt(endOffset - 1); + + // Extend selection beyond possible delimiters + while (firstElement != null && + (firstElement instanceof PsiWhiteSpace || firstElement.getNode().getElementType() == JsonElementTypes.COMMA)) { + firstElement = firstElement.getNextSibling(); + } + while (lastElement != null && + (lastElement instanceof PsiWhiteSpace || lastElement.getNode().getElementType() == JsonElementTypes.COMMA)) { + lastElement = lastElement.getPrevSibling(); + } + if (firstElement != null) { + startOffset = firstElement.getTextRange().getStartOffset(); + } + if (lastElement != null) { + endOffset = lastElement.getTextRange().getEndOffset(); + } + + final JsonElement property = PsiTreeUtil.findElementOfClassAtRange(file, startOffset, endOffset, JsonProperty.class); + if (property != null) { + final List properties = ContainerUtil.newArrayList(property); + PsiElement nextSibling = property.getNextSibling(); + while (nextSibling != null && nextSibling.getTextRange().getEndOffset() <= endOffset) { + if (nextSibling instanceof JsonProperty) { + properties.add((JsonProperty)nextSibling); + } + nextSibling = nextSibling.getNextSibling(); + } + return properties.toArray(new PsiElement[properties.size()]); + } + + final JsonValue value = PsiTreeUtil.findElementOfClassAtRange(file, startOffset, endOffset, JsonValue.class); + if (value != null) { + return new PsiElement[]{value}; + } + return PsiElement.EMPTY_ARRAY; + } + + @NotNull + @Override + public Surrounder[] getSurrounders() { + return ourSurrounders; + } + + @Override + public boolean isExclusive() { + return false; + } +} diff --git a/json/src/com/intellij/json/surroundWith/JsonWithObjectLiteralSurrounder.java b/json/src/com/intellij/json/surroundWith/JsonWithObjectLiteralSurrounder.java new file mode 100644 index 000000000000..9f0a804c9c04 --- /dev/null +++ b/json/src/com/intellij/json/surroundWith/JsonWithObjectLiteralSurrounder.java @@ -0,0 +1,87 @@ +package com.intellij.json.surroundWith; + +import com.intellij.json.JsonBundle; +import com.intellij.json.psi.*; +import com.intellij.lang.surroundWith.Surrounder; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.PsiElement; +import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * This surrounder ported from JavaScript allows to wrap single JSON value or several consecutive JSON properties + * in object literal. + *

+ * Examples: + *

    + *
  1. {@code [42]} converts to [{"property": 42}]
  2. + *
  3. + * {
    + *    "foo": 42,
    + *    "bar": false
    + * }
    + * 
    converts to
    + * {
    + *    "property": {
    + *      "foo": 42,
    + *      "bar": false
    + *    }
    + * }
    + * 
  4. + *
+ * + * @author Mikhail Golubev + */ +public class JsonWithObjectLiteralSurrounder implements Surrounder { + @Override + public String getTemplateDescription() { + return JsonBundle.message("name.surround.with.object.literal"); + } + + @Override + public boolean isApplicable(@NotNull PsiElement[] elements) { + return !JsonPsiUtil.isPropertyKey(elements[0]); + } + + @Nullable + @Override + public TextRange surroundElements(@NotNull Project project, + @NotNull Editor editor, + @NotNull PsiElement[] elements) throws IncorrectOperationException { + + if (!isApplicable(elements)) { + return null; + } + + final JsonElementGenerator generator = new JsonElementGenerator(project); + + final PsiElement firstElement = elements[0]; + final JsonElement newNameElement; + if (firstElement instanceof JsonValue) { + assert elements.length == 1 : "Only single JSON value can be wrapped in object literal"; + JsonObject replacement = generator.createValue("{\n\"property\": " + firstElement.getText() + "\n}"); + replacement = (JsonObject)firstElement.replace(replacement); + newNameElement = replacement.getPropertyList().get(0).getNameElement(); + } + else { + assert firstElement instanceof JsonProperty; + final JsonProperty firstProperty = (JsonProperty)elements[0]; + final JsonProperty lastProperty = (JsonProperty)elements[elements.length - 1]; + final TextRange replacedRange = new TextRange(firstProperty.getTextOffset(), lastProperty.getTextRange().getEndOffset()); + final String propertiesText = replacedRange.substring(firstProperty.getContainingFile().getText()); + if (firstProperty != lastProperty) { + final PsiElement parent = firstProperty.getParent(); + parent.deleteChildRange(firstProperty.getNextSibling(), lastProperty); + } + final JsonObject tempJsonObject = generator.createValue("{\"property\": {\n" + propertiesText + "\n}}"); + JsonProperty replacement = tempJsonObject.getPropertyList().get(0); + replacement = (JsonProperty)firstProperty.replace(replacement); + newNameElement = replacement.getNameElement(); + } + final TextRange rangeWithQuotes = newNameElement.getTextRange(); + return new TextRange(rangeWithQuotes.getStartOffset() + 1, rangeWithQuotes.getEndOffset() - 1); + } +} diff --git a/json/src/json.bnf b/json/src/json.bnf new file mode 100644 index 000000000000..1f36efc907c2 --- /dev/null +++ b/json/src/json.bnf @@ -0,0 +1,112 @@ +{ + parserClass = 'com.intellij.json.JsonParser' + parserUtilClass = "com.intellij.json.psi.JsonParserUtil" + psiPackage = 'com.intellij.json.psi' + psiImplPackage = 'com.intellij.json.psi.impl' + + elementTypeHolderClass = 'com.intellij.json.JsonElementTypes' + elementTypeClass = 'com.intellij.json.JsonElementType' + psiClassPrefix = "Json" + psiVisitorName = "JsonElementVisitor" + + psiImplUtilClass = 'com.intellij.json.psi.impl.JsonPsiImplUtils' + tokenTypeClass = 'com.intellij.json.JsonTokenType' + + implements = "com.intellij.json.psi.JsonElement" + extends = "com.intellij.json.psi.impl.JsonElementImpl" + + tokens = [ + L_CURLY='{' + R_CURLY='}' + L_BRACKET='[' + R_BRACKET=']' + + COMMA=',' + COLON=':' + LINE_COMMENT='regexp://.*' + // "/*" ([^*]|\*+[^*/])* (\*+"/")? + BLOCK_COMMENT='regexp:/\*([^*]|\*+[^*/])*(\*+/)?' + // else /\*(?:[^*]|\*[^/])*\*+/ + + // unclosed string literal matches till the line's end + // any escape sequences included, illegal escapes are indicated by SyntaxHighlighter + // and JsonStringLiteralAnnotator + DOUBLE_QUOTED_STRING="regexp:\"([^\\\"\r\n]|\\[^\r\n])*\"?" + SINGLE_QUOTED_STRING="regexp:'([^\\\'\r\n]|\\[^\r\n])*'?" +// STRING='regexp:"([^\\"\r\n]|\\([\\"/bfnrt]|u[a-fA-F0-9]{4}))*"?' + + NUMBER='regexp:-?\d+(\.\d+([eE][+-]?\d+)?)?' + TRUE='true' + FALSE='false' + NULL='null' + // Actually not defined in RFC 4627, but may be used for JSON5 and helps with + // auto completion of keywords. + INDENTIFIER='regexp:[:jletter:] [:jletterdigit:]*' + ] + + extends("container|literal|reference_expression")=value + extends("array|object")=container + extends("string_literal|number_literal|boolean_literal|null_literal")=literal + implements("property")=[ + "com.intellij.json.psi.JsonElement" + "com.intellij.psi.PsiNamedElement" + ] +} + +// For compatibility we allow any value at root level (see JsonStandardComplianceAnnotator) +json ::= value + +object ::= '{' object_element* '}' { + pin=1 + methods=[ + findProperty + ] +} + +// Hackity-hack to parse array elements and properties even if separating commas are missing, +// TODO: Find out if there is any simpler way to do so in GrammarKit +private object_element ::= property (','|&'}') { + recoverWhile = not_brace_or_next_value + pin = 1 +} + +property ::= property_name (':' value) { + methods=[ + getName + getNameElement + getValue + // suppress getValueList() accessor + value="" + ] + mixin="com.intellij.json.psi.impl.JsonPropertyMixin" + pin(".*")=1 +} + +private property_name ::= literal | reference_expression + +array ::= '[' array_element* ']' { pin=1 } + +private array_element ::= value (','|&']') { + recoverWhile = not_bracket_or_next_value + pin=1 +} + +string_literal ::= SINGLE_QUOTED_STRING | DOUBLE_QUOTED_STRING +number_literal ::= NUMBER +boolean_literal ::= TRUE | FALSE +null_literal ::= NULL + +literal ::= string_literal | number_literal | boolean_literal | null_literal { + methods=[isQuotedString] + mixin="com.intellij.json.psi.impl.JsonLiteralMixin" +} + +fake container ::= object | literal + +reference_expression ::= INDENTIFIER + +value ::= object | array | literal | reference_expression + +// Recoveries +private not_bracket_or_next_value ::= !(']'|value) +private not_brace_or_next_value ::= !('}'|value) \ No newline at end of file diff --git a/json/tests/json-tests.iml b/json/tests/json-tests.iml new file mode 100644 index 000000000000..3edce1f127c9 --- /dev/null +++ b/json/tests/json-tests.iml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/json/tests/test/com/intellij/json/JsonCommenterTest.java b/json/tests/test/com/intellij/json/JsonCommenterTest.java new file mode 100644 index 000000000000..86edf968f61a --- /dev/null +++ b/json/tests/test/com/intellij/json/JsonCommenterTest.java @@ -0,0 +1,24 @@ +package com.intellij.json; + +import com.intellij.openapi.actionSystem.IdeActions; +import org.jetbrains.annotations.NotNull; + +/** + * @author Mikhail Golubev + */ +public class JsonCommenterTest extends JsonTestCase { + + private void doTest(@NotNull String actionId) { + myFixture.configureByFile("commenter/" + getTestName(false) + ".json"); + myFixture.performEditorAction(actionId); + myFixture.checkResultByFile("commenter/" + getTestName(false) + "_after.json", true); + } + + public void testLineComment() { + doTest(IdeActions.ACTION_COMMENT_LINE); + } + + public void testBlockComment() { + doTest(IdeActions.ACTION_COMMENT_BLOCK); + } +} diff --git a/json/tests/test/com/intellij/json/JsonCompletionTest.java b/json/tests/test/com/intellij/json/JsonCompletionTest.java new file mode 100644 index 000000000000..a0fa049d1a6c --- /dev/null +++ b/json/tests/test/com/intellij/json/JsonCompletionTest.java @@ -0,0 +1,53 @@ +package com.intellij.json; + +import com.intellij.codeInsight.lookup.LookupElement; +import com.intellij.util.ArrayUtil; + +/** + * @author Mikhail Golubev + */ +public class JsonCompletionTest extends JsonTestCase { + private static final String[] ALL_KEYWORDS = new String[]{"true", "false", "null"}; + private static final String[] NOTHING = ArrayUtil.EMPTY_STRING_ARRAY; + + private void doTest(String... variants) { + myFixture.testCompletionVariants("completion/" + getTestName(false) + ".json", variants); + } + + private void doTestSingleVariant() { + myFixture.configureByFile("completion/" + getTestName(false) + ".json"); + final LookupElement[] variants = myFixture.completeBasic(); + assertNull(variants); + myFixture.checkResultByFile("completion/" + getTestName(false) + "_after.json" ); + } + + public void testInsideArrayElement1() throws Exception { + doTest(ALL_KEYWORDS); + } + + public void testInsideArrayElement2() throws Exception { + doTest(ALL_KEYWORDS); + } + + public void testInsidePropertyKey1() throws Exception { + doTest(NOTHING); + } + + public void testInsidePropertyKey2() throws Exception { + doTest(NOTHING); + } + + public void testInsidePropertyValue() throws Exception { + doTest(ALL_KEYWORDS); + } + + // Moved from JavaScript + + public void testKeywords() { + doTestSingleVariant(); + } + + public void testKeywords_2() { + doTestSingleVariant(); + } +} diff --git a/json/tests/test/com/intellij/json/JsonFoldingTest.java b/json/tests/test/com/intellij/json/JsonFoldingTest.java new file mode 100644 index 000000000000..702ea2af5498 --- /dev/null +++ b/json/tests/test/com/intellij/json/JsonFoldingTest.java @@ -0,0 +1,34 @@ +package com.intellij.json; + +/** + * @author Mikhail Golubev + */ +public class JsonFoldingTest extends JsonTestCase { + private void doTest() { + myFixture.testFolding(getTestDataPath() + "/folding/" + getTestName(false) + ".json"); + } + + + public void testArrayFolding() { + doTest(); + } + + public void testObjectFolding() { + doTest(); + } + + // Moved from JavaScript + + public void testObjectLiteral2() { + doTest(); + } + + public void testObjectLiteral3() { + doTest(); + } + + public void testObjectLiteral4() { + doTest(); + } + +} diff --git a/json/tests/test/com/intellij/json/JsonFormattingTest.java b/json/tests/test/com/intellij/json/JsonFormattingTest.java new file mode 100644 index 000000000000..289f3f464af4 --- /dev/null +++ b/json/tests/test/com/intellij/json/JsonFormattingTest.java @@ -0,0 +1,109 @@ +package com.intellij.json; + +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.psi.codeStyle.CodeStyleSettings; +import com.intellij.psi.codeStyle.CodeStyleSettingsManager; +import com.intellij.psi.codeStyle.CommonCodeStyleSettings; +import com.intellij.psi.formatter.FormatterTestCase; +import com.intellij.testFramework.IdeaTestCase; +import com.intellij.testFramework.PlatformTestUtil; +import com.intellij.testFramework.TestLoggerFactory; + +/** + * @author Mikhail Golubev + */ +public class JsonFormattingTest extends FormatterTestCase { + static { + Logger.setFactory(TestLoggerFactory.class); + } + + @Override + protected void setUp() throws Exception { + IdeaTestCase.initPlatformPrefix(); + super.setUp(); + } + + @Override + protected String getTestName(boolean ignored) { + // always use uppercase first letter for consistency + return super.getTestName(false); + } + + @Override + protected String getBasePath() { + return "formatting"; + } + + @Override + protected String getTestDataPath() { + return PlatformTestUtil.getCommunityPath() + "/json/tests/testData/"; + } + + @Override + protected String getFileExtension() { + return "json"; + } + + public void testContainerElementsAlignment() throws Exception { + doTest(); + } + + public void testBlankLinesStripping() throws Exception { + doTest(); + } + + public void testSpacesInsertion() throws Exception { + doTest(); + } + + public void testWrapping() throws Exception { + final CodeStyleSettings settings = getSettings(); + settings.setRightMargin(JsonLanguage.INSTANCE, 20); + doTest(); + } + + // Moved from JavaScript + + public void testWeb3830() throws Exception { + final CodeStyleSettings settings = CodeStyleSettingsManager.getInstance(getProject()).getCurrentSettings(); + final CommonCodeStyleSettings.IndentOptions indentOptions = settings.getCommonSettings(JsonLanguage.INSTANCE).getIndentOptions(); + final int indent = indentOptions.INDENT_SIZE; + final boolean useTabs = indentOptions.USE_TAB_CHARACTER; + final int tabSize = indentOptions.TAB_SIZE; + try { + indentOptions.INDENT_SIZE = 8; + indentOptions.USE_TAB_CHARACTER = true; + indentOptions.TAB_SIZE = 8; + doTest(); + } + finally { + indentOptions.INDENT_SIZE = indent; + indentOptions.USE_TAB_CHARACTER = useTabs; + indentOptions.TAB_SIZE = tabSize; + } + } + + public void testReformatJSon() throws Exception { + final CommonCodeStyleSettings.IndentOptions indentOptions = getSettings().getCommonSettings(JsonLanguage.INSTANCE).getIndentOptions(); + final int oldIndentSize = indentOptions.INDENT_SIZE; + try { + indentOptions.INDENT_SIZE = 4; + doTest(); + } + finally { + indentOptions.INDENT_SIZE = oldIndentSize; + } + } + + public void testReformatJSon2() throws Exception { + final CommonCodeStyleSettings.IndentOptions indentOptions = getSettings().getCommonSettings(JsonLanguage.INSTANCE).getIndentOptions(); + final int oldIndentSize = indentOptions.INDENT_SIZE; + try { + indentOptions.INDENT_SIZE = 4; + doTest(); + } + finally { + indentOptions.INDENT_SIZE = oldIndentSize; + } + } +} diff --git a/json/tests/test/com/intellij/json/JsonHighlightingTest.java b/json/tests/test/com/intellij/json/JsonHighlightingTest.java new file mode 100644 index 000000000000..8114d83f4db6 --- /dev/null +++ b/json/tests/test/com/intellij/json/JsonHighlightingTest.java @@ -0,0 +1,57 @@ +package com.intellij.json; + +import com.intellij.json.codeinsight.JsonStandardComplianceInspection; + +/** + * @author Mikhail Golubev + */ +public class JsonHighlightingTest extends JsonTestCase { + + private void doTest() { + doTestHighlighting(true, true, true); + } + + private long doTestHighlighting(boolean checkInfo, boolean checkWeakWarning, boolean checkWarning) { + return myFixture.testHighlighting(checkWarning, checkInfo, checkWeakWarning, "/highlighting/" + getTestName(false) + ".json"); + } + + private void enableStandardComplianceInspection(boolean checkComments) { + final JsonStandardComplianceInspection inspection = new JsonStandardComplianceInspection(); + inspection.myWarnAboutComments = checkComments; + myFixture.enableInspections(inspection); + } + + public void testStringLiterals() { + doTest(); + } + + public void testComplianceProblemsTopLevelValue() { + enableStandardComplianceInspection(true); + doTest(); + } + + public void testComplianceProblems() { + enableStandardComplianceInspection(true); + doTest(); + } + + // Moved from JavaScript + + public void testJSON_with_comment() throws Exception { + enableStandardComplianceInspection(false); + doTest(); + } + + public void testJSON() throws Exception { + enableStandardComplianceInspection(true); + doTest(); + } + + // TODO: JSON semantic highlighting? + /* + public void testSemanticHighlighting() throws Exception { + doTest(); + } + */ + +} diff --git a/json/tests/test/com/intellij/json/JsonLineMoverTest.java b/json/tests/test/com/intellij/json/JsonLineMoverTest.java new file mode 100644 index 000000000000..2babea91e3bb --- /dev/null +++ b/json/tests/test/com/intellij/json/JsonLineMoverTest.java @@ -0,0 +1,49 @@ +package com.intellij.json; + +import com.intellij.openapi.actionSystem.IdeActions; +import com.intellij.openapi.fileEditor.FileDocumentManager; + +/** + * @author Mikhail Golubev + */ +public class JsonLineMoverTest extends JsonTestCase { + private void doTest(boolean down) { + final String testName = getTestName(false); + + if (down) { + myFixture.configureByFile("mover/" + testName + ".json"); + myFixture.performEditorAction(IdeActions.ACTION_MOVE_STATEMENT_DOWN_ACTION); + myFixture.checkResultByFile("mover/" + testName + "_afterDown.json", true); + } + else { + myFixture.configureByFile("mover/" + testName + ".json"); + myFixture.performEditorAction(IdeActions.ACTION_MOVE_STATEMENT_UP_ACTION); + myFixture.checkResultByFile("mover/" + testName + "_afterUp.json", true); + } + } + + public void testLastArrayElementMovedUp() { + doTest(false); + } + + public void testLastObjectPropertyMovedUp() { + doTest(false); + } + + public void testArraySelectionMovedDown() { + doTest(true); + } + + public void testObjectSelectionMovedDown() { + doTest(true); + } + + + // Moved from JavaScript + + public void testWeb_10585() { + doTest(false); + FileDocumentManager.getInstance().reloadFromDisk(myFixture.getDocument(myFixture.getFile())); + doTest(true); + } +} diff --git a/json/tests/test/com/intellij/json/JsonParsingTest.java b/json/tests/test/com/intellij/json/JsonParsingTest.java new file mode 100644 index 000000000000..ee07a9437a4a --- /dev/null +++ b/json/tests/test/com/intellij/json/JsonParsingTest.java @@ -0,0 +1,96 @@ +package com.intellij.json; + +import com.intellij.testFramework.ParsingTestCase; +import com.intellij.testFramework.PlatformTestUtil; +import com.intellij.testFramework.TestDataPath; + +/** + * @author Mikhail Golubev + */ +@TestDataPath("$CONTENT_ROOT/testData/psi/") +public class JsonParsingTest extends ParsingTestCase { + public JsonParsingTest() { + super("psi", "json", new JsonParserDefinition()); + } + + @Override + protected String getTestDataPath() { + return PlatformTestUtil.getCommunityPath() + "/json/tests/testData"; + } + + private void doTest() { + doTest(true); + } + + public void testKeywords() throws Exception { + doTest(); + } + + public void testNestedArrayLiterals() { + doTest(); + } + + public void testNestedObjectLiterals() { + doTest(); + } + + public void testTopLevelStringLiteral() { + doTest(); + } + + public void testStringLiterals() { + doTest(); + } + + public void testComments() { + doTest(); + } + + public void testIncompleteObjectProperties() { + doTest(); + } + + public void testMissingCommaBetweenArrayElements() { + doTest(); + } + + public void testMissingCommaBetweenObjectProperties() { + doTest(); + } + + public void testNonStandardPropertyKeys() { + doTest(); + } + + public void testTrailingCommas() { + doTest(); + } + + // Moved from JavaScript + + public void testSimple1() { + doTest(); + } + + public void testSimple2() { + doTest(); + } + + public void testSimple4() { + doTest(); + } + + // TODO: ask about these tests + //public void testSimple3() { + // doTest(); + //} + // + //public void testReal1() { + // doTest(); + //} + // + //public void testReal2() { + // doTest(); + + //} +} diff --git a/json/tests/test/com/intellij/json/JsonQuickFixTest.java b/json/tests/test/com/intellij/json/JsonQuickFixTest.java new file mode 100644 index 000000000000..8a80967b4da7 --- /dev/null +++ b/json/tests/test/com/intellij/json/JsonQuickFixTest.java @@ -0,0 +1,52 @@ +package com.intellij.json; + +import com.intellij.codeInsight.intention.IntentionAction; +import com.intellij.codeInspection.LocalInspectionTool; +import com.intellij.json.codeinsight.JsonStandardComplianceInspection; +import org.jetbrains.annotations.NotNull; + +/** + * @author Mikhail Golubev + */ +public class JsonQuickFixTest extends JsonTestCase { + + protected void doTest(@NotNull Class inspectionClass, @NotNull String hint) { + final String testFileName = "quickfix/" + getTestName(false); + myFixture.enableInspections(inspectionClass); + myFixture.configureByFile(testFileName + ".json"); + myFixture.checkHighlighting(true, false, false); + final IntentionAction intentionAction = myFixture.getAvailableIntention(hint); + assertNotNull(intentionAction); + myFixture.launchAction(intentionAction); + myFixture.checkResultByFile(testFileName + "_after.json", true); + } + + public void testWrapInDoubleQuotes() { + checkWrapInDoubleQuotes("{null: false}", "{\"null\": false}"); + checkWrapInDoubleQuotes("{true: false}", "{\"true\": false}"); + checkWrapInDoubleQuotes("{42: false}", "{\"42\": false}"); + checkWrapInDoubleQuotes("{foo: false}", "{\"foo\": false}"); + checkWrapInDoubleQuotes("{'foo': false}", "{\"foo\": false}"); + checkWrapInDoubleQuotes("{\"foo\": bar}", "{\"foo\": \"bar\"}"); + checkWrapInDoubleQuotes("{\"foo\": 'bar'}", "{\"foo\": \"bar\"}"); + } + + private void checkWrapInDoubleQuotes(@NotNull String before, @NotNull String after) { + myFixture.configureByText(JsonFileType.INSTANCE, before); + myFixture.enableInspections(JsonStandardComplianceInspection.class); + final IntentionAction intentionAction = myFixture.getAvailableIntention(JsonBundle.message("name.add.double.quotes.quickfix")); + assertNotNull(intentionAction); + myFixture.launchAction(intentionAction); + myFixture.checkResult(after); + } + + // Moved from JavaScript + + public void testJSON2() { + doTest(JsonStandardComplianceInspection.class, JsonBundle.message("name.add.double.quotes.quickfix")); + } + + public void testJSON3() { + doTest(JsonStandardComplianceInspection.class, JsonBundle.message("name.add.double.quotes.quickfix")); + } +} diff --git a/json/tests/test/com/intellij/json/JsonRenameTest.java b/json/tests/test/com/intellij/json/JsonRenameTest.java new file mode 100644 index 000000000000..76b5eea0043f --- /dev/null +++ b/json/tests/test/com/intellij/json/JsonRenameTest.java @@ -0,0 +1,28 @@ +package com.intellij.json; + +/** + * @author Mikhail Golubev + */ +public class JsonRenameTest extends JsonTestCase { + + private void doTest(final String newName) { + myFixture.configureByFile("rename/" + getTestName(false) + ".json"); + myFixture.renameElementAtCaret(newName); + myFixture.checkResultByFile("rename/" + getTestName(false) + "_after.json"); + } + + public void testPropertyNameRequiresEscaping() { + doTest("\"/\\\b\f\n\r\t\0\u001B'"); + } + + // Moved from JavaScript + + public void testDuplicateProperties() { + doTest("aaa2"); + } + + public void testDuplicatePropertiesQuotedName() { + doTest("\"aaa2\""); + } + +} diff --git a/json/tests/test/com/intellij/json/JsonSmartEnterTest.java b/json/tests/test/com/intellij/json/JsonSmartEnterTest.java new file mode 100644 index 000000000000..fd6d35b6b78b --- /dev/null +++ b/json/tests/test/com/intellij/json/JsonSmartEnterTest.java @@ -0,0 +1,46 @@ +package com.intellij.json; + +import com.intellij.codeInsight.editorActions.smartEnter.SmartEnterProcessor; +import com.intellij.codeInsight.editorActions.smartEnter.SmartEnterProcessors; +import com.intellij.openapi.application.Result; +import com.intellij.openapi.command.WriteCommandAction; +import com.intellij.openapi.editor.Editor; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +/** + * @author Mikhail Golubev + */ +public class JsonSmartEnterTest extends JsonTestCase { + public void doTest() { + myFixture.configureByFile("smartEnter/" + getTestName(false) + ".json"); + final List processors = SmartEnterProcessors.INSTANCE.forKey(JsonLanguage.INSTANCE); + new WriteCommandAction(myFixture.getProject()) { + @Override + protected void run(@NotNull Result result) throws Throwable { + final Editor editor = myFixture.getEditor(); + for (SmartEnterProcessor processor : processors) { + processor.process(myFixture.getProject(), editor, myFixture.getFile()); + } + } + }.execute(); + myFixture.checkResultByFile("smartEnter/" + getTestName(false) + "_after.json", true); + } + + public void testCommaInsertedAfterArrayElement() { + doTest(); + } + + public void testCommaInsertedAfterProperty() { + doTest(); + } + + public void testCommaInsertedAfterPropertyWithMultilineValue() { + doTest(); + } + + public void testColonInsertedAfterPropertyKey() { + doTest(); + } +} diff --git a/json/tests/test/com/intellij/json/JsonStructureViewTest.java b/json/tests/test/com/intellij/json/JsonStructureViewTest.java new file mode 100644 index 000000000000..34d92bfade8a --- /dev/null +++ b/json/tests/test/com/intellij/json/JsonStructureViewTest.java @@ -0,0 +1,69 @@ +package com.intellij.json; + +import com.intellij.ide.structureView.StructureViewBuilder; +import com.intellij.ide.structureView.StructureViewModel; +import com.intellij.ide.structureView.newStructureView.StructureViewComponent; +import com.intellij.ide.util.treeView.smartTree.TreeElement; +import com.intellij.lang.LanguageStructureViewBuilder; +import com.intellij.openapi.fileEditor.FileEditor; +import com.intellij.openapi.fileEditor.FileEditorManager; +import com.intellij.openapi.util.Disposer; +import com.intellij.util.Consumer; +import com.intellij.util.PlatformIcons; + +import static com.intellij.testFramework.PlatformTestUtil.assertTreeEqual; + +/** + * @author Mikhail Golubev + */ +public class JsonStructureViewTest extends JsonTestCase { + + private void doTest(final String expected) { + myFixture.configureByFile("structureView/" + getTestName(false) + ".json"); + myFixture.testStructureView(new Consumer() { + @Override + public void consume(StructureViewComponent component) { + assertTreeEqual(component.getTree(), expected); + } + }); + } + + public void testSimpleStructure() { + doTest("-SimpleStructure.json\n" + + " aaa\n" + + " -bbb\n" + + " ccc\n"); + } + + // Moved from JavaScript + + public void testJsonStructure() { + myFixture.configureByFile("structureView/SimpleStructure.json"); + + final StructureViewBuilder builder = LanguageStructureViewBuilder.INSTANCE.getStructureViewBuilder(myFixture.getFile()); + assertNotNull(builder); + StructureViewComponent component = null; + try { + final FileEditor editor = FileEditorManager.getInstance(getProject()).getSelectedEditor(myFixture.getFile().getVirtualFile()); + component = (StructureViewComponent)builder.createStructureView(editor, myFixture.getProject()); + final StructureViewModel model = component.getTreeModel(); + + TreeElement[] children = model.getRoot().getChildren(); + assertEquals(2, children.length); + assertEquals("aaa", children[0].getPresentation().getPresentableText()); + assertEquals(PlatformIcons.PROPERTY_ICON, children[0].getPresentation().getIcon(false)); + assertEquals("bbb", children[1].getPresentation().getPresentableText()); + assertEquals(PlatformIcons.PROPERTY_ICON, children[1].getPresentation().getIcon(false)); + + children = children[1].getChildren(); + assertEquals(1, children.length); + assertEquals("ccc", children[0].getPresentation().getPresentableText()); + assertEquals(PlatformIcons.PROPERTY_ICON, children[0].getPresentation().getIcon(false)); + } + finally { + if (component != null) { + Disposer.dispose(component); + } + } + } +} diff --git a/json/tests/test/com/intellij/json/JsonSurroundWithTest.java b/json/tests/test/com/intellij/json/JsonSurroundWithTest.java new file mode 100644 index 000000000000..9502ff6e51fc --- /dev/null +++ b/json/tests/test/com/intellij/json/JsonSurroundWithTest.java @@ -0,0 +1,44 @@ +package com.intellij.json; + +import com.intellij.codeInsight.generation.surroundWith.SurroundWithHandler; +import com.intellij.json.surroundWith.JsonWithObjectLiteralSurrounder; +import com.intellij.openapi.command.WriteCommandAction; + +/** + * @author Mikhail Golubev + */ +public class JsonSurroundWithTest extends JsonTestCase { + private void doTest() { + myFixture.configureByFile("/surround/" + getTestName(false) + ".json"); + new WriteCommandAction.Simple(myFixture.getProject()) { + @Override + protected void run() { + SurroundWithHandler.invoke(myFixture.getProject(), myFixture.getEditor(), myFixture.getFile(), new JsonWithObjectLiteralSurrounder()); + } + }.execute(); + myFixture.checkResultByFile("/surround/" + getTestName(false) + "_after.json", true); + } + + public void testSingleValue() { + doTest(); + } + + public void testSingleProperty() { + doTest(); + } + + public void testMultipleProperties() { + doTest(); + } + + public void testCannotSurroundPropertyKey() { + doTest(); + } + + // Moved from JavaScript + + + public void testObjectLiteral() { + doTest(); + } +} diff --git a/json/tests/test/com/intellij/json/JsonTestCase.java b/json/tests/test/com/intellij/json/JsonTestCase.java new file mode 100644 index 000000000000..1a72cd389fa1 --- /dev/null +++ b/json/tests/test/com/intellij/json/JsonTestCase.java @@ -0,0 +1,32 @@ +package com.intellij.json; + +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.testFramework.PlatformTestCase; +import com.intellij.testFramework.TestDataPath; +import com.intellij.testFramework.TestLoggerFactory; +import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase; +import org.jetbrains.annotations.NotNull; + +/** + * @author Mikhail Golubev + */ +@TestDataPath("$CONTENT_ROOT/testData") +public abstract class JsonTestCase extends LightCodeInsightFixtureTestCase { + static { + Logger.setFactory(TestLoggerFactory.class); + } + + protected static final Logger LOG = Logger.getInstance(JsonTestCase.class); + + @Override + public void setUp() throws Exception { + PlatformTestCase.autodetectPlatformPrefix(); + //IdeaTestCase.initPlatformPrefix(); + super.setUp(); + } + + @NotNull + public String getBasePath() { + return "/json/tests/testData"; + } +} \ No newline at end of file diff --git a/json/tests/testData/commenter/BlockComment.json b/json/tests/testData/commenter/BlockComment.json new file mode 100644 index 000000000000..9d99045e4bcf --- /dev/null +++ b/json/tests/testData/commenter/BlockComment.json @@ -0,0 +1,6 @@ +{ + "foo": null, + "bar": { + "baz": [1, 2, 3] + } +} \ No newline at end of file diff --git a/json/tests/testData/commenter/BlockComment_after.json b/json/tests/testData/commenter/BlockComment_after.json new file mode 100644 index 000000000000..f402ecbbf40c --- /dev/null +++ b/json/tests/testData/commenter/BlockComment_after.json @@ -0,0 +1,6 @@ +{ + "foo": null, + "bar": /*{ + "baz": [1, 2, 3] + }*/ +} \ No newline at end of file diff --git a/json/tests/testData/commenter/LineComment.json b/json/tests/testData/commenter/LineComment.json new file mode 100644 index 000000000000..ea08678b24aa --- /dev/null +++ b/json/tests/testData/commenter/LineComment.json @@ -0,0 +1,4 @@ +{ + "foo": true, + "bar": 42 +} \ No newline at end of file diff --git a/json/tests/testData/commenter/LineComment_after.json b/json/tests/testData/commenter/LineComment_after.json new file mode 100644 index 000000000000..ff8bd777f811 --- /dev/null +++ b/json/tests/testData/commenter/LineComment_after.json @@ -0,0 +1,4 @@ +{ +// "foo": true, + "bar": 42 +} \ No newline at end of file diff --git a/json/tests/testData/completion/InsideArrayElement1.json b/json/tests/testData/completion/InsideArrayElement1.json new file mode 100644 index 000000000000..646916b66a4b --- /dev/null +++ b/json/tests/testData/completion/InsideArrayElement1.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/json/tests/testData/completion/InsideArrayElement2.json b/json/tests/testData/completion/InsideArrayElement2.json new file mode 100644 index 000000000000..26f9f8d31c91 --- /dev/null +++ b/json/tests/testData/completion/InsideArrayElement2.json @@ -0,0 +1 @@ +[1, ] \ No newline at end of file diff --git a/json/tests/testData/completion/InsidePropertyKey1.json b/json/tests/testData/completion/InsidePropertyKey1.json new file mode 100644 index 000000000000..1c6ca653aea7 --- /dev/null +++ b/json/tests/testData/completion/InsidePropertyKey1.json @@ -0,0 +1 @@ +{: "foo"} \ No newline at end of file diff --git a/json/tests/testData/completion/InsidePropertyKey2.json b/json/tests/testData/completion/InsidePropertyKey2.json new file mode 100644 index 000000000000..2014e6d669cc --- /dev/null +++ b/json/tests/testData/completion/InsidePropertyKey2.json @@ -0,0 +1 @@ +{"foo": 42, } \ No newline at end of file diff --git a/json/tests/testData/completion/InsidePropertyValue.json b/json/tests/testData/completion/InsidePropertyValue.json new file mode 100644 index 000000000000..b18a6cc075a6 --- /dev/null +++ b/json/tests/testData/completion/InsidePropertyValue.json @@ -0,0 +1 @@ +{"foo": } \ No newline at end of file diff --git a/json/tests/testData/completion/Keywords.json b/json/tests/testData/completion/Keywords.json new file mode 100644 index 000000000000..73155ebe0d9b --- /dev/null +++ b/json/tests/testData/completion/Keywords.json @@ -0,0 +1 @@ +[nu] \ No newline at end of file diff --git a/json/tests/testData/completion/Keywords_2.json b/json/tests/testData/completion/Keywords_2.json new file mode 100644 index 000000000000..0435563c4c43 --- /dev/null +++ b/json/tests/testData/completion/Keywords_2.json @@ -0,0 +1 @@ +{aaa:nu} \ No newline at end of file diff --git a/json/tests/testData/completion/Keywords_2_after.json b/json/tests/testData/completion/Keywords_2_after.json new file mode 100644 index 000000000000..e8aa019bf4c8 --- /dev/null +++ b/json/tests/testData/completion/Keywords_2_after.json @@ -0,0 +1 @@ +{aaa:null} \ No newline at end of file diff --git a/json/tests/testData/completion/Keywords_after.json b/json/tests/testData/completion/Keywords_after.json new file mode 100644 index 000000000000..8dfaa79e6723 --- /dev/null +++ b/json/tests/testData/completion/Keywords_after.json @@ -0,0 +1 @@ +[null] \ No newline at end of file diff --git a/json/tests/testData/folding/ArrayFolding.json b/json/tests/testData/folding/ArrayFolding.json new file mode 100644 index 000000000000..7a139ce2ff19 --- /dev/null +++ b/json/tests/testData/folding/ArrayFolding.json @@ -0,0 +1,5 @@ +["foo", + ["inline"], + [1, + 2] + "bar"] \ No newline at end of file diff --git a/json/tests/testData/folding/ObjectFolding.json b/json/tests/testData/folding/ObjectFolding.json new file mode 100644 index 000000000000..42b297d0c658 --- /dev/null +++ b/json/tests/testData/folding/ObjectFolding.json @@ -0,0 +1,7 @@ +{ + "foo": null, + "bar": {"inline": true}, + "baz": { + "nested": true + } +} \ No newline at end of file diff --git a/json/tests/testData/folding/ObjectLiteral2.json b/json/tests/testData/folding/ObjectLiteral2.json new file mode 100644 index 000000000000..735121a3b40a --- /dev/null +++ b/json/tests/testData/folding/ObjectLiteral2.json @@ -0,0 +1,4 @@ +{ + "id": 3, + "value": 3 +} \ No newline at end of file diff --git a/json/tests/testData/folding/ObjectLiteral3.json b/json/tests/testData/folding/ObjectLiteral3.json new file mode 100644 index 000000000000..0b8c58f86631 --- /dev/null +++ b/json/tests/testData/folding/ObjectLiteral3.json @@ -0,0 +1,3 @@ +{ + "value": 3 +} \ No newline at end of file diff --git a/json/tests/testData/folding/ObjectLiteral4.json b/json/tests/testData/folding/ObjectLiteral4.json new file mode 100644 index 000000000000..1f900975755a --- /dev/null +++ b/json/tests/testData/folding/ObjectLiteral4.json @@ -0,0 +1,3 @@ +{ + "value": {"id": 3} +} \ No newline at end of file diff --git a/json/tests/testData/formatting/BlankLinesStripping.json b/json/tests/testData/formatting/BlankLinesStripping.json new file mode 100644 index 000000000000..69a90e18ff9d --- /dev/null +++ b/json/tests/testData/formatting/BlankLinesStripping.json @@ -0,0 +1,19 @@ +{ + "foo": + + + [ + + 42, + + null, + + { + + + "bar": "baz" + } + + + ] +} \ No newline at end of file diff --git a/json/tests/testData/formatting/BlankLinesStripping_after.json b/json/tests/testData/formatting/BlankLinesStripping_after.json new file mode 100644 index 000000000000..9ec9afd41975 --- /dev/null +++ b/json/tests/testData/formatting/BlankLinesStripping_after.json @@ -0,0 +1,9 @@ +{ + "foo": [ + 42, + null, + { + "bar": "baz" + } + ] +} \ No newline at end of file diff --git a/json/tests/testData/formatting/ContainerElementsAlignment.json b/json/tests/testData/formatting/ContainerElementsAlignment.json new file mode 100644 index 000000000000..43c477e371e0 --- /dev/null +++ b/json/tests/testData/formatting/ContainerElementsAlignment.json @@ -0,0 +1,12 @@ +{ + "array": [ + 1, + {}, + "eggs", +null + ], + "object": { + "foo": [1, 2], +"bar": true + } +} \ No newline at end of file diff --git a/json/tests/testData/formatting/ContainerElementsAlignment_after.json b/json/tests/testData/formatting/ContainerElementsAlignment_after.json new file mode 100644 index 000000000000..82d930ff2024 --- /dev/null +++ b/json/tests/testData/formatting/ContainerElementsAlignment_after.json @@ -0,0 +1,15 @@ +{ + "array": [ + 1, + {}, + "eggs", + null + ], + "object": { + "foo": [ + 1, + 2 + ], + "bar": true + } +} \ No newline at end of file diff --git a/json/tests/testData/formatting/ReformatJSon.json b/json/tests/testData/formatting/ReformatJSon.json new file mode 100644 index 000000000000..7d8fefb95541 --- /dev/null +++ b/json/tests/testData/formatting/ReformatJSon.json @@ -0,0 +1 @@ +[[{"id":1,"class":"Train","latitude":47.9329065912321,"longitude":12.041015625,"name":"Train AA","ontime":true},{"id":2,"class":"Train","latitude":47.9862451742621,"longitude":13.436279296875,"name":"Train AA","ontime":false},{"id":3,"class":"Train","latitude":48.0890879988176,"longitude":11.8185424804688,"name":"Train AA","ontime":true}],[]] \ No newline at end of file diff --git a/json/tests/testData/formatting/ReformatJSon2.json b/json/tests/testData/formatting/ReformatJSon2.json new file mode 100644 index 000000000000..1dad0744b0b9 --- /dev/null +++ b/json/tests/testData/formatting/ReformatJSon2.json @@ -0,0 +1 @@ +{ "data":{"id":{"type":"primary","label":"ID"},"destination_id":{"type":"int","label":"Destination","helper":"select","belongs":"destination"},"type_id":{"type":"int","label":"Type","helper":"select","list":"event_types"},"tour_guide_id":{"type":"int","label":"TourGuide","helper":"select","belongs":"tour_guide"},"name":{"type":"text","label":"Title"},"body":{"type":"text","label":"Body","helper":"richedit"},"date":{"type":"int","label":"Date","helper":"datetime"},"created":{"type":"int","label":"Created","helper":"datetime","editable":"false"},"modified":{"type":"int","label":"Modified","helper":"datetime","editable":"false" }}} \ No newline at end of file diff --git a/json/tests/testData/formatting/ReformatJSon2_after.json b/json/tests/testData/formatting/ReformatJSon2_after.json new file mode 100644 index 000000000000..f6ce08672849 --- /dev/null +++ b/json/tests/testData/formatting/ReformatJSon2_after.json @@ -0,0 +1,52 @@ +{ + "data": { + "id": { + "type": "primary", + "label": "ID" + }, + "destination_id": { + "type": "int", + "label": "Destination", + "helper": "select", + "belongs": "destination" + }, + "type_id": { + "type": "int", + "label": "Type", + "helper": "select", + "list": "event_types" + }, + "tour_guide_id": { + "type": "int", + "label": "TourGuide", + "helper": "select", + "belongs": "tour_guide" + }, + "name": { + "type": "text", + "label": "Title" + }, + "body": { + "type": "text", + "label": "Body", + "helper": "richedit" + }, + "date": { + "type": "int", + "label": "Date", + "helper": "datetime" + }, + "created": { + "type": "int", + "label": "Created", + "helper": "datetime", + "editable": "false" + }, + "modified": { + "type": "int", + "label": "Modified", + "helper": "datetime", + "editable": "false" + } + } +} \ No newline at end of file diff --git a/json/tests/testData/formatting/ReformatJSon_after.json b/json/tests/testData/formatting/ReformatJSon_after.json new file mode 100644 index 000000000000..10c0b4fcd31b --- /dev/null +++ b/json/tests/testData/formatting/ReformatJSon_after.json @@ -0,0 +1,29 @@ +[ + [ + { + "id": 1, + "class": "Train", + "latitude": 47.9329065912321, + "longitude": 12.041015625, + "name": "Train AA", + "ontime": true + }, + { + "id": 2, + "class": "Train", + "latitude": 47.9862451742621, + "longitude": 13.436279296875, + "name": "Train AA", + "ontime": false + }, + { + "id": 3, + "class": "Train", + "latitude": 48.0890879988176, + "longitude": 11.8185424804688, + "name": "Train AA", + "ontime": true + } + ], + [] +] \ No newline at end of file diff --git a/json/tests/testData/formatting/SpacesInsertion.json b/json/tests/testData/formatting/SpacesInsertion.json new file mode 100644 index 000000000000..3f77fd3e9348 --- /dev/null +++ b/json/tests/testData/formatting/SpacesInsertion.json @@ -0,0 +1 @@ +{"foo":[1,2 ,"bar"],"baz":null} \ No newline at end of file diff --git a/json/tests/testData/formatting/SpacesInsertion_after.json b/json/tests/testData/formatting/SpacesInsertion_after.json new file mode 100644 index 000000000000..09973711870a --- /dev/null +++ b/json/tests/testData/formatting/SpacesInsertion_after.json @@ -0,0 +1,8 @@ +{ + "foo": [ + 1, + 2, + "bar" + ], + "baz": null +} \ No newline at end of file diff --git a/json/tests/testData/formatting/Web3830.json b/json/tests/testData/formatting/Web3830.json new file mode 100644 index 000000000000..60751eeb64a1 --- /dev/null +++ b/json/tests/testData/formatting/Web3830.json @@ -0,0 +1,181 @@ +{"version": 2, "guid": "james-quiz-with-one-page", "name": "[James] Quiz with one page", "page": [ + { + "page_type": "TOC", + "sequence_number": 0, + "uid": 1, + "page_number": 1, + "item_element": [ + { + "item_element_type": "TOC_ENTRY", + "toc_entry": { + "link": { + "label": "[James] Quiz with one page", + "ref": "2#james-quiz-with-one-page", + "uri": "", + "new_window_type": "NOT_NEW_WINDOW", + "labelRichText": { + "rich_text_type": "RICH_TEXT", + "richText": [ + { + "rich_text_type": "STR", + "str": { + "text": "[James] Quiz with one page" + } + } + ] + } + } + } + } + ] + }, + { + "page_type": "QUIZ", + "sequence_number": 1, + "uid": 2, + "page_number": 2, + "item_element": [ + { + "item_element_type": "TOC_OBJECT_ITEM", + "toc_object_item": { + "tocObject": { + "id": "james-quiz-with-one-page", + "label": "[James] Quiz with one page", + "level": 1, + "labelRichText": { + "rich_text_type": "RICH_TEXT", + "richText": [ + { + "rich_text_type": "STR", + "str": { + "text": "[James] Quiz with one page" + } + } + ] + } + } + } + }, + { + "item_element_type": "FILL_IN_THE_BLANK", + "fill_in_the_blank": { + "ChoiceQuestionElement": { + "id": "fib", + "question_number": 1, + "title": { + "text": "" + }, + "questionText": { + "rich_text_type": "RICH_TEXT", + "richText": [ + { + "rich_text_type": "RICH_TEXT", + "richText": [ + { + "rich_text_type": "STR", + "str": { + "text": "Fill in the Blank" + } + } + ] + }, + { + "rich_text_type": "ELEMENT_BR", + "element_br": {} + }, + { + "rich_text_type": "RICH_TEXT", + "richText": [ + { + "rich_text_type": "STR", + "str": { + "text": "___ * ___ = 6" + } + } + ] + } + ], + "id": "stem" + }, + "answer_element": [ + { + "answer_value": "2", + "text": { + "rich_text_type": "RICH_TEXT", + "richText": [ + { + "rich_text_type": "STR", + "str": { + "text": "2" + } + } + ], + "id": "option1" + }, + "correct": "CORRECT_ANSWER" + }, + { + "answer_value": "3", + "text": { + "rich_text_type": "RICH_TEXT", + "richText": [ + { + "rich_text_type": "STR", + "str": { + "text": "3" + } + } + ], + "id": "option2" + }, + "correct": "CORRECT_ANSWER" + } + ], + "answer_type": "COURSE_PROVIDED", + "number_tries": 1, + "correct_remediation_element": { + "text": [ + { + "rich_text_type": "RICH_TEXT", + "richText": [ + { + "rich_text_type": "STR", + "str": { + "text": "Correct Feedback Text" + } + } + ], + "id": "remediation-correct" + } + ] + }, + "incorrect_remediation_element": { + "text": [ + { + "rich_text_type": "RICH_TEXT", + "richText": [ + { + "rich_text_type": "STR", + "str": { + "text": "Incorrect Feedback Text" + } + } + ], + "id": "remediation-incorrect" + } + ] + } + } + } + } + ], + "id": "james-quiz-with-one-page", + "page_subtype": "UNDEFINED" + }, + { + "page_type": "FINISH", + "sequence_number": 6, + "uid": 5, + "page_number": 5 + } +], "toc_page_number": 1, "passing_level": 0.0} \ No newline at end of file diff --git a/json/tests/testData/formatting/Web3830_after.json b/json/tests/testData/formatting/Web3830_after.json new file mode 100644 index 000000000000..1276e3f338f5 --- /dev/null +++ b/json/tests/testData/formatting/Web3830_after.json @@ -0,0 +1,188 @@ +{ + "version": 2, + "guid": "james-quiz-with-one-page", + "name": "[James] Quiz with one page", + "page": [ + { + "page_type": "TOC", + "sequence_number": 0, + "uid": 1, + "page_number": 1, + "item_element": [ + { + "item_element_type": "TOC_ENTRY", + "toc_entry": { + "link": { + "label": "[James] Quiz with one page", + "ref": "2#james-quiz-with-one-page", + "uri": "", + "new_window_type": "NOT_NEW_WINDOW", + "labelRichText": { + "rich_text_type": "RICH_TEXT", + "richText": [ + { + "rich_text_type": "STR", + "str": { + "text": "[James] Quiz with one page" + } + } + ] + } + } + } + } + ] + }, + { + "page_type": "QUIZ", + "sequence_number": 1, + "uid": 2, + "page_number": 2, + "item_element": [ + { + "item_element_type": "TOC_OBJECT_ITEM", + "toc_object_item": { + "tocObject": { + "id": "james-quiz-with-one-page", + "label": "[James] Quiz with one page", + "level": 1, + "labelRichText": { + "rich_text_type": "RICH_TEXT", + "richText": [ + { + "rich_text_type": "STR", + "str": { + "text": "[James] Quiz with one page" + } + } + ] + } + } + } + }, + { + "item_element_type": "FILL_IN_THE_BLANK", + "fill_in_the_blank": { + "ChoiceQuestionElement": { + "id": "fib", + "question_number": 1, + "title": { + "text": "" + }, + "questionText": { + "rich_text_type": "RICH_TEXT", + "richText": [ + { + "rich_text_type": "RICH_TEXT", + "richText": [ + { + "rich_text_type": "STR", + "str": { + "text": "Fill in the Blank" + } + } + ] + }, + { + "rich_text_type": "ELEMENT_BR", + "element_br": {} + }, + { + "rich_text_type": "RICH_TEXT", + "richText": [ + { + "rich_text_type": "STR", + "str": { + "text": "___ * ___ = 6" + } + } + ] + } + ], + "id": "stem" + }, + "answer_element": [ + { + "answer_value": "2", + "text": { + "rich_text_type": "RICH_TEXT", + "richText": [ + { + "rich_text_type": "STR", + "str": { + "text": "2" + } + } + ], + "id": "option1" + }, + "correct": "CORRECT_ANSWER" + }, + { + "answer_value": "3", + "text": { + "rich_text_type": "RICH_TEXT", + "richText": [ + { + "rich_text_type": "STR", + "str": { + "text": "3" + } + } + ], + "id": "option2" + }, + "correct": "CORRECT_ANSWER" + } + ], + "answer_type": "COURSE_PROVIDED", + "number_tries": 1, + "correct_remediation_element": { + "text": [ + { + "rich_text_type": "RICH_TEXT", + "richText": [ + { + "rich_text_type": "STR", + "str": { + "text": "Correct Feedback Text" + } + } + ], + "id": "remediation-correct" + } + ] + }, + "incorrect_remediation_element": { + "text": [ + { + "rich_text_type": "RICH_TEXT", + "richText": [ + { + "rich_text_type": "STR", + "str": { + "text": "Incorrect Feedback Text" + } + } + ], + "id": "remediation-incorrect" + } + ] + } + } + } + } + ], + "id": "james-quiz-with-one-page", + "page_subtype": "UNDEFINED" + }, + { + "page_type": "FINISH", + "sequence_number": 6, + "uid": 5, + "page_number": 5 + } + ], + "toc_page_number": 1, + "passing_level": 0.0 +} \ No newline at end of file diff --git a/json/tests/testData/formatting/Wrapping.json b/json/tests/testData/formatting/Wrapping.json new file mode 100644 index 000000000000..33722e73f6a4 --- /dev/null +++ b/json/tests/testData/formatting/Wrapping.json @@ -0,0 +1,5 @@ +[ + "spam!spam!spam!spam!", "ham!ham!ham!", + 1, 2, 3, 4, 5, 6, 7, + ["this line is long too"] +] \ No newline at end of file diff --git a/json/tests/testData/formatting/Wrapping_after.json b/json/tests/testData/formatting/Wrapping_after.json new file mode 100644 index 000000000000..f6f41b61f43b --- /dev/null +++ b/json/tests/testData/formatting/Wrapping_after.json @@ -0,0 +1,12 @@ +[ + "spam!spam!spam!spam!", + "ham!ham!ham!", + 1, + 2, + 3, + 4, + 5, + 6, + 7, + ["this line is long too"] +] \ No newline at end of file diff --git a/json/tests/testData/highlighting/ComplianceProblems.json b/json/tests/testData/highlighting/ComplianceProblems.json new file mode 100644 index 000000000000..1511f50b369e --- /dev/null +++ b/json/tests/testData/highlighting/ComplianceProblems.json @@ -0,0 +1,14 @@ +{ + /* Property key checks */ + "key1": null, + 'key2': null, + key3: null, + 42: null, + + // Property value checks + "key4": "foo", + "key5": 'foo', + "key6": foo, + "key7": [42,], + "key8": false, +} \ No newline at end of file diff --git a/json/tests/testData/highlighting/ComplianceProblemsTopLevelValue.json b/json/tests/testData/highlighting/ComplianceProblemsTopLevelValue.json new file mode 100644 index 000000000000..211a7b597d5d --- /dev/null +++ b/json/tests/testData/highlighting/ComplianceProblemsTopLevelValue.json @@ -0,0 +1 @@ +null \ No newline at end of file diff --git a/json/tests/testData/highlighting/JSON.json b/json/tests/testData/highlighting/JSON.json new file mode 100644 index 000000000000..709e80ba1311 --- /dev/null +++ b/json/tests/testData/highlighting/JSON.json @@ -0,0 +1,12 @@ +{ "bindings": [ + {"ircEvent": undefined}, + {'ircEvent' :123}, + /*comment*/ + {ircEvent: "PRIVMSG", "method": "randomURI", + "regex": 'test' + }, + ], +/** some comment */ +"foo": 1, +"lastComma": {"a":"b",} +} diff --git a/json/tests/testData/highlighting/JSON_with_comment.json b/json/tests/testData/highlighting/JSON_with_comment.json new file mode 100644 index 000000000000..7cca61f3d904 --- /dev/null +++ b/json/tests/testData/highlighting/JSON_with_comment.json @@ -0,0 +1,4 @@ +{ + // simple comment + "test" : 1 +} diff --git a/json/tests/testData/highlighting/SemanticHighlighting.json b/json/tests/testData/highlighting/SemanticHighlighting.json new file mode 100644 index 000000000000..66d55b3eec1d --- /dev/null +++ b/json/tests/testData/highlighting/SemanticHighlighting.json @@ -0,0 +1,3 @@ +{ + "aaa":123 +} \ No newline at end of file diff --git a/json/tests/testData/highlighting/StringLiterals.json b/json/tests/testData/highlighting/StringLiterals.json new file mode 100644 index 000000000000..b901624d93a5 --- /dev/null +++ b/json/tests/testData/highlighting/StringLiterals.json @@ -0,0 +1,8 @@ +[ + "correctly escaped trailing slashes \\\\", + "illegal escape sequence: \q", + "illegal unicode escapes: \uZZZZ", + "illegal unicode escapes: \uBEE", + "unclosed double quoted string , + 'unclosed single quoted string +] \ No newline at end of file diff --git a/json/tests/testData/mover/ArraySelectionMovedDown.json b/json/tests/testData/mover/ArraySelectionMovedDown.json new file mode 100644 index 000000000000..476b56d42dea --- /dev/null +++ b/json/tests/testData/mover/ArraySelectionMovedDown.json @@ -0,0 +1,5 @@ +[ + 1, [2], [true, + false], + null +] \ No newline at end of file diff --git a/json/tests/testData/mover/ArraySelectionMovedDown_afterDown.json b/json/tests/testData/mover/ArraySelectionMovedDown_afterDown.json new file mode 100644 index 000000000000..9db514b06905 --- /dev/null +++ b/json/tests/testData/mover/ArraySelectionMovedDown_afterDown.json @@ -0,0 +1,5 @@ +[ + null, + 1, [2], [true, + false] +] \ No newline at end of file diff --git a/json/tests/testData/mover/LastArrayElementMovedUp.json b/json/tests/testData/mover/LastArrayElementMovedUp.json new file mode 100644 index 000000000000..5f8f4d74465b --- /dev/null +++ b/json/tests/testData/mover/LastArrayElementMovedUp.json @@ -0,0 +1,4 @@ +[ + 1, + 2 +] \ No newline at end of file diff --git a/json/tests/testData/mover/LastArrayElementMovedUp_afterUp.json b/json/tests/testData/mover/LastArrayElementMovedUp_afterUp.json new file mode 100644 index 000000000000..9331d492ed88 --- /dev/null +++ b/json/tests/testData/mover/LastArrayElementMovedUp_afterUp.json @@ -0,0 +1,4 @@ +[ + 2, + 1 +] \ No newline at end of file diff --git a/json/tests/testData/mover/LastObjectPropertyMovedUp.json b/json/tests/testData/mover/LastObjectPropertyMovedUp.json new file mode 100644 index 000000000000..d9b302180842 --- /dev/null +++ b/json/tests/testData/mover/LastObjectPropertyMovedUp.json @@ -0,0 +1,4 @@ +{ + "foo": true, + "bar": false +} \ No newline at end of file diff --git a/json/tests/testData/mover/LastObjectPropertyMovedUp_afterUp.json b/json/tests/testData/mover/LastObjectPropertyMovedUp_afterUp.json new file mode 100644 index 000000000000..72202c20b8f6 --- /dev/null +++ b/json/tests/testData/mover/LastObjectPropertyMovedUp_afterUp.json @@ -0,0 +1,4 @@ +{ + "bar": false, + "foo": true +} \ No newline at end of file diff --git a/json/tests/testData/mover/ObjectSelectionMovedDown.json b/json/tests/testData/mover/ObjectSelectionMovedDown.json new file mode 100644 index 000000000000..57efbe402072 --- /dev/null +++ b/json/tests/testData/mover/ObjectSelectionMovedDown.json @@ -0,0 +1,6 @@ +{ + "foo": false, "bar": 42, + "baz": [1, 2, + 3], + "quux": null +} \ No newline at end of file diff --git a/json/tests/testData/mover/ObjectSelectionMovedDown_afterDown.json b/json/tests/testData/mover/ObjectSelectionMovedDown_afterDown.json new file mode 100644 index 000000000000..860d3b5d3783 --- /dev/null +++ b/json/tests/testData/mover/ObjectSelectionMovedDown_afterDown.json @@ -0,0 +1,6 @@ +{ + "quux": null, + "foo": false, "bar": 42, + "baz": [1, 2, + 3] +} \ No newline at end of file diff --git a/json/tests/testData/mover/Web_10585.json b/json/tests/testData/mover/Web_10585.json new file mode 100644 index 000000000000..ed5067667585 --- /dev/null +++ b/json/tests/testData/mover/Web_10585.json @@ -0,0 +1,4 @@ +{ + "name": "foo", + "cycles": 37, +} \ No newline at end of file diff --git a/json/tests/testData/mover/Web_10585_afterDown.json b/json/tests/testData/mover/Web_10585_afterDown.json new file mode 100644 index 000000000000..d74a0f9cd1f7 --- /dev/null +++ b/json/tests/testData/mover/Web_10585_afterDown.json @@ -0,0 +1,4 @@ +{ + "name": "foo", +} +"cycles": 37, diff --git a/json/tests/testData/mover/Web_10585_afterUp.json b/json/tests/testData/mover/Web_10585_afterUp.json new file mode 100644 index 000000000000..2b3b3c4b2e3e --- /dev/null +++ b/json/tests/testData/mover/Web_10585_afterUp.json @@ -0,0 +1,4 @@ +{ + "cycles": 37, + "name": "foo", +} \ No newline at end of file diff --git a/json/tests/testData/psi/Comments.json b/json/tests/testData/psi/Comments.json new file mode 100644 index 000000000000..60ad9bf4e447 --- /dev/null +++ b/json/tests/testData/psi/Comments.json @@ -0,0 +1,15 @@ +// End of line comment +{ +/**/ +/***/ +/*** */ +/** **/ +/* **/ +/*a*/ /*b*/ +/* C-style block comment */ +/* May span +multiple +lines + */ +} +/* And be incomplete \ No newline at end of file diff --git a/json/tests/testData/psi/Comments.txt b/json/tests/testData/psi/Comments.txt new file mode 100644 index 000000000000..7225a522186e --- /dev/null +++ b/json/tests/testData/psi/Comments.txt @@ -0,0 +1,27 @@ +JsonFile: Comments.json + PsiComment(LINE_COMMENT)('// End of line comment') + PsiWhiteSpace('\n') + JsonObject + PsiElement({)('{') + PsiWhiteSpace('\n') + PsiComment(BLOCK_COMMENT)('/**/') + PsiWhiteSpace('\n') + PsiComment(BLOCK_COMMENT)('/***/') + PsiWhiteSpace('\n') + PsiComment(BLOCK_COMMENT)('/*** */') + PsiWhiteSpace('\n') + PsiComment(BLOCK_COMMENT)('/** **/') + PsiWhiteSpace('\n') + PsiComment(BLOCK_COMMENT)('/* **/') + PsiWhiteSpace('\n') + PsiComment(BLOCK_COMMENT)('/*a*/') + PsiWhiteSpace(' ') + PsiComment(BLOCK_COMMENT)('/*b*/') + PsiWhiteSpace('\n') + PsiComment(BLOCK_COMMENT)('/* C-style block comment */') + PsiWhiteSpace('\n') + PsiComment(BLOCK_COMMENT)('/* May span\nmultiple\nlines\n */') + PsiWhiteSpace('\n') + PsiElement(})('}') + PsiWhiteSpace('\n') + PsiComment(BLOCK_COMMENT)('/* And be incomplete') \ No newline at end of file diff --git a/json/tests/testData/psi/IncompleteObjectProperties.json b/json/tests/testData/psi/IncompleteObjectProperties.json new file mode 100644 index 000000000000..9866dcb1264c --- /dev/null +++ b/json/tests/testData/psi/IncompleteObjectProperties.json @@ -0,0 +1,5 @@ +{ + "foo": , + "bar", + "baz" +} \ No newline at end of file diff --git a/json/tests/testData/psi/IncompleteObjectProperties.txt b/json/tests/testData/psi/IncompleteObjectProperties.txt new file mode 100644 index 000000000000..4187382bfd1e --- /dev/null +++ b/json/tests/testData/psi/IncompleteObjectProperties.txt @@ -0,0 +1,27 @@ +JsonFile: IncompleteObjectProperties.json + JsonObject + PsiElement({)('{') + PsiWhiteSpace('\n ') + JsonProperty + JsonStringLiteral + PsiElement(DOUBLE_QUOTED_STRING)('"foo"') + PsiElement(:)(':') + PsiErrorElement: expected, got ',' + + PsiWhiteSpace(' ') + PsiElement(,)(',') + PsiWhiteSpace('\n ') + JsonProperty + JsonStringLiteral + PsiElement(DOUBLE_QUOTED_STRING)('"bar"') + PsiErrorElement:':' expected, got ',' + + PsiElement(,)(',') + PsiWhiteSpace('\n ') + JsonProperty + JsonStringLiteral + PsiElement(DOUBLE_QUOTED_STRING)('"baz"') + PsiErrorElement:':' expected, got '}' + + PsiWhiteSpace('\n') + PsiElement(})('}') \ No newline at end of file diff --git a/json/tests/testData/psi/Keywords.json b/json/tests/testData/psi/Keywords.json new file mode 100644 index 000000000000..6e6569203669 --- /dev/null +++ b/json/tests/testData/psi/Keywords.json @@ -0,0 +1 @@ +[true, false, null] \ No newline at end of file diff --git a/json/tests/testData/psi/Keywords.txt b/json/tests/testData/psi/Keywords.txt new file mode 100644 index 000000000000..c032f64c7e0c --- /dev/null +++ b/json/tests/testData/psi/Keywords.txt @@ -0,0 +1,14 @@ +JsonFile: Keywords.json + JsonArray + PsiElement([)('[') + JsonBooleanLiteral + PsiElement(true)('true') + PsiElement(,)(',') + PsiWhiteSpace(' ') + JsonBooleanLiteral + PsiElement(false)('false') + PsiElement(,)(',') + PsiWhiteSpace(' ') + JsonNullLiteral + PsiElement(null)('null') + PsiElement(])(']') \ No newline at end of file diff --git a/json/tests/testData/psi/MissingCommaBetweenArrayElements.json b/json/tests/testData/psi/MissingCommaBetweenArrayElements.json new file mode 100644 index 000000000000..ded015e783ca --- /dev/null +++ b/json/tests/testData/psi/MissingCommaBetweenArrayElements.json @@ -0,0 +1,4 @@ +[ + 42 + null +] \ No newline at end of file diff --git a/json/tests/testData/psi/MissingCommaBetweenArrayElements.txt b/json/tests/testData/psi/MissingCommaBetweenArrayElements.txt new file mode 100644 index 000000000000..c1289d9199e6 --- /dev/null +++ b/json/tests/testData/psi/MissingCommaBetweenArrayElements.txt @@ -0,0 +1,13 @@ +JsonFile: MissingCommaBetweenArrayElements.json + JsonArray + PsiElement([)('[') + PsiWhiteSpace('\n ') + JsonNumberLiteral + PsiElement(NUMBER)('42') + PsiErrorElement:',' or ']' expected, got 'null' + + PsiWhiteSpace('\n ') + JsonNullLiteral + PsiElement(null)('null') + PsiWhiteSpace('\n') + PsiElement(])(']') \ No newline at end of file diff --git a/json/tests/testData/psi/MissingCommaBetweenObjectProperties.json b/json/tests/testData/psi/MissingCommaBetweenObjectProperties.json new file mode 100644 index 000000000000..ff7451a67df5 --- /dev/null +++ b/json/tests/testData/psi/MissingCommaBetweenObjectProperties.json @@ -0,0 +1,4 @@ +{ + "foo": 42 + "bar": null +} \ No newline at end of file diff --git a/json/tests/testData/psi/MissingCommaBetweenObjectProperties.txt b/json/tests/testData/psi/MissingCommaBetweenObjectProperties.txt new file mode 100644 index 000000000000..dcd15c72a05a --- /dev/null +++ b/json/tests/testData/psi/MissingCommaBetweenObjectProperties.txt @@ -0,0 +1,23 @@ +JsonFile: MissingCommaBetweenObjectProperties.json + JsonObject + PsiElement({)('{') + PsiWhiteSpace('\n ') + JsonProperty + JsonStringLiteral + PsiElement(DOUBLE_QUOTED_STRING)('"foo"') + PsiElement(:)(':') + PsiWhiteSpace(' ') + JsonNumberLiteral + PsiElement(NUMBER)('42') + PsiErrorElement:',' or '}' expected, got '"bar"' + + PsiWhiteSpace('\n ') + JsonProperty + JsonStringLiteral + PsiElement(DOUBLE_QUOTED_STRING)('"bar"') + PsiElement(:)(':') + PsiWhiteSpace(' ') + JsonNullLiteral + PsiElement(null)('null') + PsiWhiteSpace('\n') + PsiElement(})('}') \ No newline at end of file diff --git a/json/tests/testData/psi/NestedArrayLiterals.json b/json/tests/testData/psi/NestedArrayLiterals.json new file mode 100644 index 000000000000..87b6305b04bb --- /dev/null +++ b/json/tests/testData/psi/NestedArrayLiterals.json @@ -0,0 +1 @@ +[1, ["foo", [null]]] \ No newline at end of file diff --git a/json/tests/testData/psi/NestedArrayLiterals.txt b/json/tests/testData/psi/NestedArrayLiterals.txt new file mode 100644 index 000000000000..a80d62256fa3 --- /dev/null +++ b/json/tests/testData/psi/NestedArrayLiterals.txt @@ -0,0 +1,20 @@ +JsonFile: NestedArrayLiterals.json + JsonArray + PsiElement([)('[') + JsonNumberLiteral + PsiElement(NUMBER)('1') + PsiElement(,)(',') + PsiWhiteSpace(' ') + JsonArray + PsiElement([)('[') + JsonStringLiteral + PsiElement(DOUBLE_QUOTED_STRING)('"foo"') + PsiElement(,)(',') + PsiWhiteSpace(' ') + JsonArray + PsiElement([)('[') + JsonNullLiteral + PsiElement(null)('null') + PsiElement(])(']') + PsiElement(])(']') + PsiElement(])(']') \ No newline at end of file diff --git a/json/tests/testData/psi/NestedObjectLiterals.json b/json/tests/testData/psi/NestedObjectLiterals.json new file mode 100644 index 000000000000..0294a5555c81 --- /dev/null +++ b/json/tests/testData/psi/NestedObjectLiterals.json @@ -0,0 +1 @@ +{ "foo": { "bar": {"baz": null} }} \ No newline at end of file diff --git a/json/tests/testData/psi/NestedObjectLiterals.txt b/json/tests/testData/psi/NestedObjectLiterals.txt new file mode 100644 index 000000000000..2cfb189613da --- /dev/null +++ b/json/tests/testData/psi/NestedObjectLiterals.txt @@ -0,0 +1,30 @@ +JsonFile: NestedObjectLiterals.json + JsonObject + PsiElement({)('{') + PsiWhiteSpace(' ') + JsonProperty + JsonStringLiteral + PsiElement(DOUBLE_QUOTED_STRING)('"foo"') + PsiElement(:)(':') + PsiWhiteSpace(' ') + JsonObject + PsiElement({)('{') + PsiWhiteSpace(' ') + JsonProperty + JsonStringLiteral + PsiElement(DOUBLE_QUOTED_STRING)('"bar"') + PsiElement(:)(':') + PsiWhiteSpace(' ') + JsonObject + PsiElement({)('{') + JsonProperty + JsonStringLiteral + PsiElement(DOUBLE_QUOTED_STRING)('"baz"') + PsiElement(:)(':') + PsiWhiteSpace(' ') + JsonNullLiteral + PsiElement(null)('null') + PsiElement(})('}') + PsiWhiteSpace(' ') + PsiElement(})('}') + PsiElement(})('}') \ No newline at end of file diff --git a/json/tests/testData/psi/NonStandardPropertyKeys.json b/json/tests/testData/psi/NonStandardPropertyKeys.json new file mode 100644 index 000000000000..20ce603be5d1 --- /dev/null +++ b/json/tests/testData/psi/NonStandardPropertyKeys.json @@ -0,0 +1,7 @@ +{ + 42: null, + null: null, + false: null, + true: null, + identifier: null +} \ No newline at end of file diff --git a/json/tests/testData/psi/NonStandardPropertyKeys.txt b/json/tests/testData/psi/NonStandardPropertyKeys.txt new file mode 100644 index 000000000000..dd862090033e --- /dev/null +++ b/json/tests/testData/psi/NonStandardPropertyKeys.txt @@ -0,0 +1,49 @@ +JsonFile: NonStandardPropertyKeys.json + JsonObject + PsiElement({)('{') + PsiWhiteSpace('\n ') + JsonProperty + JsonNumberLiteral + PsiElement(NUMBER)('42') + PsiElement(:)(':') + PsiWhiteSpace(' ') + JsonNullLiteral + PsiElement(null)('null') + PsiElement(,)(',') + PsiWhiteSpace('\n ') + JsonProperty + JsonNullLiteral + PsiElement(null)('null') + PsiElement(:)(':') + PsiWhiteSpace(' ') + JsonNullLiteral + PsiElement(null)('null') + PsiElement(,)(',') + PsiWhiteSpace('\n ') + JsonProperty + JsonBooleanLiteral + PsiElement(false)('false') + PsiElement(:)(':') + PsiWhiteSpace(' ') + JsonNullLiteral + PsiElement(null)('null') + PsiElement(,)(',') + PsiWhiteSpace('\n ') + JsonProperty + JsonBooleanLiteral + PsiElement(true)('true') + PsiElement(:)(':') + PsiWhiteSpace(' ') + JsonNullLiteral + PsiElement(null)('null') + PsiElement(,)(',') + PsiWhiteSpace('\n ') + JsonProperty + JsonReferenceExpression + PsiElement(INDENTIFIER)('identifier') + PsiElement(:)(':') + PsiWhiteSpace(' ') + JsonNullLiteral + PsiElement(null)('null') + PsiWhiteSpace('\n') + PsiElement(})('}') \ No newline at end of file diff --git a/json/tests/testData/psi/Real1.json b/json/tests/testData/psi/Real1.json new file mode 100644 index 000000000000..b8926d999ca4 --- /dev/null +++ b/json/tests/testData/psi/Real1.json @@ -0,0 +1,8 @@ +{ + navigation: [ + { + name: "Demo Applications", + demos: [ "Mail", "Moxie" ] + } + ] +} diff --git a/json/tests/testData/psi/Real1.txt b/json/tests/testData/psi/Real1.txt new file mode 100644 index 000000000000..70cfc8d56be1 --- /dev/null +++ b/json/tests/testData/psi/Real1.txt @@ -0,0 +1,43 @@ +JSFile:Real.json + JSObjectLiteralExpression + PsiElement(JS:LBRACE)('{') + PsiWhiteSpace('\n ') + JSProperty + PsiElement(JS:IDENTIFIER)('navigation') + PsiElement(JS:COLON)(':') + PsiWhiteSpace(' ') + JSArrayLiteralExpression + PsiElement(JS:LBRACKET)('[') + PsiWhiteSpace('\n ') + JSObjectLiteralExpression + PsiElement(JS:LBRACE)('{') + PsiWhiteSpace('\n ') + JSProperty + PsiElement(JS:IDENTIFIER)('name') + PsiElement(JS:COLON)(':') + PsiWhiteSpace(' ') + JSLiteralExpression + PsiElement(JS:STRING_LITERAL)('"Demo Applications"') + PsiElement(JS:COMMA)(',') + PsiWhiteSpace('\n ') + JSProperty + PsiElement(JS:IDENTIFIER)('demos') + PsiElement(JS:COLON)(':') + PsiWhiteSpace(' ') + JSArrayLiteralExpression + PsiElement(JS:LBRACKET)('[') + PsiWhiteSpace(' ') + JSLiteralExpression + PsiElement(JS:STRING_LITERAL)('"Mail"') + PsiElement(JS:COMMA)(',') + PsiWhiteSpace(' ') + JSLiteralExpression + PsiElement(JS:STRING_LITERAL)('"Moxie"') + PsiWhiteSpace(' ') + PsiElement(JS:RBRACKET)(']') + PsiWhiteSpace('\n ') + PsiElement(JS:RBRACE)('}') + PsiWhiteSpace('\n ') + PsiElement(JS:RBRACKET)(']') + PsiWhiteSpace('\n') + PsiElement(JS:RBRACE)('}') \ No newline at end of file diff --git a/json/tests/testData/psi/Real2.json b/json/tests/testData/psi/Real2.json new file mode 100644 index 000000000000..99d381bb2612 --- /dev/null +++ b/json/tests/testData/psi/Real2.json @@ -0,0 +1,3 @@ +{ + /*xxx*/ navigation: "111" // yyy +} diff --git a/json/tests/testData/psi/Real2.txt b/json/tests/testData/psi/Real2.txt new file mode 100644 index 000000000000..063c058e6368 --- /dev/null +++ b/json/tests/testData/psi/Real2.txt @@ -0,0 +1,16 @@ +JSFile:Real2.json + JSObjectLiteralExpression + PsiElement(JS:LBRACE)('{') + PsiWhiteSpace('\n ') + PsiComment(JS:C_STYLE_COMMENT)('/*xxx*/') + PsiWhiteSpace(' ') + JSProperty + PsiElement(JS:IDENTIFIER)('navigation') + PsiElement(JS:COLON)(':') + PsiWhiteSpace(' ') + JSLiteralExpression + PsiElement(JS:STRING_LITERAL)('"111"') + PsiWhiteSpace(' ') + PsiComment(JS:END_OF_LINE_COMMENT)('// yyy') + PsiWhiteSpace('\n') + PsiElement(JS:RBRACE)('}') \ No newline at end of file diff --git a/json/tests/testData/psi/Simple1.json b/json/tests/testData/psi/Simple1.json new file mode 100644 index 000000000000..70afdeb1aa0c --- /dev/null +++ b/json/tests/testData/psi/Simple1.json @@ -0,0 +1 @@ +{ "x": "x value", "y": "y value", "z": "z value", "z2":[] } \ No newline at end of file diff --git a/json/tests/testData/psi/Simple1.txt b/json/tests/testData/psi/Simple1.txt new file mode 100644 index 000000000000..5cc6332ebc9e --- /dev/null +++ b/json/tests/testData/psi/Simple1.txt @@ -0,0 +1,40 @@ +JsonFile: Simple1.json + JsonObject + PsiElement({)('{') + PsiWhiteSpace(' ') + JsonProperty + JsonStringLiteral + PsiElement(DOUBLE_QUOTED_STRING)('"x"') + PsiElement(:)(':') + PsiWhiteSpace(' ') + JsonStringLiteral + PsiElement(DOUBLE_QUOTED_STRING)('"x value"') + PsiElement(,)(',') + PsiWhiteSpace(' ') + JsonProperty + JsonStringLiteral + PsiElement(DOUBLE_QUOTED_STRING)('"y"') + PsiElement(:)(':') + PsiWhiteSpace(' ') + JsonStringLiteral + PsiElement(DOUBLE_QUOTED_STRING)('"y value"') + PsiElement(,)(',') + PsiWhiteSpace(' ') + JsonProperty + JsonStringLiteral + PsiElement(DOUBLE_QUOTED_STRING)('"z"') + PsiElement(:)(':') + PsiWhiteSpace(' ') + JsonStringLiteral + PsiElement(DOUBLE_QUOTED_STRING)('"z value"') + PsiElement(,)(',') + PsiWhiteSpace(' ') + JsonProperty + JsonStringLiteral + PsiElement(DOUBLE_QUOTED_STRING)('"z2"') + PsiElement(:)(':') + JsonArray + PsiElement([)('[') + PsiElement(])(']') + PsiWhiteSpace(' ') + PsiElement(})('}') \ No newline at end of file diff --git a/json/tests/testData/psi/Simple2.json b/json/tests/testData/psi/Simple2.json new file mode 100644 index 000000000000..b78f7d6c255c --- /dev/null +++ b/json/tests/testData/psi/Simple2.json @@ -0,0 +1 @@ +[ "x", 555, null, true, false, "z value", "z2",[] ], \ No newline at end of file diff --git a/json/tests/testData/psi/Simple2.txt b/json/tests/testData/psi/Simple2.txt new file mode 100644 index 000000000000..593cd869d822 --- /dev/null +++ b/json/tests/testData/psi/Simple2.txt @@ -0,0 +1,38 @@ +JsonFile: Simple2.json + JsonArray + PsiElement([)('[') + PsiWhiteSpace(' ') + JsonStringLiteral + PsiElement(DOUBLE_QUOTED_STRING)('"x"') + PsiElement(,)(',') + PsiWhiteSpace(' ') + JsonNumberLiteral + PsiElement(NUMBER)('555') + PsiElement(,)(',') + PsiWhiteSpace(' ') + JsonNullLiteral + PsiElement(null)('null') + PsiElement(,)(',') + PsiWhiteSpace(' ') + JsonBooleanLiteral + PsiElement(true)('true') + PsiElement(,)(',') + PsiWhiteSpace(' ') + JsonBooleanLiteral + PsiElement(false)('false') + PsiElement(,)(',') + PsiWhiteSpace(' ') + JsonStringLiteral + PsiElement(DOUBLE_QUOTED_STRING)('"z value"') + PsiElement(,)(',') + PsiWhiteSpace(' ') + JsonStringLiteral + PsiElement(DOUBLE_QUOTED_STRING)('"z2"') + PsiElement(,)(',') + JsonArray + PsiElement([)('[') + PsiElement(])(']') + PsiWhiteSpace(' ') + PsiElement(])(']') + PsiErrorElement:',' unexpected + PsiElement(,)(',') \ No newline at end of file diff --git a/json/tests/testData/psi/Simple3.json b/json/tests/testData/psi/Simple3.json new file mode 100644 index 000000000000..41ed9553df48 --- /dev/null +++ b/json/tests/testData/psi/Simple3.json @@ -0,0 +1 @@ +[ ++x, "x value", y + "y value", z, "z value", z2,[ a = b], {z=c} ] \ No newline at end of file diff --git a/json/tests/testData/psi/Simple3.txt b/json/tests/testData/psi/Simple3.txt new file mode 100644 index 000000000000..998f99e78233 --- /dev/null +++ b/json/tests/testData/psi/Simple3.txt @@ -0,0 +1,45 @@ +JSFile:Simple3.json + JSArrayLiteralExpression + PsiElement(JS:LBRACKET)('[') + PsiErrorElement:expression expected + + PsiWhiteSpace(' ') + PsiElement(BAD_CHARACTER)('++') + PsiElement(JS:IDENTIFIER)('x') + PsiElement(JS:COMMA)(',') + PsiWhiteSpace(' ') + PsiElement(JS:STRING_LITERAL)('"x value"') + PsiElement(JS:COMMA)(',') + PsiWhiteSpace(' ') + PsiElement(JS:IDENTIFIER)('y') + PsiWhiteSpace(' ') + PsiElement(BAD_CHARACTER)('+') + PsiWhiteSpace(' ') + PsiElement(JS:STRING_LITERAL)('"y value"') + PsiElement(JS:COMMA)(',') + PsiWhiteSpace(' ') + PsiElement(JS:IDENTIFIER)('z') + PsiElement(JS:COMMA)(',') + PsiWhiteSpace(' ') + PsiElement(JS:STRING_LITERAL)('"z value"') + PsiElement(JS:COMMA)(',') + PsiWhiteSpace(' ') + PsiElement(JS:IDENTIFIER)('z2') + PsiElement(JS:COMMA)(',') + PsiElement(JS:LBRACKET)('[') + PsiWhiteSpace(' ') + PsiElement(JS:IDENTIFIER)('a') + PsiWhiteSpace(' ') + PsiElement(BAD_CHARACTER)('=') + PsiWhiteSpace(' ') + PsiElement(JS:IDENTIFIER)('b') + PsiElement(JS:RBRACKET)(']') + PsiElement(JS:COMMA)(',') + PsiWhiteSpace(' ') + PsiElement(JS:LBRACE)('{') + PsiElement(JS:IDENTIFIER)('z') + PsiElement(BAD_CHARACTER)('=') + PsiElement(JS:IDENTIFIER)('c') + PsiElement(JS:RBRACE)('}') + PsiWhiteSpace(' ') + PsiElement(JS:RBRACKET)(']') \ No newline at end of file diff --git a/json/tests/testData/psi/Simple4.json b/json/tests/testData/psi/Simple4.json new file mode 100644 index 000000000000..f32a5804e292 --- /dev/null +++ b/json/tests/testData/psi/Simple4.json @@ -0,0 +1 @@ +true \ No newline at end of file diff --git a/json/tests/testData/psi/Simple4.txt b/json/tests/testData/psi/Simple4.txt new file mode 100644 index 000000000000..477366a0fcdf --- /dev/null +++ b/json/tests/testData/psi/Simple4.txt @@ -0,0 +1,3 @@ +JsonFile: Simple4.json + JsonBooleanLiteral + PsiElement(true)('true') \ No newline at end of file diff --git a/json/tests/testData/psi/StringLiterals.json b/json/tests/testData/psi/StringLiterals.json new file mode 100644 index 000000000000..e55eaff73c3b --- /dev/null +++ b/json/tests/testData/psi/StringLiterals.json @@ -0,0 +1,4 @@ +{ + 'foo': "bar", + "baz": 'quux' +} \ No newline at end of file diff --git a/json/tests/testData/psi/StringLiterals.txt b/json/tests/testData/psi/StringLiterals.txt new file mode 100644 index 000000000000..7fb01d1ecb87 --- /dev/null +++ b/json/tests/testData/psi/StringLiterals.txt @@ -0,0 +1,22 @@ +JsonFile: StringLiterals.json + JsonObject + PsiElement({)('{') + PsiWhiteSpace('\n ') + JsonProperty + JsonStringLiteral + PsiElement(SINGLE_QUOTED_STRING)(''foo'') + PsiElement(:)(':') + PsiWhiteSpace(' ') + JsonStringLiteral + PsiElement(DOUBLE_QUOTED_STRING)('"bar"') + PsiElement(,)(',') + PsiWhiteSpace('\n ') + JsonProperty + JsonStringLiteral + PsiElement(DOUBLE_QUOTED_STRING)('"baz"') + PsiElement(:)(':') + PsiWhiteSpace(' ') + JsonStringLiteral + PsiElement(SINGLE_QUOTED_STRING)(''quux'') + PsiWhiteSpace('\n') + PsiElement(})('}') \ No newline at end of file diff --git a/json/tests/testData/psi/TopLevelStringLiteral.json b/json/tests/testData/psi/TopLevelStringLiteral.json new file mode 100644 index 000000000000..13fea57a234e --- /dev/null +++ b/json/tests/testData/psi/TopLevelStringLiteral.json @@ -0,0 +1 @@ +"should not be here" \ No newline at end of file diff --git a/json/tests/testData/psi/TopLevelStringLiteral.txt b/json/tests/testData/psi/TopLevelStringLiteral.txt new file mode 100644 index 000000000000..ed741c1225c6 --- /dev/null +++ b/json/tests/testData/psi/TopLevelStringLiteral.txt @@ -0,0 +1,3 @@ +JsonFile: TopLevelStringLiteral.json + JsonStringLiteral + PsiElement(DOUBLE_QUOTED_STRING)('"should not be here"') \ No newline at end of file diff --git a/json/tests/testData/psi/TrailingCommas.json b/json/tests/testData/psi/TrailingCommas.json new file mode 100644 index 000000000000..93828866fd5f --- /dev/null +++ b/json/tests/testData/psi/TrailingCommas.json @@ -0,0 +1,3 @@ +{ + "foo": [42,], +} \ No newline at end of file diff --git a/json/tests/testData/psi/TrailingCommas.txt b/json/tests/testData/psi/TrailingCommas.txt new file mode 100644 index 000000000000..402eda96976d --- /dev/null +++ b/json/tests/testData/psi/TrailingCommas.txt @@ -0,0 +1,18 @@ +JsonFile: TrailingCommas.json + JsonObject + PsiElement({)('{') + PsiWhiteSpace('\n ') + JsonProperty + JsonStringLiteral + PsiElement(DOUBLE_QUOTED_STRING)('"foo"') + PsiElement(:)(':') + PsiWhiteSpace(' ') + JsonArray + PsiElement([)('[') + JsonNumberLiteral + PsiElement(NUMBER)('42') + PsiElement(,)(',') + PsiElement(])(']') + PsiElement(,)(',') + PsiWhiteSpace('\n') + PsiElement(})('}') \ No newline at end of file diff --git a/json/tests/testData/quickfix/JSON2.json b/json/tests/testData/quickfix/JSON2.json new file mode 100644 index 000000000000..d97d8c5d229b --- /dev/null +++ b/json/tests/testData/quickfix/JSON2.json @@ -0,0 +1 @@ +{aaa: 222} \ No newline at end of file diff --git a/json/tests/testData/quickfix/JSON2_after.json b/json/tests/testData/quickfix/JSON2_after.json new file mode 100644 index 000000000000..0e66cf666c01 --- /dev/null +++ b/json/tests/testData/quickfix/JSON2_after.json @@ -0,0 +1 @@ +{"aaa": 222} \ No newline at end of file diff --git a/json/tests/testData/quickfix/JSON3.json b/json/tests/testData/quickfix/JSON3.json new file mode 100644 index 000000000000..d3e8cce2fec2 --- /dev/null +++ b/json/tests/testData/quickfix/JSON3.json @@ -0,0 +1 @@ +{"aaa": bbb} \ No newline at end of file diff --git a/json/tests/testData/quickfix/JSON3_after.json b/json/tests/testData/quickfix/JSON3_after.json new file mode 100644 index 000000000000..618b0ca4238f --- /dev/null +++ b/json/tests/testData/quickfix/JSON3_after.json @@ -0,0 +1 @@ +{"aaa": "bbb"} \ No newline at end of file diff --git a/json/tests/testData/rename/DuplicateProperties.json b/json/tests/testData/rename/DuplicateProperties.json new file mode 100644 index 000000000000..2cfba34c01f9 --- /dev/null +++ b/json/tests/testData/rename/DuplicateProperties.json @@ -0,0 +1,4 @@ +{ + "aaa":1, + "aaa":2 +} \ No newline at end of file diff --git a/json/tests/testData/rename/DuplicatePropertiesQuotedName.json b/json/tests/testData/rename/DuplicatePropertiesQuotedName.json new file mode 100644 index 000000000000..2cfba34c01f9 --- /dev/null +++ b/json/tests/testData/rename/DuplicatePropertiesQuotedName.json @@ -0,0 +1,4 @@ +{ + "aaa":1, + "aaa":2 +} \ No newline at end of file diff --git a/json/tests/testData/rename/DuplicatePropertiesQuotedName_after.json b/json/tests/testData/rename/DuplicatePropertiesQuotedName_after.json new file mode 100644 index 000000000000..ab37e1f5cfe6 --- /dev/null +++ b/json/tests/testData/rename/DuplicatePropertiesQuotedName_after.json @@ -0,0 +1,4 @@ +{ + "aaa2":1, + "aaa2":2 +} \ No newline at end of file diff --git a/json/tests/testData/rename/DuplicateProperties_after.json b/json/tests/testData/rename/DuplicateProperties_after.json new file mode 100644 index 000000000000..ab37e1f5cfe6 --- /dev/null +++ b/json/tests/testData/rename/DuplicateProperties_after.json @@ -0,0 +1,4 @@ +{ + "aaa2":1, + "aaa2":2 +} \ No newline at end of file diff --git a/json/tests/testData/rename/PropertyNameRequiresEscaping.json b/json/tests/testData/rename/PropertyNameRequiresEscaping.json new file mode 100644 index 000000000000..bc5b44aeb2c1 --- /dev/null +++ b/json/tests/testData/rename/PropertyNameRequiresEscaping.json @@ -0,0 +1,3 @@ +{ + "foo": null +} \ No newline at end of file diff --git a/json/tests/testData/rename/PropertyNameRequiresEscaping_after.json b/json/tests/testData/rename/PropertyNameRequiresEscaping_after.json new file mode 100644 index 000000000000..255382ffb0c3 --- /dev/null +++ b/json/tests/testData/rename/PropertyNameRequiresEscaping_after.json @@ -0,0 +1,3 @@ +{ + "\"/\\\b\f\n\r\t\u0000\u001B'": null +} \ No newline at end of file diff --git a/json/tests/testData/smartEnter/ColonInsertedAfterPropertyKey.json b/json/tests/testData/smartEnter/ColonInsertedAfterPropertyKey.json new file mode 100644 index 000000000000..d455652f8c66 --- /dev/null +++ b/json/tests/testData/smartEnter/ColonInsertedAfterPropertyKey.json @@ -0,0 +1,3 @@ +{ + "foo" +} \ No newline at end of file diff --git a/json/tests/testData/smartEnter/ColonInsertedAfterPropertyKey_after.json b/json/tests/testData/smartEnter/ColonInsertedAfterPropertyKey_after.json new file mode 100644 index 000000000000..2e3ee074bff2 --- /dev/null +++ b/json/tests/testData/smartEnter/ColonInsertedAfterPropertyKey_after.json @@ -0,0 +1,3 @@ +{ + "foo": +} \ No newline at end of file diff --git a/json/tests/testData/smartEnter/CommaInsertedAfterArrayElement.json b/json/tests/testData/smartEnter/CommaInsertedAfterArrayElement.json new file mode 100644 index 000000000000..1cb4da384b85 --- /dev/null +++ b/json/tests/testData/smartEnter/CommaInsertedAfterArrayElement.json @@ -0,0 +1,3 @@ +[ + 42 +] \ No newline at end of file diff --git a/json/tests/testData/smartEnter/CommaInsertedAfterArrayElement_after.json b/json/tests/testData/smartEnter/CommaInsertedAfterArrayElement_after.json new file mode 100644 index 000000000000..c3fd82625e27 --- /dev/null +++ b/json/tests/testData/smartEnter/CommaInsertedAfterArrayElement_after.json @@ -0,0 +1,4 @@ +[ + 42, + +] \ No newline at end of file diff --git a/json/tests/testData/smartEnter/CommaInsertedAfterProperty.json b/json/tests/testData/smartEnter/CommaInsertedAfterProperty.json new file mode 100644 index 000000000000..6484206f1ed1 --- /dev/null +++ b/json/tests/testData/smartEnter/CommaInsertedAfterProperty.json @@ -0,0 +1,3 @@ +{ + "foo": 42 +} \ No newline at end of file diff --git a/json/tests/testData/smartEnter/CommaInsertedAfterPropertyWithMultilineValue.json b/json/tests/testData/smartEnter/CommaInsertedAfterPropertyWithMultilineValue.json new file mode 100644 index 000000000000..4f520847d143 --- /dev/null +++ b/json/tests/testData/smartEnter/CommaInsertedAfterPropertyWithMultilineValue.json @@ -0,0 +1,7 @@ +{ + "foo": [ + 1, + 2, + 3 + ] +} \ No newline at end of file diff --git a/json/tests/testData/smartEnter/CommaInsertedAfterPropertyWithMultilineValue_after.json b/json/tests/testData/smartEnter/CommaInsertedAfterPropertyWithMultilineValue_after.json new file mode 100644 index 000000000000..f9a951b655f8 --- /dev/null +++ b/json/tests/testData/smartEnter/CommaInsertedAfterPropertyWithMultilineValue_after.json @@ -0,0 +1,8 @@ +{ + "foo": [ + 1, + 2, + 3 + ], + +} \ No newline at end of file diff --git a/json/tests/testData/smartEnter/CommaInsertedAfterProperty_after.json b/json/tests/testData/smartEnter/CommaInsertedAfterProperty_after.json new file mode 100644 index 000000000000..65f93e6709eb --- /dev/null +++ b/json/tests/testData/smartEnter/CommaInsertedAfterProperty_after.json @@ -0,0 +1,4 @@ +{ + "foo": 42, + +} \ No newline at end of file diff --git a/json/tests/testData/structureView/SimpleStructure.json b/json/tests/testData/structureView/SimpleStructure.json new file mode 100644 index 000000000000..3bf40b22337b --- /dev/null +++ b/json/tests/testData/structureView/SimpleStructure.json @@ -0,0 +1,6 @@ +{ + "aaa":1, + "bbb":{ + "ccc":2 + } +} \ No newline at end of file diff --git a/json/tests/testData/surround/CannotSurroundPropertyKey.json b/json/tests/testData/surround/CannotSurroundPropertyKey.json new file mode 100644 index 000000000000..b98cca877c6a --- /dev/null +++ b/json/tests/testData/surround/CannotSurroundPropertyKey.json @@ -0,0 +1,3 @@ +{ + "foo": false +} \ No newline at end of file diff --git a/json/tests/testData/surround/CannotSurroundPropertyKey_after.json b/json/tests/testData/surround/CannotSurroundPropertyKey_after.json new file mode 100644 index 000000000000..6e101319e899 --- /dev/null +++ b/json/tests/testData/surround/CannotSurroundPropertyKey_after.json @@ -0,0 +1,3 @@ +{ + "foo": false +} \ No newline at end of file diff --git a/json/tests/testData/surround/MultipleProperties.json b/json/tests/testData/surround/MultipleProperties.json new file mode 100644 index 000000000000..7cc748055a5d --- /dev/null +++ b/json/tests/testData/surround/MultipleProperties.json @@ -0,0 +1,4 @@ +{ + "foo": null, + "bar": false +} \ No newline at end of file diff --git a/json/tests/testData/surround/MultipleProperties_after.json b/json/tests/testData/surround/MultipleProperties_after.json new file mode 100644 index 000000000000..3a16bfb3c7ee --- /dev/null +++ b/json/tests/testData/surround/MultipleProperties_after.json @@ -0,0 +1,6 @@ +{ + "property": { + "foo": null, + "bar": false + } +} \ No newline at end of file diff --git a/json/tests/testData/surround/ObjectLiteral.json b/json/tests/testData/surround/ObjectLiteral.json new file mode 100644 index 000000000000..c00208c2ccee --- /dev/null +++ b/json/tests/testData/surround/ObjectLiteral.json @@ -0,0 +1,7 @@ +{ + "main":{ + "id": 'burndown', + "title": 'Burndown', + "xtype": 'app.widgets.BurndownDiagram' + } +} diff --git a/json/tests/testData/surround/ObjectLiteral_after.json b/json/tests/testData/surround/ObjectLiteral_after.json new file mode 100644 index 000000000000..06903a8e9f03 --- /dev/null +++ b/json/tests/testData/surround/ObjectLiteral_after.json @@ -0,0 +1,9 @@ +{ + "main": { + "property": { + "id": 'burndown', + "title": 'Burndown', + "xtype": 'app.widgets.BurndownDiagram' + } + } +} diff --git a/json/tests/testData/surround/SingleProperty.json b/json/tests/testData/surround/SingleProperty.json new file mode 100644 index 000000000000..f9e3dfdb6575 --- /dev/null +++ b/json/tests/testData/surround/SingleProperty.json @@ -0,0 +1,3 @@ +{ + "foo": null +} \ No newline at end of file diff --git a/json/tests/testData/surround/SingleProperty_after.json b/json/tests/testData/surround/SingleProperty_after.json new file mode 100644 index 000000000000..019436aa8090 --- /dev/null +++ b/json/tests/testData/surround/SingleProperty_after.json @@ -0,0 +1,5 @@ +{ + "property": { + "foo": null + } +} \ No newline at end of file diff --git a/json/tests/testData/surround/SingleValue.json b/json/tests/testData/surround/SingleValue.json new file mode 100644 index 000000000000..500db4a86aa3 --- /dev/null +++ b/json/tests/testData/surround/SingleValue.json @@ -0,0 +1 @@ +[null] \ No newline at end of file diff --git a/json/tests/testData/surround/SingleValue_after.json b/json/tests/testData/surround/SingleValue_after.json new file mode 100644 index 000000000000..440c9ce72ee7 --- /dev/null +++ b/json/tests/testData/surround/SingleValue_after.json @@ -0,0 +1,3 @@ +{ + "property": [null] +} \ No newline at end of file diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/RemoteExternalSystemCommunicationManager.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/RemoteExternalSystemCommunicationManager.java index e17d2443da61..83d2359ff498 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/RemoteExternalSystemCommunicationManager.java +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/RemoteExternalSystemCommunicationManager.java @@ -159,7 +159,7 @@ public class RemoteExternalSystemCommunicationManager implements ExternalSystemC if (externalSystemId != null) { ExternalSystemManager manager = ExternalSystemApiUtil.getManager(externalSystemId); if (manager != null) { - params.getClassPath().add(PathUtil.getJarPathForClass(manager.getProjectResolverClass().getClass())); + params.getClassPath().add(PathUtil.getJarPathForClass(manager.getProjectResolverClass())); params.getProgramParametersList().add(manager.getProjectResolverClass().getName()); params.getProgramParametersList().add(manager.getTaskManagerClass().getName()); manager.enhanceRemoteProcessing(params); diff --git a/platform/lang-impl/src/com/intellij/execution/impl/ConsoleViewImpl.java b/platform/lang-impl/src/com/intellij/execution/impl/ConsoleViewImpl.java index ae5cfe829f6c..c20c8195ac56 100644 --- a/platform/lang-impl/src/com/intellij/execution/impl/ConsoleViewImpl.java +++ b/platform/lang-impl/src/com/intellij/execution/impl/ConsoleViewImpl.java @@ -1085,9 +1085,6 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo }; if (immediately) { model.runBatchFoldingOperation(operation); - if (isCaretAtLastLine()) { - EditorUtil.scrollToTheEnd(myEditor); - } } else { model.runBatchFoldingOperationDoNotCollapseCaret(operation); diff --git a/platform/lang-impl/src/com/intellij/psi/codeStyle/autodetect/IndentOptionsDetectorImpl.java b/platform/lang-impl/src/com/intellij/psi/codeStyle/autodetect/IndentOptionsDetectorImpl.java index f4972cda633c..b5d5d40ce190 100644 --- a/platform/lang-impl/src/com/intellij/psi/codeStyle/autodetect/IndentOptionsDetectorImpl.java +++ b/platform/lang-impl/src/com/intellij/psi/codeStyle/autodetect/IndentOptionsDetectorImpl.java @@ -31,7 +31,6 @@ public class IndentOptionsDetectorImpl implements IndentOptionsDetector { private static Logger LOG = Logger.getInstance("#com.intellij.psi.codeStyle.CommonCodeStyleSettings.IndentOptionsDetector"); private static final double RATE_THRESHOLD = 0.8; - private static final int MIN_LINES_THRESHOLD = 20; private static final int MAX_INDENT_TO_DETECT = 8; private final PsiFile myFile; @@ -68,10 +67,14 @@ public class IndentOptionsDetectorImpl implements IndentOptionsDetector { LOG.info("Detected tab usage in" + myFile); } } - else if (linesWithWhiteSpaceIndent > MIN_LINES_THRESHOLD) { + else { + if (indentOptions.USE_TAB_CHARACTER) { + indentOptions.USE_TAB_CHARACTER = false; + LOG.info("No tab usage detected " + myFile); + } + int newIndentSize = getPositiveIndentSize(stats); if (newIndentSize > 0) { - indentOptions.USE_TAB_CHARACTER = false; if (indentOptions.INDENT_SIZE != newIndentSize) { indentOptions.INDENT_SIZE = newIndentSize; LOG.info("Detected indent size: " + newIndentSize + " for file " + myFile); diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java index 37fd93bc601a..00ed3d054e9e 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java @@ -307,6 +307,13 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi private CaretImpl myPrimaryCaret; + private final TIntFunction myLineNumberAreaWidthFunction = new TIntFunction() { + @Override + public int execute(int lineNumber) { + return getFontMetrics(Font.PLAIN).stringWidth(Integer.toString(lineNumber + 2)) + 6; + } + }; + static { ourCaretBlinkingCommand.start(); } @@ -510,6 +517,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi setFontSize(UISettings.getInstance().PRESENTATION_MODE_FONT_SIZE); } + myGutterComponent.setLineNumberAreaWidth(myLineNumberAreaWidthFunction); myGutterComponent.updateSize(); Dimension preferredSize = getPreferredSize(); myEditorComponent.setSize(preferredSize); @@ -1810,12 +1818,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi } myPreferredSize = dim; - myGutterComponent.setLineNumberAreaWidth(new TIntFunction() { - @Override - public int execute(int lineNumber) { - return getFontMetrics(Font.PLAIN).stringWidth(Integer.toString(lineNumber + 2)) + 6; - } - }); + myGutterComponent.setLineNumberAreaWidth(myLineNumberAreaWidthFunction); myGutterComponent.updateSize(); myEditorComponent.setSize(dim); diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/FoldingModelImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/FoldingModelImpl.java index b92970425e99..15946220c5fe 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/FoldingModelImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/FoldingModelImpl.java @@ -64,7 +64,7 @@ public class FoldingModelImpl implements FoldingModelEx, PrioritizedDocumentList private int mySavedCaretX; private int mySavedCaretY; - private int mySavedCaretPositionBeforeBatchFolding; + private int mySavedCaretShift; private boolean myCaretPositionSaved; private final MultiMap myGroups = new MultiMap(); private boolean myDocumentChangeProcessed = true; @@ -205,7 +205,8 @@ public class FoldingModelImpl implements FoldingModelEx, PrioritizedDocumentList myDoNotCollapseCaret |= dontCollapseCaret; boolean oldBatchFlag = myIsBatchFoldingProcessing; if (!oldBatchFlag) { - mySavedCaretPositionBeforeBatchFolding = myEditor.visibleLineToY(myEditor.getCaretModel().getVisualPosition().line); + ((ScrollingModelImpl)myEditor.getScrollingModel()).finishAnimation(); + mySavedCaretShift = myEditor.visibleLineToY(myEditor.getCaretModel().getVisualPosition().line) - myEditor.getScrollingModel().getVerticalScrollOffset(); } myIsBatchFoldingProcessing = true; @@ -236,7 +237,7 @@ public class FoldingModelImpl implements FoldingModelEx, PrioritizedDocumentList * Should be called from inside batch operation runnable. */ public void flushCaretShift() { - mySavedCaretPositionBeforeBatchFolding = -1; + mySavedCaretShift = -1; } @Override @@ -429,18 +430,11 @@ public class FoldingModelImpl implements FoldingModelEx, PrioritizedDocumentList myEditor.getSelectionModel().setSelection(selectionStart, selectionEnd); } - if (mySavedCaretPositionBeforeBatchFolding >= 0) { - final int offset = myEditor.visibleLineToY(myEditor.getCaretModel().getVisualPosition().line) - mySavedCaretPositionBeforeBatchFolding; + if (mySavedCaretShift > 0) { final ScrollingModel scrollingModel = myEditor.getScrollingModel(); - scrollingModel.runActionOnScrollingFinished(new Runnable() { - @Override - public void run() { - scrollingModel.disableAnimation(); - int pos = scrollingModel.getVerticalScrollOffset(); - scrollingModel.scrollVertically(pos + offset); - scrollingModel.enableAnimation(); - } - }); + scrollingModel.disableAnimation(); + scrollingModel.scrollVertically(myEditor.visibleLineToY(myEditor.getCaretModel().getVisualPosition().line) - mySavedCaretShift); + scrollingModel.enableAnimation(); } } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/ScrollingModelImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/ScrollingModelImpl.java index 2942bafcc7d5..029d6742b4e8 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/ScrollingModelImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/ScrollingModelImpl.java @@ -376,7 +376,7 @@ public class ScrollingModelImpl implements ScrollingModelEx { LOG.assertTrue(success); } - public void commandStarted() { + public void finishAnimation() { cancelAnimatedScrolling(true); } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/VisibleEditorsTracker.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/VisibleEditorsTracker.java index bacbf42ec614..5cbdb35b3eaf 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/VisibleEditorsTracker.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/VisibleEditorsTracker.java @@ -54,7 +54,7 @@ public class VisibleEditorsTracker extends CommandAdapter { myEditorsVisibleOnCommandStart.add(editor); } - ((ScrollingModelImpl)editor.getScrollingModel()).commandStarted(); + ((ScrollingModelImpl)editor.getScrollingModel()).finishAnimation(); myCurrentCommandStart = System.currentTimeMillis(); } } diff --git a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/AbstractUpdateDialog.java b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/AbstractUpdateDialog.java index 9fba509bf356..7d5456dd53b3 100644 --- a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/AbstractUpdateDialog.java +++ b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/AbstractUpdateDialog.java @@ -113,7 +113,7 @@ public abstract class AbstractUpdateDialog extends DialogWrapper { UIUtil.getCssFontDeclaration(UIUtil.getLabelFont(), fontColor, null, null) + "" + "" + - messageBody + + (myEnableLink ? messageBody : "") + ""; area.setBackground(UIUtil.getPanelBackground()); diff --git a/platform/platform-resources/src/META-INF/JsonPlugin.xml b/platform/platform-resources/src/META-INF/JsonPlugin.xml new file mode 100644 index 000000000000..abb8bae4e7f4 --- /dev/null +++ b/platform/platform-resources/src/META-INF/JsonPlugin.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + com.intellij.json.JsonBundle + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/platform/platform-tests/testData/codeStyle/autodetect/smallFileWithIndent8.java b/platform/platform-tests/testData/codeStyle/autodetect/smallFileWithIndent8.java new file mode 100644 index 000000000000..e20d0ffce1b7 --- /dev/null +++ b/platform/platform-tests/testData/codeStyle/autodetect/smallFileWithIndent8.java @@ -0,0 +1,6 @@ +public void Test { + + public void run() { + } + +} \ No newline at end of file diff --git a/platform/platform-tests/testData/codeStyle/autodetect/smallFileWithTabs.java b/platform/platform-tests/testData/codeStyle/autodetect/smallFileWithTabs.java new file mode 100644 index 000000000000..574df5794fdf --- /dev/null +++ b/platform/platform-tests/testData/codeStyle/autodetect/smallFileWithTabs.java @@ -0,0 +1,5 @@ +public void TabTest { + + public void run() {} + +} \ No newline at end of file diff --git a/platform/platform-tests/testSrc/com/intellij/psi/autodetect/IndentAutoDetectionTest.java b/platform/platform-tests/testSrc/com/intellij/psi/autodetect/IndentAutoDetectionTest.java index 2ff3ede87117..d22db1104340 100644 --- a/platform/platform-tests/testSrc/com/intellij/psi/autodetect/IndentAutoDetectionTest.java +++ b/platform/platform-tests/testSrc/com/intellij/psi/autodetect/IndentAutoDetectionTest.java @@ -62,6 +62,14 @@ public class IndentAutoDetectionTest extends LightPlatformCodeInsightTestCase { doTestMaxUsedIndent(2); } + public void testSmallFileWithIndent8() { + doTestMaxUsedIndent(8); + } + + public void testSmallFileWithTabs() { + doTestTabsUsed(); + } + public void testSpacesToNumbers() throws Exception { String text = " i\n" + " a\n" + diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/evaluate/quick/XValueHint.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/evaluate/quick/XValueHint.java index 37b641e1042e..e959bd87c711 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/evaluate/quick/XValueHint.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/evaluate/quick/XValueHint.java @@ -15,6 +15,7 @@ */ package com.intellij.xdebugger.impl.evaluate.quick; +import com.intellij.codeInsight.hint.HintManager; import com.intellij.codeInsight.hint.HintUtil; import com.intellij.execution.console.LanguageConsoleImpl; import com.intellij.execution.console.LanguageConsoleView; @@ -24,6 +25,7 @@ import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.ActionManager; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.ShortcutSet; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.fileEditor.FileDocumentManager; @@ -204,6 +206,15 @@ public class XValueHint extends AbstractValueHint { @Override public void errorOccurred(@NotNull final String errorMessage) { + ApplicationManager.getApplication().invokeLater(new Runnable() { + @Override + public void run() { + HintManager.getInstance().showErrorHint(getEditor(), errorMessage, getCurrentRange().getStartOffset(), + getCurrentRange().getEndOffset(), HintManager.ABOVE, + HintManager.HIDE_BY_ESCAPE | HintManager.HIDE_BY_TEXT_CHANGE, + 0); + } + }); LOG.debug("Cannot evaluate '" + myExpression + "':" + errorMessage); } }, myExpressionPosition); diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/util/GroovyPropertyUtils.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/util/GroovyPropertyUtils.java index 15765bbd1a15..48bee8fe3807 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/util/GroovyPropertyUtils.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/util/GroovyPropertyUtils.java @@ -557,12 +557,12 @@ public class GroovyPropertyUtils { private static void annotateWithNullableStuff(final PsiModifierListOwner field, final PsiModifierListOwner listOwner) throws IncorrectOperationException { final NullableNotNullManager manager = NullableNotNullManager.getInstance(field.getProject()); - final String notNull = manager.getNotNull(field); + final PsiAnnotation notNull = manager.copyNotNullAnnotation(field); if (notNull != null) { annotate(listOwner, notNull); } else { - final String nullable = manager.getNullable(field); + final PsiAnnotation nullable = manager.copyNullableAnnotation(field); if (nullable != null) { annotate(listOwner, nullable); } @@ -577,11 +577,11 @@ public class GroovyPropertyUtils { } } - private static void annotate(final PsiModifierListOwner listOwner, final String annotationQName) + private static void annotate(final PsiModifierListOwner listOwner, final PsiAnnotation annotation) throws IncorrectOperationException { final PsiModifierList modifierList = listOwner.getModifierList(); LOG.assertTrue(modifierList != null); - modifierList.addAnnotation(annotationQName); + modifierList.addAnnotation(annotation.getQualifiedName()); } diff --git a/resources/src/idea/RichPlatformPlugin.xml b/resources/src/idea/RichPlatformPlugin.xml index 3f2971e21570..e548b08e5d66 100644 --- a/resources/src/idea/RichPlatformPlugin.xml +++ b/resources/src/idea/RichPlatformPlugin.xml @@ -396,6 +396,7 @@ + diff --git a/tools/lexer/jflex-1.4/bin/jflex b/tools/lexer/jflex-1.4/bin/jflex index d6ad789e2490..65cb93efbbbb 100755 --- a/tools/lexer/jflex-1.4/bin/jflex +++ b/tools/lexer/jflex-1.4/bin/jflex @@ -1,4 +1,4 @@ -#! /bin/bash +#!/bin/bash # # JFlex start script $Revision$ #