Merge remote-tracking branch 'origin/json-support'
authorMikhail Golubev <mikhail.golubev@jetbrains.com>
Tue, 19 Aug 2014 09:16:27 +0000 (13:16 +0400)
committerMikhail Golubev <mikhail.golubev@jetbrains.com>
Tue, 19 Aug 2014 09:16:27 +0000 (13:16 +0400)
Conflicts:
.idea/modules.xml

77 files changed:
.idea/modules.xml
community-main.iml
plugins/json-tests/json-tests.iml [new file with mode: 0644]
plugins/json-tests/test/com/intellij/json/JsonCompletionTest.java [new file with mode: 0644]
plugins/json-tests/test/com/intellij/json/JsonFormattingTest.java [new file with mode: 0644]
plugins/json-tests/test/com/intellij/json/JsonParsingTest.java [new file with mode: 0644]
plugins/json-tests/testData/completion/InsideArrayElement1.json [new file with mode: 0644]
plugins/json-tests/testData/completion/InsideArrayElement2.json [new file with mode: 0644]
plugins/json-tests/testData/completion/InsidePropertyKey1.json [new file with mode: 0644]
plugins/json-tests/testData/completion/InsidePropertyKey2.json [new file with mode: 0644]
plugins/json-tests/testData/completion/InsidePropertyValue.json [new file with mode: 0644]
plugins/json-tests/testData/formatting/BlankLinesStripping.json [new file with mode: 0644]
plugins/json-tests/testData/formatting/BlankLinesStripping_after.json [new file with mode: 0644]
plugins/json-tests/testData/formatting/ContainerElementsAlignment.json [new file with mode: 0644]
plugins/json-tests/testData/formatting/ContainerElementsAlignment_after.json [new file with mode: 0644]
plugins/json-tests/testData/formatting/SpacesInsertion.json [new file with mode: 0644]
plugins/json-tests/testData/formatting/SpacesInsertion_after.json [new file with mode: 0644]
plugins/json-tests/testData/formatting/Wrapping.json [new file with mode: 0644]
plugins/json-tests/testData/formatting/Wrapping_after.json [new file with mode: 0644]
plugins/json-tests/testData/psi/Keywords.json [new file with mode: 0644]
plugins/json-tests/testData/psi/Keywords.txt [new file with mode: 0644]
plugins/json-tests/testData/psi/NestedArrayLiterals.json [new file with mode: 0644]
plugins/json-tests/testData/psi/NestedArrayLiterals.txt [new file with mode: 0644]
plugins/json-tests/testData/psi/NestedObjectLiterals.json [new file with mode: 0644]
plugins/json-tests/testData/psi/NestedObjectLiterals.txt [new file with mode: 0644]
plugins/json-tests/testData/psi/TopLevelStringLiteral.json [new file with mode: 0644]
plugins/json-tests/testData/psi/TopLevelStringLiteral.txt [new file with mode: 0644]
plugins/json/gen/com/intellij/json/JsonElementTypes.java [new file with mode: 0644]
plugins/json/gen/com/intellij/json/JsonLexer.java [new file with mode: 0644]
plugins/json/gen/com/intellij/json/JsonParser.java [new file with mode: 0644]
plugins/json/gen/com/intellij/json/JsonParserUtil.java [new file with mode: 0644]
plugins/json/gen/com/intellij/json/_JsonLexer.flex [new file with mode: 0644]
plugins/json/gen/com/intellij/json/_JsonLexer.java [new file with mode: 0644]
plugins/json/gen/com/intellij/json/psi/JsonArray.java [new file with mode: 0644]
plugins/json/gen/com/intellij/json/psi/JsonBooleanLiteral.java [new file with mode: 0644]
plugins/json/gen/com/intellij/json/psi/JsonLiteral.java [new file with mode: 0644]
plugins/json/gen/com/intellij/json/psi/JsonNullLiteral.java [new file with mode: 0644]
plugins/json/gen/com/intellij/json/psi/JsonNumberLiteral.java [new file with mode: 0644]
plugins/json/gen/com/intellij/json/psi/JsonObject.java [new file with mode: 0644]
plugins/json/gen/com/intellij/json/psi/JsonProperty.java [new file with mode: 0644]
plugins/json/gen/com/intellij/json/psi/JsonPropertyName.java [new file with mode: 0644]
plugins/json/gen/com/intellij/json/psi/JsonStringLiteral.java [new file with mode: 0644]
plugins/json/gen/com/intellij/json/psi/JsonValue.java [new file with mode: 0644]
plugins/json/gen/com/intellij/json/psi/JsonVisitor.java [new file with mode: 0644]
plugins/json/gen/com/intellij/json/psi/impl/JsonArrayImpl.java [new file with mode: 0644]
plugins/json/gen/com/intellij/json/psi/impl/JsonBooleanLiteralImpl.java [new file with mode: 0644]
plugins/json/gen/com/intellij/json/psi/impl/JsonLiteralImpl.java [new file with mode: 0644]
plugins/json/gen/com/intellij/json/psi/impl/JsonNullLiteralImpl.java [new file with mode: 0644]
plugins/json/gen/com/intellij/json/psi/impl/JsonNumberLiteralImpl.java [new file with mode: 0644]
plugins/json/gen/com/intellij/json/psi/impl/JsonObjectImpl.java [new file with mode: 0644]
plugins/json/gen/com/intellij/json/psi/impl/JsonPropertyImpl.java [new file with mode: 0644]
plugins/json/gen/com/intellij/json/psi/impl/JsonPropertyNameImpl.java [new file with mode: 0644]
plugins/json/gen/com/intellij/json/psi/impl/JsonStringLiteralImpl.java [new file with mode: 0644]
plugins/json/gen/com/intellij/json/psi/impl/JsonValueImpl.java [new file with mode: 0644]
plugins/json/json.iml [new file with mode: 0644]
plugins/json/src/META-INF/plugin.xml [new file with mode: 0644]
plugins/json/src/com/intellij/json/JsonBraceMatcher.java [new file with mode: 0644]
plugins/json/src/com/intellij/json/JsonElementType.java [new file with mode: 0644]
plugins/json/src/com/intellij/json/JsonFile.java [new file with mode: 0644]
plugins/json/src/com/intellij/json/JsonFileType.java [new file with mode: 0644]
plugins/json/src/com/intellij/json/JsonFileTypeFactory.java [new file with mode: 0644]
plugins/json/src/com/intellij/json/JsonLanguage.java [new file with mode: 0644]
plugins/json/src/com/intellij/json/JsonParserDefinition.java [new file with mode: 0644]
plugins/json/src/com/intellij/json/JsonQuoteHandler.java [new file with mode: 0644]
plugins/json/src/com/intellij/json/JsonSyntaxHighlighterFactory.java [new file with mode: 0644]
plugins/json/src/com/intellij/json/JsonTokenType.java [new file with mode: 0644]
plugins/json/src/com/intellij/json/breadcrumbs/JsonBreadcrumbsProvider.java [new file with mode: 0644]
plugins/json/src/com/intellij/json/codeinsight/JsonCompletionContributor.java [new file with mode: 0644]
plugins/json/src/com/intellij/json/codeinsight/JsonStringLiteralAnnotator.java [new file with mode: 0644]
plugins/json/src/com/intellij/json/formatter/JsonBlock.java [new file with mode: 0644]
plugins/json/src/com/intellij/json/formatter/JsonCodeStyleSettings.java [new file with mode: 0644]
plugins/json/src/com/intellij/json/formatter/JsonCodeStyleSettingsProvider.java [new file with mode: 0644]
plugins/json/src/com/intellij/json/formatter/JsonEnterBetweenBracesHandler.java [new file with mode: 0644]
plugins/json/src/com/intellij/json/formatter/JsonFormattingBuilderModel.java [new file with mode: 0644]
plugins/json/src/com/intellij/json/formatter/JsonLanguageCodeStyleSettingsProvider.java [new file with mode: 0644]
plugins/json/src/com/intellij/json/psi/JsonPsiImplUtils.java [new file with mode: 0644]
plugins/json/src/json.bnf [new file with mode: 0644]

index 0322352e9df9763deb9509575d8ae0b4bdea89d4..94f45924b26ede1fec75f830bcd19dca8117512f 100644 (file)
       <module fileurl="file://$PROJECT_DIR$/jps/plugin-system/jps-plugin-system.iml" filepath="$PROJECT_DIR$/jps/plugin-system/jps-plugin-system.iml" group="jps" />
       <module fileurl="file://$PROJECT_DIR$/jps/model-serialization/jps-serialization-tests.iml" filepath="$PROJECT_DIR$/jps/model-serialization/jps-serialization-tests.iml" group="jps" />
       <module fileurl="file://$PROJECT_DIR$/jps/standalone-builder/jps-standalone-builder.iml" filepath="$PROJECT_DIR$/jps/standalone-builder/jps-standalone-builder.iml" group="jps" />
+      <module fileurl="file://$PROJECT_DIR$/plugins/json/json.iml" filepath="$PROJECT_DIR$/plugins/json/json.iml" group="plugins" />
+      <module fileurl="file://$PROJECT_DIR$/plugins/json-tests/json-tests.iml" filepath="$PROJECT_DIR$/plugins/json-tests/json-tests.iml" group="plugins" />
       <module fileurl="file://$PROJECT_DIR$/java/jsp-base-openapi/jsp-base-openapi.iml" filepath="$PROJECT_DIR$/java/jsp-base-openapi/jsp-base-openapi.iml" group="java" />
       <module fileurl="file://$PROJECT_DIR$/java/jsp-openapi/jsp-openapi.iml" filepath="$PROJECT_DIR$/java/jsp-openapi/jsp-openapi.iml" group="java" />
       <module fileurl="file://$PROJECT_DIR$/java/jsp-spi/jsp-spi.iml" filepath="$PROJECT_DIR$/java/jsp-spi/jsp-spi.iml" group="java" />
index 51f709d431a13a93a3210bde2a32896875d0f93f..6d76dc43b6853730733f51ad274dcdf27c41dc92 100644 (file)
     <orderEntry type="module" module-name="structuralsearch-java" />
     <orderEntry type="module" module-name="structuralsearch-tests" scope="TEST" />
     <orderEntry type="module" module-name="structuralsearch-groovy" />
+    <orderEntry type="module" module-name="json" />
+    <orderEntry type="module" module-name="json-tests" scope="TEST" />
   </component>
 </module>
 
diff --git a/plugins/json-tests/json-tests.iml b/plugins/json-tests/json-tests.iml
new file mode 100644 (file)
index 0000000..b399fc3
--- /dev/null
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+  <component name="NewModuleRootManager" inherit-compiler-output="true">
+    <exclude-output />
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="module" module-name="testFramework" scope="TEST" />
+    <orderEntry type="module" module-name="json" scope="TEST" />
+    <orderEntry type="module" module-name="testFramework-java" scope="TEST" />
+  </component>
+</module>
+
diff --git a/plugins/json-tests/test/com/intellij/json/JsonCompletionTest.java b/plugins/json-tests/test/com/intellij/json/JsonCompletionTest.java
new file mode 100644 (file)
index 0000000..94a1ab7
--- /dev/null
@@ -0,0 +1,54 @@
+package com.intellij.json;
+
+import com.intellij.testFramework.IdeaTestCase;
+import com.intellij.testFramework.fixtures.CodeInsightFixtureTestCase;
+import com.intellij.util.ArrayUtil;
+
+/**
+ * @author Mikhail Golubev
+ */
+public class JsonCompletionTest extends CodeInsightFixtureTestCase {
+  private static final String[] KEYWORDS = new String[]{"true", "false", "null"};
+  private static final String[] NOTHING = ArrayUtil.EMPTY_STRING_ARRAY;
+
+  @Override
+  public void setUp() throws Exception {
+    IdeaTestCase.initPlatformPrefix();
+    super.setUp();
+  }
+  @Override
+  protected String getBasePath() {
+    return "/plugins/json-tests/testData/completion";
+  }
+
+
+
+  @Override
+  protected boolean isCommunity() {
+    return true;
+  }
+
+  public void testInsideArrayElement1() throws Exception {
+    doTest(KEYWORDS);
+  }
+
+  public void testInsideArrayElement2() throws Exception {
+    doTest(KEYWORDS);
+  }
+
+  public void testInsidePropertyKey1() throws Exception {
+    doTest(NOTHING);
+  }
+
+  public void testInsidePropertyKey2() throws Exception {
+    doTest(NOTHING);
+  }
+
+  public void testInsidePropertyValue() throws Exception {
+    doTest(KEYWORDS);
+  }
+
+  private void doTest(String... variants) {
+    myFixture.testCompletionVariants(getTestName(false) + ".json", variants);
+  }
+}
diff --git a/plugins/json-tests/test/com/intellij/json/JsonFormattingTest.java b/plugins/json-tests/test/com/intellij/json/JsonFormattingTest.java
new file mode 100644 (file)
index 0000000..81b7570
--- /dev/null
@@ -0,0 +1,62 @@
+package com.intellij.json;
+
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.psi.codeStyle.CodeStyleSettings;
+import com.intellij.psi.formatter.FormatterTestCase;
+import com.intellij.testFramework.IdeaTestCase;
+import com.intellij.testFramework.PlatformTestUtil;
+
+/**
+ * @author Mikhail Golubev
+ */
+public class JsonFormattingTest extends FormatterTestCase {
+  private static final Logger LOG = Logger.getInstance(JsonFormattingTest.class);
+
+  @Override
+  protected void setUp() throws Exception {
+    IdeaTestCase.initPlatformPrefix();
+    super.setUp();
+  }
+
+  @Override
+  protected String getBasePath() {
+    return "formatting";
+  }
+
+  // Why the heck do I specify path to testData in each kind of test differently?
+  @Override
+  protected String getTestDataPath() {
+    return PlatformTestUtil.getCommunityPath() + "/plugins/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 {
+    CodeStyleSettings settings = getSettings();
+    int indentSize = settings.getCommonSettings(JsonLanguage.INSTANCE).getIndentOptions().INDENT_SIZE;
+//    LOG.debug("Intend size for JSON: " + indentSize);
+    settings.RIGHT_MARGIN = 20;
+    doTest();
+  }
+
+  @Override
+  protected String getTestName(boolean ignored) {
+    // always use uppercase first letter for consistency
+    return super.getTestName(false);
+  }
+}
diff --git a/plugins/json-tests/test/com/intellij/json/JsonParsingTest.java b/plugins/json-tests/test/com/intellij/json/JsonParsingTest.java
new file mode 100644 (file)
index 0000000..b7d374b
--- /dev/null
@@ -0,0 +1,38 @@
+package com.intellij.json;
+
+import com.intellij.testFramework.ParsingTestCase;
+import com.intellij.testFramework.PlatformTestUtil;
+
+/**
+ * @author Mikhail Golubev
+ */
+public class JsonParsingTest extends ParsingTestCase {
+  public JsonParsingTest() {
+    super("psi", "json", new JsonParserDefinition());
+  }
+
+  public void testKeywords() throws Exception {
+    doTest();
+  }
+
+  public void testNestedArrayLiterals() throws Exception {
+    doTest();
+  }
+
+  public void testNestedObjectLiterals() throws Exception {
+    doTest();
+  }
+
+  public void testTopLevelStringLiteral() throws Exception {
+    doTest();
+  }
+
+  private void doTest() {
+    doTest(true);
+  }
+
+  @Override
+  protected String getTestDataPath() {
+    return PlatformTestUtil.getCommunityPath() + "/plugins/json-tests/testData";
+  }
+}
diff --git a/plugins/json-tests/testData/completion/InsideArrayElement1.json b/plugins/json-tests/testData/completion/InsideArrayElement1.json
new file mode 100644 (file)
index 0000000..646916b
--- /dev/null
@@ -0,0 +1 @@
+[<caret>]
\ No newline at end of file
diff --git a/plugins/json-tests/testData/completion/InsideArrayElement2.json b/plugins/json-tests/testData/completion/InsideArrayElement2.json
new file mode 100644 (file)
index 0000000..26f9f8d
--- /dev/null
@@ -0,0 +1 @@
+[1, <caret>]
\ No newline at end of file
diff --git a/plugins/json-tests/testData/completion/InsidePropertyKey1.json b/plugins/json-tests/testData/completion/InsidePropertyKey1.json
new file mode 100644 (file)
index 0000000..1c6ca65
--- /dev/null
@@ -0,0 +1 @@
+{<caret>: "foo"}
\ No newline at end of file
diff --git a/plugins/json-tests/testData/completion/InsidePropertyKey2.json b/plugins/json-tests/testData/completion/InsidePropertyKey2.json
new file mode 100644 (file)
index 0000000..2014e6d
--- /dev/null
@@ -0,0 +1 @@
+{"foo": 42, <caret>}
\ No newline at end of file
diff --git a/plugins/json-tests/testData/completion/InsidePropertyValue.json b/plugins/json-tests/testData/completion/InsidePropertyValue.json
new file mode 100644 (file)
index 0000000..b18a6cc
--- /dev/null
@@ -0,0 +1 @@
+{"foo": <caret>}
\ No newline at end of file
diff --git a/plugins/json-tests/testData/formatting/BlankLinesStripping.json b/plugins/json-tests/testData/formatting/BlankLinesStripping.json
new file mode 100644 (file)
index 0000000..69a90e1
--- /dev/null
@@ -0,0 +1,19 @@
+{
+  "foo":
+
+
+      [
+
+          42,
+
+          null,
+
+        {
+
+
+          "bar": "baz"
+        }
+
+
+      ]
+}
\ No newline at end of file
diff --git a/plugins/json-tests/testData/formatting/BlankLinesStripping_after.json b/plugins/json-tests/testData/formatting/BlankLinesStripping_after.json
new file mode 100644 (file)
index 0000000..9ec9afd
--- /dev/null
@@ -0,0 +1,9 @@
+{
+  "foo": [
+    42,
+    null,
+    {
+      "bar": "baz"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/plugins/json-tests/testData/formatting/ContainerElementsAlignment.json b/plugins/json-tests/testData/formatting/ContainerElementsAlignment.json
new file mode 100644 (file)
index 0000000..43c477e
--- /dev/null
@@ -0,0 +1,12 @@
+{
+  "array": [
+    1,
+       {},
+        "eggs",
+null
+  ],
+  "object": {
+       "foo": [1, 2],
+"bar": true
+  }
+}
\ No newline at end of file
diff --git a/plugins/json-tests/testData/formatting/ContainerElementsAlignment_after.json b/plugins/json-tests/testData/formatting/ContainerElementsAlignment_after.json
new file mode 100644 (file)
index 0000000..1bc85ad
--- /dev/null
@@ -0,0 +1,12 @@
+{
+  "array": [
+    1,
+    {},
+    "eggs",
+    null
+  ],
+  "object": {
+    "foo": [1, 2],
+    "bar": true
+  }
+}
\ No newline at end of file
diff --git a/plugins/json-tests/testData/formatting/SpacesInsertion.json b/plugins/json-tests/testData/formatting/SpacesInsertion.json
new file mode 100644 (file)
index 0000000..3f77fd3
--- /dev/null
@@ -0,0 +1 @@
+{"foo":[1,2 ,"bar"],"baz":null}
\ No newline at end of file
diff --git a/plugins/json-tests/testData/formatting/SpacesInsertion_after.json b/plugins/json-tests/testData/formatting/SpacesInsertion_after.json
new file mode 100644 (file)
index 0000000..f15d717
--- /dev/null
@@ -0,0 +1 @@
+{"foo": [1, 2, "bar"], "baz": null}
\ No newline at end of file
diff --git a/plugins/json-tests/testData/formatting/Wrapping.json b/plugins/json-tests/testData/formatting/Wrapping.json
new file mode 100644 (file)
index 0000000..33722e7
--- /dev/null
@@ -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/plugins/json-tests/testData/formatting/Wrapping_after.json b/plugins/json-tests/testData/formatting/Wrapping_after.json
new file mode 100644 (file)
index 0000000..d0e8a02
--- /dev/null
@@ -0,0 +1,8 @@
+[
+  "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/plugins/json-tests/testData/psi/Keywords.json b/plugins/json-tests/testData/psi/Keywords.json
new file mode 100644 (file)
index 0000000..6e65692
--- /dev/null
@@ -0,0 +1 @@
+[true, false, null]
\ No newline at end of file
diff --git a/plugins/json-tests/testData/psi/Keywords.txt b/plugins/json-tests/testData/psi/Keywords.txt
new file mode 100644 (file)
index 0000000..3d58b73
--- /dev/null
@@ -0,0 +1,14 @@
+FILE
+  JsonArrayImpl(ARRAY)
+    PsiElement([)('[')
+    JsonBooleanLiteralImpl(BOOLEAN_LITERAL)
+      PsiElement(true)('true')
+    PsiElement(,)(',')
+    PsiWhiteSpace(' ')
+    JsonBooleanLiteralImpl(BOOLEAN_LITERAL)
+      PsiElement(false)('false')
+    PsiElement(,)(',')
+    PsiWhiteSpace(' ')
+    JsonNullLiteralImpl(NULL_LITERAL)
+      PsiElement(null)('null')
+    PsiElement(])(']')
\ No newline at end of file
diff --git a/plugins/json-tests/testData/psi/NestedArrayLiterals.json b/plugins/json-tests/testData/psi/NestedArrayLiterals.json
new file mode 100644 (file)
index 0000000..87b6305
--- /dev/null
@@ -0,0 +1 @@
+[1, ["foo", [null]]]
\ No newline at end of file
diff --git a/plugins/json-tests/testData/psi/NestedArrayLiterals.txt b/plugins/json-tests/testData/psi/NestedArrayLiterals.txt
new file mode 100644 (file)
index 0000000..ac42ee2
--- /dev/null
@@ -0,0 +1,20 @@
+FILE
+  JsonArrayImpl(ARRAY)
+    PsiElement([)('[')
+    JsonNumberLiteralImpl(NUMBER_LITERAL)
+      PsiElement(NUMBER)('1')
+    PsiElement(,)(',')
+    PsiWhiteSpace(' ')
+    JsonArrayImpl(ARRAY)
+      PsiElement([)('[')
+      JsonStringLiteralImpl(STRING_LITERAL)
+        PsiElement(STRING)('"foo"')
+      PsiElement(,)(',')
+      PsiWhiteSpace(' ')
+      JsonArrayImpl(ARRAY)
+        PsiElement([)('[')
+        JsonNullLiteralImpl(NULL_LITERAL)
+          PsiElement(null)('null')
+        PsiElement(])(']')
+      PsiElement(])(']')
+    PsiElement(])(']')
\ No newline at end of file
diff --git a/plugins/json-tests/testData/psi/NestedObjectLiterals.json b/plugins/json-tests/testData/psi/NestedObjectLiterals.json
new file mode 100644 (file)
index 0000000..0294a55
--- /dev/null
@@ -0,0 +1 @@
+{ "foo": { "bar": {"baz": null} }}
\ No newline at end of file
diff --git a/plugins/json-tests/testData/psi/NestedObjectLiterals.txt b/plugins/json-tests/testData/psi/NestedObjectLiterals.txt
new file mode 100644 (file)
index 0000000..4706ab8
--- /dev/null
@@ -0,0 +1,33 @@
+FILE
+  JsonObjectImpl(OBJECT)
+    PsiElement({)('{')
+    PsiWhiteSpace(' ')
+    JsonPropertyImpl(PROPERTY)
+      JsonPropertyNameImpl(PROPERTY_NAME)
+        JsonStringLiteralImpl(STRING_LITERAL)
+          PsiElement(STRING)('"foo"')
+      PsiElement(:)(':')
+      PsiWhiteSpace(' ')
+      JsonObjectImpl(OBJECT)
+        PsiElement({)('{')
+        PsiWhiteSpace(' ')
+        JsonPropertyImpl(PROPERTY)
+          JsonPropertyNameImpl(PROPERTY_NAME)
+            JsonStringLiteralImpl(STRING_LITERAL)
+              PsiElement(STRING)('"bar"')
+          PsiElement(:)(':')
+          PsiWhiteSpace(' ')
+          JsonObjectImpl(OBJECT)
+            PsiElement({)('{')
+            JsonPropertyImpl(PROPERTY)
+              JsonPropertyNameImpl(PROPERTY_NAME)
+                JsonStringLiteralImpl(STRING_LITERAL)
+                  PsiElement(STRING)('"baz"')
+              PsiElement(:)(':')
+              PsiWhiteSpace(' ')
+              JsonNullLiteralImpl(NULL_LITERAL)
+                PsiElement(null)('null')
+            PsiElement(})('}')
+        PsiWhiteSpace(' ')
+        PsiElement(})('}')
+    PsiElement(})('}')
\ No newline at end of file
diff --git a/plugins/json-tests/testData/psi/TopLevelStringLiteral.json b/plugins/json-tests/testData/psi/TopLevelStringLiteral.json
new file mode 100644 (file)
index 0000000..13fea57
--- /dev/null
@@ -0,0 +1 @@
+"should not be here"
\ No newline at end of file
diff --git a/plugins/json-tests/testData/psi/TopLevelStringLiteral.txt b/plugins/json-tests/testData/psi/TopLevelStringLiteral.txt
new file mode 100644 (file)
index 0000000..6166316
--- /dev/null
@@ -0,0 +1,3 @@
+FILE
+  PsiErrorElement:'[' or '{' expected, got '"should not be here"'
+    PsiElement(STRING)('"should not be here"')
\ No newline at end of file
diff --git a/plugins/json/gen/com/intellij/json/JsonElementTypes.java b/plugins/json/gen/com/intellij/json/JsonElementTypes.java
new file mode 100644 (file)
index 0000000..019f10d
--- /dev/null
@@ -0,0 +1,71 @@
+// 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 PROPERTY_NAME = new JsonElementType("PROPERTY_NAME");
+  IElementType STRING_LITERAL = new JsonElementType("STRING_LITERAL");
+  IElementType VALUE = new JsonElementType("VALUE");
+
+  IElementType COLON = new JsonTokenType(":");
+  IElementType COMMA = new JsonTokenType(",");
+  IElementType FALSE = new JsonTokenType("false");
+  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 STRING = new JsonTokenType("STRING");
+  IElementType TEXT = new JsonTokenType("TEXT");
+  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 == PROPERTY_NAME) {
+        return new JsonPropertyNameImpl(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/plugins/json/gen/com/intellij/json/JsonLexer.java b/plugins/json/gen/com/intellij/json/JsonLexer.java
new file mode 100644 (file)
index 0000000..ce771b9
--- /dev/null
@@ -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/plugins/json/gen/com/intellij/json/JsonParser.java b/plugins/json/gen/com/intellij/json/JsonParser.java
new file mode 100644 (file)
index 0000000..c85f732
--- /dev/null
@@ -0,0 +1,377 @@
+// 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.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_ == PROPERTY_NAME) {
+      result_ = property_name(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, STRING_LITERAL, VALUE),
+  };
+
+  /* ********************************************************** */
+  // L_BRACKET [array_elements] R_BRACKET
+  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_ = false;
+    boolean pinned_ = false;
+    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_elements]
+  private static boolean array_1(PsiBuilder builder_, int level_) {
+    if (!recursion_guard_(builder_, level_, "array_1")) return false;
+    array_elements(builder_, level_ + 1);
+    return true;
+  }
+
+  /* ********************************************************** */
+  // value
+  static boolean array_element(PsiBuilder builder_, int level_) {
+    if (!recursion_guard_(builder_, level_, "array_element")) return false;
+    boolean result_ = false;
+    Marker marker_ = enter_section_(builder_, level_, _NONE_, null);
+    result_ = value(builder_, level_ + 1);
+    exit_section_(builder_, level_, marker_, null, result_, false, bracket_or_comma_parser_);
+    return result_;
+  }
+
+  /* ********************************************************** */
+  // array_element (COMMA array_element)*
+  static boolean array_elements(PsiBuilder builder_, int level_) {
+    if (!recursion_guard_(builder_, level_, "array_elements")) return false;
+    boolean result_ = false;
+    boolean pinned_ = false;
+    Marker marker_ = enter_section_(builder_, level_, _NONE_, null);
+    result_ = array_element(builder_, level_ + 1);
+    pinned_ = result_; // pin = 1
+    result_ = result_ && array_elements_1(builder_, level_ + 1);
+    exit_section_(builder_, level_, marker_, null, result_, pinned_, null);
+    return result_ || pinned_;
+  }
+
+  // (COMMA array_element)*
+  private static boolean array_elements_1(PsiBuilder builder_, int level_) {
+    if (!recursion_guard_(builder_, level_, "array_elements_1")) return false;
+    int pos_ = current_position_(builder_);
+    while (true) {
+      if (!array_elements_1_0(builder_, level_ + 1)) break;
+      if (!empty_element_parsed_guard_(builder_, "array_elements_1", pos_)) break;
+      pos_ = current_position_(builder_);
+    }
+    return true;
+  }
+
+  // COMMA array_element
+  private static boolean array_elements_1_0(PsiBuilder builder_, int level_) {
+    if (!recursion_guard_(builder_, level_, "array_elements_1_0")) return false;
+    boolean result_ = false;
+    boolean pinned_ = false;
+    Marker marker_ = enter_section_(builder_, level_, _NONE_, null);
+    result_ = consumeToken(builder_, COMMA);
+    pinned_ = result_; // pin = 1
+    result_ = result_ && array_element(builder_, level_ + 1);
+    exit_section_(builder_, level_, marker_, null, result_, pinned_, null);
+    return result_ || pinned_;
+  }
+
+  /* ********************************************************** */
+  // TRUE | FALSE
+  public static boolean boolean_literal(PsiBuilder builder_, int level_) {
+    if (!recursion_guard_(builder_, level_, "boolean_literal")) return false;
+    if (!nextTokenIs(builder_, "<boolean literal>", FALSE, TRUE)) return false;
+    boolean result_ = false;
+    Marker marker_ = enter_section_(builder_, level_, _COLLAPSE_, "<boolean literal>");
+    result_ = consumeToken(builder_, TRUE);
+    if (!result_) result_ = consumeToken(builder_, FALSE);
+    exit_section_(builder_, level_, marker_, BOOLEAN_LITERAL, result_, false, null);
+    return result_;
+  }
+
+  /* ********************************************************** */
+  // !(R_CURLY|COMMA)
+  static boolean brace_or_comma(PsiBuilder builder_, int level_) {
+    if (!recursion_guard_(builder_, level_, "brace_or_comma")) return false;
+    boolean result_ = false;
+    Marker marker_ = enter_section_(builder_, level_, _NOT_, null);
+    result_ = !brace_or_comma_0(builder_, level_ + 1);
+    exit_section_(builder_, level_, marker_, null, result_, false, null);
+    return result_;
+  }
+
+  // R_CURLY|COMMA
+  private static boolean brace_or_comma_0(PsiBuilder builder_, int level_) {
+    if (!recursion_guard_(builder_, level_, "brace_or_comma_0")) return false;
+    boolean result_ = false;
+    Marker marker_ = enter_section_(builder_);
+    result_ = consumeToken(builder_, R_CURLY);
+    if (!result_) result_ = consumeToken(builder_, COMMA);
+    exit_section_(builder_, marker_, null, result_);
+    return result_;
+  }
+
+  /* ********************************************************** */
+  // !(R_BRACKET|COMMA)
+  static boolean bracket_or_comma(PsiBuilder builder_, int level_) {
+    if (!recursion_guard_(builder_, level_, "bracket_or_comma")) return false;
+    boolean result_ = false;
+    Marker marker_ = enter_section_(builder_, level_, _NOT_, null);
+    result_ = !bracket_or_comma_0(builder_, level_ + 1);
+    exit_section_(builder_, level_, marker_, null, result_, false, null);
+    return result_;
+  }
+
+  // R_BRACKET|COMMA
+  private static boolean bracket_or_comma_0(PsiBuilder builder_, int level_) {
+    if (!recursion_guard_(builder_, level_, "bracket_or_comma_0")) return false;
+    boolean result_ = false;
+    Marker marker_ = enter_section_(builder_);
+    result_ = consumeToken(builder_, R_BRACKET);
+    if (!result_) result_ = consumeToken(builder_, COMMA);
+    exit_section_(builder_, marker_, null, result_);
+    return result_;
+  }
+
+  /* ********************************************************** */
+  // object | array
+  static boolean json(PsiBuilder builder_, int level_) {
+    if (!recursion_guard_(builder_, level_, "json")) return false;
+    if (!nextTokenIs(builder_, "", L_BRACKET, L_CURLY)) return false;
+    boolean result_ = false;
+    Marker marker_ = enter_section_(builder_);
+    result_ = object(builder_, level_ + 1);
+    if (!result_) result_ = array(builder_, level_ + 1);
+    exit_section_(builder_, marker_, null, result_);
+    return result_;
+  }
+
+  /* ********************************************************** */
+  // 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_ = false;
+    Marker marker_ = enter_section_(builder_, level_, _COLLAPSE_, "<literal>");
+    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_;
+  }
+
+  /* ********************************************************** */
+  // 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_ = false;
+    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_ = false;
+    Marker marker_ = enter_section_(builder_);
+    result_ = consumeToken(builder_, NUMBER);
+    exit_section_(builder_, marker_, NUMBER_LITERAL, result_);
+    return result_;
+  }
+
+  /* ********************************************************** */
+  // L_CURLY [properties] R_CURLY
+  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_ = false;
+    boolean pinned_ = false;
+    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_;
+  }
+
+  // [properties]
+  private static boolean object_1(PsiBuilder builder_, int level_) {
+    if (!recursion_guard_(builder_, level_, "object_1")) return false;
+    properties(builder_, level_ + 1);
+    return true;
+  }
+
+  /* ********************************************************** */
+  // property (COMMA property)*
+  static boolean properties(PsiBuilder builder_, int level_) {
+    if (!recursion_guard_(builder_, level_, "properties")) return false;
+    if (!nextTokenIs(builder_, STRING)) return false;
+    boolean result_ = false;
+    boolean pinned_ = false;
+    Marker marker_ = enter_section_(builder_, level_, _NONE_, null);
+    result_ = property(builder_, level_ + 1);
+    pinned_ = result_; // pin = 1
+    result_ = result_ && properties_1(builder_, level_ + 1);
+    exit_section_(builder_, level_, marker_, null, result_, pinned_, null);
+    return result_ || pinned_;
+  }
+
+  // (COMMA property)*
+  private static boolean properties_1(PsiBuilder builder_, int level_) {
+    if (!recursion_guard_(builder_, level_, "properties_1")) return false;
+    int pos_ = current_position_(builder_);
+    while (true) {
+      if (!properties_1_0(builder_, level_ + 1)) break;
+      if (!empty_element_parsed_guard_(builder_, "properties_1", pos_)) break;
+      pos_ = current_position_(builder_);
+    }
+    return true;
+  }
+
+  // COMMA property
+  private static boolean properties_1_0(PsiBuilder builder_, int level_) {
+    if (!recursion_guard_(builder_, level_, "properties_1_0")) return false;
+    boolean result_ = false;
+    boolean pinned_ = false;
+    Marker marker_ = enter_section_(builder_, level_, _NONE_, null);
+    result_ = consumeToken(builder_, COMMA);
+    pinned_ = result_; // pin = 1
+    result_ = result_ && property(builder_, level_ + 1);
+    exit_section_(builder_, level_, marker_, null, result_, pinned_, null);
+    return result_ || pinned_;
+  }
+
+  /* ********************************************************** */
+  // property_name COLON value
+  public static boolean property(PsiBuilder builder_, int level_) {
+    if (!recursion_guard_(builder_, level_, "property")) return false;
+    boolean result_ = false;
+    boolean pinned_ = false;
+    Marker marker_ = enter_section_(builder_, level_, _NONE_, "<property>");
+    result_ = property_name(builder_, level_ + 1);
+    pinned_ = result_; // pin = 1
+    result_ = result_ && report_error_(builder_, consumeToken(builder_, COLON));
+    result_ = pinned_ && value(builder_, level_ + 1) && result_;
+    exit_section_(builder_, level_, marker_, PROPERTY, result_, pinned_, brace_or_comma_parser_);
+    return result_ || pinned_;
+  }
+
+  /* ********************************************************** */
+  // string_literal
+  public static boolean property_name(PsiBuilder builder_, int level_) {
+    if (!recursion_guard_(builder_, level_, "property_name")) return false;
+    if (!nextTokenIs(builder_, STRING)) return false;
+    boolean result_ = false;
+    Marker marker_ = enter_section_(builder_);
+    result_ = string_literal(builder_, level_ + 1);
+    exit_section_(builder_, marker_, PROPERTY_NAME, result_);
+    return result_;
+  }
+
+  /* ********************************************************** */
+  // STRING
+  public static boolean string_literal(PsiBuilder builder_, int level_) {
+    if (!recursion_guard_(builder_, level_, "string_literal")) return false;
+    if (!nextTokenIs(builder_, STRING)) return false;
+    boolean result_ = false;
+    Marker marker_ = enter_section_(builder_);
+    result_ = consumeToken(builder_, STRING);
+    exit_section_(builder_, marker_, STRING_LITERAL, result_);
+    return result_;
+  }
+
+  /* ********************************************************** */
+  // object | array | literal
+  public static boolean value(PsiBuilder builder_, int level_) {
+    if (!recursion_guard_(builder_, level_, "value")) return false;
+    boolean result_ = false;
+    Marker marker_ = enter_section_(builder_, level_, _COLLAPSE_, "<value>");
+    result_ = object(builder_, level_ + 1);
+    if (!result_) result_ = array(builder_, level_ + 1);
+    if (!result_) result_ = literal(builder_, level_ + 1);
+    exit_section_(builder_, level_, marker_, VALUE, result_, false, null);
+    return result_;
+  }
+
+  final static Parser brace_or_comma_parser_ = new Parser() {
+    public boolean parse(PsiBuilder builder_, int level_) {
+      return brace_or_comma(builder_, level_ + 1);
+    }
+  };
+  final static Parser bracket_or_comma_parser_ = new Parser() {
+    public boolean parse(PsiBuilder builder_, int level_) {
+      return bracket_or_comma(builder_, level_ + 1);
+    }
+  };
+}
diff --git a/plugins/json/gen/com/intellij/json/JsonParserUtil.java b/plugins/json/gen/com/intellij/json/JsonParserUtil.java
new file mode 100644 (file)
index 0000000..a2e6ef5
--- /dev/null
@@ -0,0 +1,9 @@
+package com.intellij.json;
+
+import com.intellij.lang.parser.GeneratedParserUtilBase;
+
+/**
+ * @author Mikhail Golubev
+ */
+public class JsonParserUtil extends GeneratedParserUtilBase {
+}
diff --git a/plugins/json/gen/com/intellij/json/_JsonLexer.flex b/plugins/json/gen/com/intellij/json/_JsonLexer.flex
new file mode 100644 (file)
index 0000000..0083719
--- /dev/null
@@ -0,0 +1,48 @@
+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})+
+
+STRING=\"([^\\\"\r\n]|\\[^\r\n])*\"?
+NUMBER=-?[0-9]+(\.[0-9]+([eE][+-]?[0-9]+)?)?
+TEXT=[a-zA-Z_0-9]+
+
+%%
+<YYINITIAL> {
+  {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; }
+
+  {STRING}           { return STRING; }
+  {NUMBER}           { return NUMBER; }
+  {TEXT}             { return TEXT; }
+
+  [^] { return com.intellij.psi.TokenType.BAD_CHARACTER; }
+}
diff --git a/plugins/json/gen/com/intellij/json/_JsonLexer.java b/plugins/json/gen/com/intellij/json/_JsonLexer.java
new file mode 100644 (file)
index 0000000..1cf42f5
--- /dev/null
@@ -0,0 +1,547 @@
+/* The following code was generated by JFlex 1.4.3 on 7/15/14 2:07 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 
+ * <a href="http://www.jflex.de/">JFlex</a> 1.4.3
+ * on 7/15/14 2:07 PM from the specification file
+ * <tt>/home/east825/develop/repos/IDEA-branch/community/plugins/json/gen/com/intellij/json/_JsonLexer.flex</tt>
+ */
+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\0\1\2\1\1\1\0\1\2\1\1\22\0\1\2\1\0\1\3"+
+    "\10\0\1\11\1\17\1\5\1\7\1\0\12\6\1\20\6\0\4\12"+
+    "\1\10\25\12\1\15\1\4\1\16\1\0\1\12\1\0\1\26\3\12"+
+    "\1\24\1\25\5\12\1\27\1\12\1\31\3\12\1\22\1\30\1\21"+
+    "\1\23\5\12\1\13\1\0\1\14\uff82\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\3\1\1\1\4\1\5\1\6"+
+    "\1\7\1\10\1\11\1\12\1\13\3\5\1\3\1\0"+
+    "\1\4\1\0\3\5\1\4\3\5\1\0\1\14\1\5"+
+    "\1\15\1\0\1\4\1\16";
+
+  private static int [] zzUnpackAction() {
+    int [] result = new int[34];
+    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\32\0\64\0\116\0\150\0\202\0\234\0\32"+
+    "\0\32\0\32\0\32\0\32\0\32\0\266\0\320\0\352"+
+    "\0\32\0\u0104\0\u011e\0\u0138\0\u0152\0\u016c\0\u0186\0\u01a0"+
+    "\0\u01ba\0\u01d4\0\u01ee\0\u0208\0\234\0\u0222\0\234\0\u023c"+
+    "\0\u023c\0\234";
+
+  private static int [] zzUnpackRowMap() {
+    int [] result = new int[34];
+    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\2\3\1\4\1\2\1\5\1\6\1\2\1\7"+
+    "\1\2\1\7\1\10\1\11\1\12\1\13\1\14\1\15"+
+    "\1\16\3\7\1\17\3\7\1\20\33\0\2\3\27\0"+
+    "\1\4\1\0\1\4\1\21\1\22\25\4\6\0\1\23"+
+    "\31\0\1\6\1\24\1\7\1\0\1\7\6\0\11\7"+
+    "\6\0\1\7\1\0\1\7\1\0\1\7\6\0\11\7"+
+    "\6\0\1\7\1\0\1\7\1\0\1\7\6\0\1\7"+
+    "\1\25\7\7\6\0\1\7\1\0\1\7\1\0\1\7"+
+    "\6\0\5\7\1\26\3\7\6\0\1\7\1\0\1\7"+
+    "\1\0\1\7\6\0\2\7\1\27\6\7\1\4\1\0"+
+    "\30\4\6\0\1\23\1\24\30\0\1\30\31\0\1\7"+
+    "\1\0\1\7\1\0\1\7\6\0\2\7\1\31\6\7"+
+    "\6\0\1\7\1\0\1\7\1\0\1\7\6\0\6\7"+
+    "\1\32\2\7\6\0\1\7\1\0\1\7\1\0\1\7"+
+    "\6\0\6\7\1\33\2\7\6\0\1\30\1\0\1\34"+
+    "\13\0\1\34\13\0\1\7\1\0\1\7\1\0\1\7"+
+    "\6\0\3\7\1\35\5\7\6\0\1\7\1\0\1\7"+
+    "\1\0\1\7\6\0\7\7\1\36\1\7\6\0\1\7"+
+    "\1\0\1\7\1\0\1\7\6\0\6\7\1\37\2\7"+
+    "\5\0\1\40\1\41\2\0\1\40\26\0\1\7\1\0"+
+    "\1\7\1\0\1\7\6\0\3\7\1\42\5\7\6\0"+
+    "\1\41\23\0";
+
+  private static int [] zzUnpackTrans() {
+    int [] result = new int[598];
+    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 <code>aState</code>
+   */
+  private static final int [] ZZ_ATTRIBUTE = zzUnpackAttribute();
+
+  private static final String ZZ_ATTRIBUTE_PACKED_0 =
+    "\1\0\1\11\5\1\6\11\3\1\1\11\1\0\1\1"+
+    "\1\0\7\1\1\0\3\1\1\0\2\1";
+
+  private static int [] zzUnpackAttribute() {
+    int [] result = new int[34];
+    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 < 92) {
+      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      <code>false</code>, 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 <tt>pos</tt> 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 7: 
+          { return R_CURLY;
+          }
+        case 15: break;
+        case 6: 
+          { return L_CURLY;
+          }
+        case 16: break;
+        case 13: 
+          { return NULL;
+          }
+        case 17: break;
+        case 8: 
+          { return L_BRACKET;
+          }
+        case 18: break;
+        case 10: 
+          { return COMMA;
+          }
+        case 19: break;
+        case 2: 
+          { return com.intellij.psi.TokenType.WHITE_SPACE;
+          }
+        case 20: break;
+        case 5: 
+          { return TEXT;
+          }
+        case 21: break;
+        case 1: 
+          { return com.intellij.psi.TokenType.BAD_CHARACTER;
+          }
+        case 22: break;
+        case 12: 
+          { return TRUE;
+          }
+        case 23: break;
+        case 11: 
+          { return COLON;
+          }
+        case 24: break;
+        case 3: 
+          { return STRING;
+          }
+        case 25: break;
+        case 4: 
+          { return NUMBER;
+          }
+        case 26: break;
+        case 14: 
+          { return FALSE;
+          }
+        case 27: break;
+        case 9: 
+          { return R_BRACKET;
+          }
+        case 28: break;
+        default:
+          if (zzInput == YYEOF && zzStartRead == zzCurrentPos) {
+            zzAtEOF = true;
+            return null;
+          }
+          else {
+            zzScanError(ZZ_NO_MATCH);
+          }
+      }
+    }
+  }
+
+
+}
diff --git a/plugins/json/gen/com/intellij/json/psi/JsonArray.java b/plugins/json/gen/com/intellij/json/psi/JsonArray.java
new file mode 100644 (file)
index 0000000..a46a8b8
--- /dev/null
@@ -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 JsonValue {
+
+  @NotNull
+  List<JsonValue> getValueList();
+
+}
diff --git a/plugins/json/gen/com/intellij/json/psi/JsonBooleanLiteral.java b/plugins/json/gen/com/intellij/json/psi/JsonBooleanLiteral.java
new file mode 100644 (file)
index 0000000..684d798
--- /dev/null
@@ -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/plugins/json/gen/com/intellij/json/psi/JsonLiteral.java b/plugins/json/gen/com/intellij/json/psi/JsonLiteral.java
new file mode 100644 (file)
index 0000000..b11833e
--- /dev/null
@@ -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 JsonLiteral extends JsonValue {
+
+}
diff --git a/plugins/json/gen/com/intellij/json/psi/JsonNullLiteral.java b/plugins/json/gen/com/intellij/json/psi/JsonNullLiteral.java
new file mode 100644 (file)
index 0000000..a57448e
--- /dev/null
@@ -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/plugins/json/gen/com/intellij/json/psi/JsonNumberLiteral.java b/plugins/json/gen/com/intellij/json/psi/JsonNumberLiteral.java
new file mode 100644 (file)
index 0000000..c189357
--- /dev/null
@@ -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/plugins/json/gen/com/intellij/json/psi/JsonObject.java b/plugins/json/gen/com/intellij/json/psi/JsonObject.java
new file mode 100644 (file)
index 0000000..308b0a6
--- /dev/null
@@ -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 JsonObject extends JsonValue {
+
+  @NotNull
+  List<JsonProperty> getPropertyList();
+
+}
diff --git a/plugins/json/gen/com/intellij/json/psi/JsonProperty.java b/plugins/json/gen/com/intellij/json/psi/JsonProperty.java
new file mode 100644 (file)
index 0000000..a62e400
--- /dev/null
@@ -0,0 +1,19 @@
+// 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 JsonProperty extends PsiElement {
+
+  @NotNull
+  JsonPropertyName getPropertyName();
+
+  @Nullable
+  JsonValue getValue();
+
+  @NotNull
+  String getName();
+
+}
diff --git a/plugins/json/gen/com/intellij/json/psi/JsonPropertyName.java b/plugins/json/gen/com/intellij/json/psi/JsonPropertyName.java
new file mode 100644 (file)
index 0000000..986dfc4
--- /dev/null
@@ -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 JsonPropertyName extends PsiElement {
+
+  @NotNull
+  JsonStringLiteral getStringLiteral();
+
+}
diff --git a/plugins/json/gen/com/intellij/json/psi/JsonStringLiteral.java b/plugins/json/gen/com/intellij/json/psi/JsonStringLiteral.java
new file mode 100644 (file)
index 0000000..b1bc0e8
--- /dev/null
@@ -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 JsonStringLiteral extends JsonLiteral {
+
+  @NotNull
+  PsiElement getString();
+
+}
diff --git a/plugins/json/gen/com/intellij/json/psi/JsonValue.java b/plugins/json/gen/com/intellij/json/psi/JsonValue.java
new file mode 100644 (file)
index 0000000..46995ec
--- /dev/null
@@ -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 PsiElement {
+
+}
diff --git a/plugins/json/gen/com/intellij/json/psi/JsonVisitor.java b/plugins/json/gen/com/intellij/json/psi/JsonVisitor.java
new file mode 100644 (file)
index 0000000..cdb8755
--- /dev/null
@@ -0,0 +1,54 @@
+// This is a generated file. Not intended for manual editing.
+package com.intellij.json.psi;
+
+import org.jetbrains.annotations.*;
+import com.intellij.psi.PsiElementVisitor;
+import com.intellij.psi.PsiElement;
+
+public class JsonVisitor extends PsiElementVisitor {
+
+  public void visitArray(@NotNull JsonArray o) {
+    visitValue(o);
+  }
+
+  public void visitBooleanLiteral(@NotNull JsonBooleanLiteral o) {
+    visitLiteral(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) {
+    visitValue(o);
+  }
+
+  public void visitProperty(@NotNull JsonProperty o) {
+    visitPsiElement(o);
+  }
+
+  public void visitPropertyName(@NotNull JsonPropertyName o) {
+    visitPsiElement(o);
+  }
+
+  public void visitStringLiteral(@NotNull JsonStringLiteral o) {
+    visitLiteral(o);
+  }
+
+  public void visitValue(@NotNull JsonValue o) {
+    visitPsiElement(o);
+  }
+
+  public void visitPsiElement(@NotNull PsiElement o) {
+    visitElement(o);
+  }
+
+}
diff --git a/plugins/json/gen/com/intellij/json/psi/impl/JsonArrayImpl.java b/plugins/json/gen/com/intellij/json/psi/impl/JsonArrayImpl.java
new file mode 100644 (file)
index 0000000..54a9603
--- /dev/null
@@ -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 JsonValueImpl implements JsonArray {
+
+  public JsonArrayImpl(ASTNode node) {
+    super(node);
+  }
+
+  public void accept(@NotNull PsiElementVisitor visitor) {
+    if (visitor instanceof JsonVisitor) ((JsonVisitor)visitor).visitArray(this);
+    else super.accept(visitor);
+  }
+
+  @Override
+  @NotNull
+  public List<JsonValue> getValueList() {
+    return PsiTreeUtil.getChildrenOfTypeAsList(this, JsonValue.class);
+  }
+
+}
diff --git a/plugins/json/gen/com/intellij/json/psi/impl/JsonBooleanLiteralImpl.java b/plugins/json/gen/com/intellij/json/psi/impl/JsonBooleanLiteralImpl.java
new file mode 100644 (file)
index 0000000..107879c
--- /dev/null
@@ -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 JsonVisitor) ((JsonVisitor)visitor).visitBooleanLiteral(this);
+    else super.accept(visitor);
+  }
+
+}
diff --git a/plugins/json/gen/com/intellij/json/psi/impl/JsonLiteralImpl.java b/plugins/json/gen/com/intellij/json/psi/impl/JsonLiteralImpl.java
new file mode 100644 (file)
index 0000000..54aac5d
--- /dev/null
@@ -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 JsonLiteralImpl extends JsonValueImpl implements JsonLiteral {
+
+  public JsonLiteralImpl(ASTNode node) {
+    super(node);
+  }
+
+  public void accept(@NotNull PsiElementVisitor visitor) {
+    if (visitor instanceof JsonVisitor) ((JsonVisitor)visitor).visitLiteral(this);
+    else super.accept(visitor);
+  }
+
+}
diff --git a/plugins/json/gen/com/intellij/json/psi/impl/JsonNullLiteralImpl.java b/plugins/json/gen/com/intellij/json/psi/impl/JsonNullLiteralImpl.java
new file mode 100644 (file)
index 0000000..78454b0
--- /dev/null
@@ -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 JsonVisitor) ((JsonVisitor)visitor).visitNullLiteral(this);
+    else super.accept(visitor);
+  }
+
+}
diff --git a/plugins/json/gen/com/intellij/json/psi/impl/JsonNumberLiteralImpl.java b/plugins/json/gen/com/intellij/json/psi/impl/JsonNumberLiteralImpl.java
new file mode 100644 (file)
index 0000000..f4469bc
--- /dev/null
@@ -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 JsonVisitor) ((JsonVisitor)visitor).visitNumberLiteral(this);
+    else super.accept(visitor);
+  }
+
+  @Override
+  @NotNull
+  public PsiElement getNumber() {
+    return findNotNullChildByType(NUMBER);
+  }
+
+}
diff --git a/plugins/json/gen/com/intellij/json/psi/impl/JsonObjectImpl.java b/plugins/json/gen/com/intellij/json/psi/impl/JsonObjectImpl.java
new file mode 100644 (file)
index 0000000..c2e5b32
--- /dev/null
@@ -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 JsonObjectImpl extends JsonValueImpl implements JsonObject {
+
+  public JsonObjectImpl(ASTNode node) {
+    super(node);
+  }
+
+  public void accept(@NotNull PsiElementVisitor visitor) {
+    if (visitor instanceof JsonVisitor) ((JsonVisitor)visitor).visitObject(this);
+    else super.accept(visitor);
+  }
+
+  @Override
+  @NotNull
+  public List<JsonProperty> getPropertyList() {
+    return PsiTreeUtil.getChildrenOfTypeAsList(this, JsonProperty.class);
+  }
+
+}
diff --git a/plugins/json/gen/com/intellij/json/psi/impl/JsonPropertyImpl.java b/plugins/json/gen/com/intellij/json/psi/impl/JsonPropertyImpl.java
new file mode 100644 (file)
index 0000000..5265b1c
--- /dev/null
@@ -0,0 +1,42 @@
+// 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.extapi.psi.ASTWrapperPsiElement;
+import com.intellij.json.psi.*;
+
+public class JsonPropertyImpl extends ASTWrapperPsiElement implements JsonProperty {
+
+  public JsonPropertyImpl(ASTNode node) {
+    super(node);
+  }
+
+  public void accept(@NotNull PsiElementVisitor visitor) {
+    if (visitor instanceof JsonVisitor) ((JsonVisitor)visitor).visitProperty(this);
+    else super.accept(visitor);
+  }
+
+  @Override
+  @NotNull
+  public JsonPropertyName getPropertyName() {
+    return findNotNullChildByClass(JsonPropertyName.class);
+  }
+
+  @Override
+  @Nullable
+  public JsonValue getValue() {
+    return findChildByClass(JsonValue.class);
+  }
+
+  @NotNull
+  public String getName() {
+    return JsonPsiImplUtils.getName(this);
+  }
+
+}
diff --git a/plugins/json/gen/com/intellij/json/psi/impl/JsonPropertyNameImpl.java b/plugins/json/gen/com/intellij/json/psi/impl/JsonPropertyNameImpl.java
new file mode 100644 (file)
index 0000000..39f9573
--- /dev/null
@@ -0,0 +1,31 @@
+// 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.extapi.psi.ASTWrapperPsiElement;
+import com.intellij.json.psi.*;
+
+public class JsonPropertyNameImpl extends ASTWrapperPsiElement implements JsonPropertyName {
+
+  public JsonPropertyNameImpl(ASTNode node) {
+    super(node);
+  }
+
+  public void accept(@NotNull PsiElementVisitor visitor) {
+    if (visitor instanceof JsonVisitor) ((JsonVisitor)visitor).visitPropertyName(this);
+    else super.accept(visitor);
+  }
+
+  @Override
+  @NotNull
+  public JsonStringLiteral getStringLiteral() {
+    return findNotNullChildByClass(JsonStringLiteral.class);
+  }
+
+}
diff --git a/plugins/json/gen/com/intellij/json/psi/impl/JsonStringLiteralImpl.java b/plugins/json/gen/com/intellij/json/psi/impl/JsonStringLiteralImpl.java
new file mode 100644 (file)
index 0000000..e018ce6
--- /dev/null
@@ -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 JsonStringLiteralImpl extends JsonLiteralImpl implements JsonStringLiteral {
+
+  public JsonStringLiteralImpl(ASTNode node) {
+    super(node);
+  }
+
+  public void accept(@NotNull PsiElementVisitor visitor) {
+    if (visitor instanceof JsonVisitor) ((JsonVisitor)visitor).visitStringLiteral(this);
+    else super.accept(visitor);
+  }
+
+  @Override
+  @NotNull
+  public PsiElement getString() {
+    return findNotNullChildByType(STRING);
+  }
+
+}
diff --git a/plugins/json/gen/com/intellij/json/psi/impl/JsonValueImpl.java b/plugins/json/gen/com/intellij/json/psi/impl/JsonValueImpl.java
new file mode 100644 (file)
index 0000000..28bcf95
--- /dev/null
@@ -0,0 +1,25 @@
+// 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.extapi.psi.ASTWrapperPsiElement;
+import com.intellij.json.psi.*;
+
+public class JsonValueImpl extends ASTWrapperPsiElement implements JsonValue {
+
+  public JsonValueImpl(ASTNode node) {
+    super(node);
+  }
+
+  public void accept(@NotNull PsiElementVisitor visitor) {
+    if (visitor instanceof JsonVisitor) ((JsonVisitor)visitor).visitValue(this);
+    else super.accept(visitor);
+  }
+
+}
diff --git a/plugins/json/json.iml b/plugins/json/json.iml
new file mode 100644 (file)
index 0000000..d58b456
--- /dev/null
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+  <component name="NewModuleRootManager" inherit-compiler-output="true">
+    <exclude-output />
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/gen" isTestSource="false" generated="true" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="module" module-name="core-api" />
+    <orderEntry type="module" module-name="platform-api" />
+    <orderEntry type="module" module-name="lang-impl" />
+    <orderEntry type="module" module-name="xml-openapi" />
+  </component>
+</module>
+
diff --git a/plugins/json/src/META-INF/plugin.xml b/plugins/json/src/META-INF/plugin.xml
new file mode 100644 (file)
index 0000000..5f6952c
--- /dev/null
@@ -0,0 +1,44 @@
+<idea-plugin version="2">
+  <id>com.intellij.json</id>
+  <name>JSON Support</name>
+  <version>1.0</version>
+  <vendor>JetBrains</vendor>
+
+  <depends>com.intellij.modules.xml</depends>
+
+  <description><![CDATA[
+      Provides highlighting and formatting for JSON files.
+  ]]></description>
+
+  <application-components>
+    <!-- Add your application components here -->
+  </application-components>
+
+  <project-components>
+    <!-- Add your project components here -->
+  </project-components>
+
+  <actions>
+    <!--<action class="com.jetbrains.json.actions.RestartHighlightingAction" id="json.restart.highlighting" text="Restart Highlighting"/>-->
+  </actions>
+
+  <extensions defaultExtensionNs="com.intellij">
+    <fileTypeFactory implementation="com.intellij.json.JsonFileTypeFactory"/>
+    <lang.parserDefinition language="JSON" implementationClass="com.intellij.json.JsonParserDefinition"/>
+    <lang.syntaxHighlighterFactory key="JSON" implementationClass="com.intellij.json.JsonSyntaxHighlighterFactory"/>
+
+    <!-- Code style and formatting -->
+    <codeStyleSettingsProvider implementation="com.intellij.json.formatter.JsonCodeStyleSettingsProvider"/>
+    <langCodeStyleSettingsProvider implementation="com.intellij.json.formatter.JsonLanguageCodeStyleSettingsProvider"/>
+    <lang.formatter language="JSON" implementationClass="com.intellij.json.formatter.JsonFormattingBuilderModel"/>
+    <lang.braceMatcher language="JSON" implementationClass="com.intellij.json.JsonBraceMatcher"/>
+    <quoteHandler fileType="JSON" className="com.intellij.json.JsonQuoteHandler"/>
+    <enterHandlerDelegate implementation="com.intellij.json.formatter.JsonEnterBetweenBracesHandler"/>
+
+    <!-- Codeinsight -->
+    <breadcrumbsInfoProvider implementation="com.intellij.json.breadcrumbs.JsonBreadcrumbsProvider"/>
+    <completion.contributor language="JSON" implementationClass="com.intellij.json.codeinsight.JsonCompletionContributor"/>
+    <annotator language="JSON" implementationClass="com.intellij.json.codeinsight.JsonStringLiteralAnnotator"/>
+
+  </extensions>
+</idea-plugin>
\ No newline at end of file
diff --git a/plugins/json/src/com/intellij/json/JsonBraceMatcher.java b/plugins/json/src/com/intellij/json/JsonBraceMatcher.java
new file mode 100644 (file)
index 0000000..2504f30
--- /dev/null
@@ -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/plugins/json/src/com/intellij/json/JsonElementType.java b/plugins/json/src/com/intellij/json/JsonElementType.java
new file mode 100644 (file)
index 0000000..54e05df
--- /dev/null
@@ -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/plugins/json/src/com/intellij/json/JsonFile.java b/plugins/json/src/com/intellij/json/JsonFile.java
new file mode 100644 (file)
index 0000000..e897156
--- /dev/null
@@ -0,0 +1,19 @@
+package com.intellij.json;
+
+import com.intellij.extapi.psi.PsiFileBase;
+import com.intellij.openapi.fileTypes.FileType;
+import com.intellij.psi.FileViewProvider;
+import org.jetbrains.annotations.NotNull;
+
+public class JsonFile extends PsiFileBase {
+
+  public JsonFile(FileViewProvider fileViewProvider) {
+    super(fileViewProvider, JsonLanguage.INSTANCE);
+  }
+
+  @NotNull
+  @Override
+  public FileType getFileType() {
+    return JsonFileType.INSTANCE;
+  }
+}
diff --git a/plugins/json/src/com/intellij/json/JsonFileType.java b/plugins/json/src/com/intellij/json/JsonFileType.java
new file mode 100644 (file)
index 0000000..3a2d0a4
--- /dev/null
@@ -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/plugins/json/src/com/intellij/json/JsonFileTypeFactory.java b/plugins/json/src/com/intellij/json/JsonFileTypeFactory.java
new file mode 100644 (file)
index 0000000..2cfaa2f
--- /dev/null
@@ -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/plugins/json/src/com/intellij/json/JsonLanguage.java b/plugins/json/src/com/intellij/json/JsonLanguage.java
new file mode 100644 (file)
index 0000000..09e2807
--- /dev/null
@@ -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/plugins/json/src/com/intellij/json/JsonParserDefinition.java b/plugins/json/src/com/intellij/json/JsonParserDefinition.java
new file mode 100644 (file)
index 0000000..473a716
--- /dev/null
@@ -0,0 +1,72 @@
+package com.intellij.json;
+
+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;
+
+public class JsonParserDefinition implements ParserDefinition {
+  public static final TokenSet WHITE_SPACES = TokenSet.create(TokenType.WHITE_SPACE);
+  public static final TokenSet STRING_LITERALS = TokenSet.create(JsonElementTypes.STRING);
+  public static final IFileElementType FILE = new IFileElementType(JsonLanguage.INSTANCE);
+  public static final TokenSet CONTAINERS = TokenSet.create(JsonElementTypes.ARRAY, JsonElementTypes.OBJECT);
+
+
+  @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 TokenSet.EMPTY;
+  }
+
+  @NotNull
+  @Override
+  public TokenSet getStringLiteralElements() {
+    return STRING_LITERALS;
+  }
+
+  @NotNull
+  @Override
+  public PsiElement createElement(ASTNode astNode) {
+    return JsonElementTypes.Factory.createElement(astNode);
+  }
+
+  @Override
+  public PsiFile createFile(FileViewProvider fileViewProvider) {
+    return new JsonFile(fileViewProvider);
+  }
+
+  @Override
+  public SpaceRequirements spaceExistanceTypeBetweenTokens(ASTNode astNode, ASTNode astNode2) {
+    return SpaceRequirements.MAY;
+  }
+}
diff --git a/plugins/json/src/com/intellij/json/JsonQuoteHandler.java b/plugins/json/src/com/intellij/json/JsonQuoteHandler.java
new file mode 100644 (file)
index 0000000..4ddac1a
--- /dev/null
@@ -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/plugins/json/src/com/intellij/json/JsonSyntaxHighlighterFactory.java b/plugins/json/src/com/intellij/json/JsonSyntaxHighlighterFactory.java
new file mode 100644 (file)
index 0000000..a9c29de
--- /dev/null
@@ -0,0 +1,67 @@
+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.psi.StringEscapesTokenTypes.*;
+import static com.intellij.json.JsonElementTypes.*;
+
+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<IElementType, TextAttributesKey> ourAttributes = new HashMap<IElementType, TextAttributesKey>();
+
+    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, STRING);
+      fillMap(ourAttributes, DefaultLanguageHighlighterColors.NUMBER, NUMBER);
+      fillMap(ourAttributes, DefaultLanguageHighlighterColors.KEYWORD, TRUE, FALSE, NULL);
+      fillMap(ourAttributes, HighlighterColors.TEXT, TEXT);
+      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());
+      StringLiteralLexer stringLexer = new StringLiteralLexer('\"', STRING, false, "/", false, false);
+      layeredLexer.registerSelfStoppingLayer(stringLexer, new IElementType[]{STRING}, IElementType.EMPTY_ARRAY);
+      return layeredLexer;
+    }
+
+    @NotNull
+    @Override
+    public TextAttributesKey[] getTokenHighlights(IElementType type) {
+      return pack(ourAttributes.get(type));
+    }
+  }
+}
diff --git a/plugins/json/src/com/intellij/json/JsonTokenType.java b/plugins/json/src/com/intellij/json/JsonTokenType.java
new file mode 100644 (file)
index 0000000..a7752cd
--- /dev/null
@@ -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/plugins/json/src/com/intellij/json/breadcrumbs/JsonBreadcrumbsProvider.java b/plugins/json/src/com/intellij/json/breadcrumbs/JsonBreadcrumbsProvider.java
new file mode 100644 (file)
index 0000000..e41f316
--- /dev/null
@@ -0,0 +1,61 @@
+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.JsonArray;
+import com.intellij.json.psi.JsonProperty;
+import com.intellij.json.psi.JsonValue;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+
+/**
+ * @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 isProperty(e) || isArrayElement(e);
+  }
+
+  @NotNull
+  @Override
+  public String getElementInfo(@NotNull PsiElement e) {
+    if (isProperty(e)) {
+      return ((JsonProperty)e).getName();
+    }
+    else if (isArrayElement(e)) {
+      List<JsonValue> 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 taken only for JsonProperty or JsonArray's element");
+  }
+
+  @Nullable
+  @Override
+  public String getElementTooltip(@NotNull PsiElement e) {
+    return null;
+  }
+
+  private static boolean isProperty(PsiElement element) {
+    return element instanceof JsonProperty;
+  }
+
+  private static boolean isArrayElement(PsiElement element) {
+    return element instanceof JsonValue && element.getParent() instanceof JsonArray;
+  }
+}
diff --git a/plugins/json/src/com/intellij/json/codeinsight/JsonCompletionContributor.java b/plugins/json/src/com/intellij/json/codeinsight/JsonCompletionContributor.java
new file mode 100644 (file)
index 0000000..aed1217
--- /dev/null
@@ -0,0 +1,58 @@
+package com.intellij.json.codeinsight;
+
+import com.intellij.codeInsight.completion.*;
+import com.intellij.codeInsight.lookup.LookupElementBuilder;
+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 com.intellij.json.psi.JsonArray;
+import com.intellij.json.psi.JsonProperty;
+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 String COMPLETION_PLACEHOLDER = "\"" + CompletionInitializationContext.DUMMY_IDENTIFIER + "\"";
+
+  private static final PsiElementPattern.Capture<PsiElement> AFTER_COLON_IN_PROPERTY = psiElement()
+    .afterLeaf(":").withSuperParent(2, JsonProperty.class);
+  private static final PsiElementPattern.Capture<PsiElement> 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);
+  }
+
+
+  @Override
+  public void beforeCompletion(@NotNull CompletionInitializationContext context) {
+    context.setDummyIdentifier(COMPLETION_PLACEHOLDER);
+  }
+
+  private static class MyKeywordsCompletionProvider extends CompletionProvider<CompletionParameters> {
+    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/plugins/json/src/com/intellij/json/codeinsight/JsonStringLiteralAnnotator.java b/plugins/json/src/com/intellij/json/codeinsight/JsonStringLiteralAnnotator.java
new file mode 100644 (file)
index 0000000..92c63ad
--- /dev/null
@@ -0,0 +1,83 @@
+package com.intellij.json.codeinsight;
+
+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 com.intellij.json.psi.JsonStringLiteral;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author Mikhail Golubev
+ */
+public class JsonStringLiteralAnnotator implements Annotator {
+
+  @NonNls public static final String MISSING_CLOSING_QUOTE = "Missing closing quote";
+  @NonNls public static final String ILLEGAL_ESCAPE_SEQUENCE = "Illegal escape sequence";
+  @NonNls public static final String ILLEGAL_UNICODE_ESCAPE_SEQUENCE = "Illegal unicode escape sequence";
+
+  @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(length - 1) != '\"' || quoteEscaped(text, length - 1)) {
+        holder.createErrorAnnotation(element.getTextRange(), 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, 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, ILLEGAL_UNICODE_ESCAPE_SEQUENCE);
+                  break;
+                }
+              }
+              pos = i;
+              break;
+            default:
+              TextRange range = new TextRange(offset + pos, offset + pos + 2);
+              holder.createErrorAnnotation(range, 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/plugins/json/src/com/intellij/json/formatter/JsonBlock.java b/plugins/json/src/com/intellij/json/formatter/JsonBlock.java
new file mode 100644 (file)
index 0000000..69d6a89
--- /dev/null
@@ -0,0 +1,184 @@
+package com.intellij.json.formatter;
+
+import com.intellij.formatting.*;
+import com.intellij.json.JsonParserDefinition;
+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.tree.IElementType;
+import com.intellij.psi.tree.TokenSet;
+import com.intellij.util.Function;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.json.psi.JsonProperty;
+import com.intellij.json.psi.JsonValue;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+
+import static com.intellij.json.JsonElementTypes.*;
+
+/**
+ * @author Mikhail Golubev
+ */
+public class JsonBlock implements ASTBlock {
+  private static final TokenSet OPEN_BRACES = TokenSet.create(L_BRACKET, L_CURLY);
+  private static final TokenSet CLOSE_BRACES = TokenSet.create(R_BRACKET, 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<Block> 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<Block> getSubBlocks() {
+    if (mySubBlocks == null) {
+      mySubBlocks = ContainerUtil.mapNotNull(myNode.getChildren(null), new Function<ASTNode, Block>() {
+        @Override
+        public Block fun(ASTNode node) {
+          if (isWhitespaceOrEmpty(node)) {
+            return null;
+          }
+          return makeSubBlock(node);
+        }
+      });
+    }
+    return mySubBlocks;
+  }
+
+  private Block makeSubBlock(ASTNode childNode) {
+    IElementType childNodeType = childNode.getElementType();
+    PsiElement childPsiElement = childNode.getPsi();
+
+    Indent indent = Indent.getNoneIndent();
+    Alignment alignment = null;
+    Wrap wrap = null;
+
+    if (isContainer() && childNodeType != COMMA && !BRACES.contains(childNodeType)) {
+      wrap = Wrap.createWrap(WrapType.NORMAL, true);
+      alignment = myChildAlignment;
+      indent = Indent.getNormalIndent();
+    }
+    if (myNode.getElementType() == PROPERTY && childPsiElement instanceof JsonValue) {
+      wrap = Wrap.createWrap(WrapType.NORMAL, true);
+      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) {
+    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() == COMMA) {
+        return new ChildAttributes(Indent.getNormalIndent(), myChildAlignment);
+      }
+    }
+//    // TODO find out why inside object then cursor is after '"a": []<cursor>', 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 == OBJECT) {
+      return lastChildNode != null && lastChildNode.getElementType() == R_CURLY;
+    }
+    else if (nodeType == ARRAY) {
+      return lastChildNode != null && lastChildNode.getElementType() == 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.CONTAINERS.contains(myNode.getElementType());
+  }
+
+}
diff --git a/plugins/json/src/com/intellij/json/formatter/JsonCodeStyleSettings.java b/plugins/json/src/com/intellij/json/formatter/JsonCodeStyleSettings.java
new file mode 100644 (file)
index 0000000..5bb29b5
--- /dev/null
@@ -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/plugins/json/src/com/intellij/json/formatter/JsonCodeStyleSettingsProvider.java b/plugins/json/src/com/intellij/json/formatter/JsonCodeStyleSettingsProvider.java
new file mode 100644 (file)
index 0000000..712a4cd
--- /dev/null
@@ -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/plugins/json/src/com/intellij/json/formatter/JsonEnterBetweenBracesHandler.java b/plugins/json/src/com/intellij/json/formatter/JsonEnterBetweenBracesHandler.java
new file mode 100644 (file)
index 0000000..71638e8
--- /dev/null
@@ -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/plugins/json/src/com/intellij/json/formatter/JsonFormattingBuilderModel.java b/plugins/json/src/com/intellij/json/formatter/JsonFormattingBuilderModel.java
new file mode 100644 (file)
index 0000000..041e609
--- /dev/null
@@ -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.RIGHT_MARGIN);
+    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/plugins/json/src/com/intellij/json/formatter/JsonLanguageCodeStyleSettingsProvider.java b/plugins/json/src/com/intellij/json/formatter/JsonLanguageCodeStyleSettingsProvider.java
new file mode 100644 (file)
index 0000000..541e54c
--- /dev/null
@@ -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/plugins/json/src/com/intellij/json/psi/JsonPsiImplUtils.java b/plugins/json/src/com/intellij/json/psi/JsonPsiImplUtils.java
new file mode 100644 (file)
index 0000000..80cdcee
--- /dev/null
@@ -0,0 +1,11 @@
+package com.intellij.json.psi;
+
+import com.intellij.openapi.util.text.StringUtil;
+import org.jetbrains.annotations.NotNull;
+
+public class JsonPsiImplUtils {
+  @NotNull
+  public static String getName(@NotNull JsonProperty property) {
+    return StringUtil.unquoteString(property.getPropertyName().getText());
+  }
+}
diff --git a/plugins/json/src/json.bnf b/plugins/json/src/json.bnf
new file mode 100644 (file)
index 0000000..51626b5
--- /dev/null
@@ -0,0 +1,73 @@
+{
+  parserClass='com.intellij.json.JsonParser'
+  parserUtilClass="com.intellij.json.JsonParserUtil"
+  psiPackage='com.intellij.json.psi'
+  psiImplPackage='com.intellij.json.psi.impl'
+
+  elementTypeHolderClass='com.intellij.json.JsonElementTypes'
+  elementTypeClass='com.intellij.json.JsonElementType'
+  psiClassPrefix="Json"
+
+  psiImplUtilClass='com.intellij.json.psi.JsonPsiImplUtils'
+  tokenTypeClass='com.intellij.json.JsonTokenType'
+
+    tokens = [
+        L_CURLY='{'
+        R_CURLY='}'
+        L_BRACKET='['
+        R_BRACKET=']'
+        
+        COMMA=','
+        COLON=':'
+        // COMMENT='regexp:"//.*|/\*.*?\*/"'
+        // else /\*(?:[^*]|\*[^/])*\*+/
+
+        // unclosed string literal matches till the line's end
+        // any escape sequences included, illegal escapes are indicated by SyntaxHighlighter
+        // and JsonStringLiteralAnnotator
+        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'
+        // Workaround for highlighting issue
+        // plays role of IDENTIFIER or NAME token in other languages
+        TEXT='regexp:\w+'
+    ]
+
+    extends("object|array|literal")=value
+    extends("string_literal|number_literal|boolean_literal|null_literal")=literal
+}
+
+json ::= object | array
+
+object ::= L_CURLY [properties] R_CURLY {
+  pin=1
+}
+private properties ::= property (COMMA property)* { pin(".*")=1 }
+property ::= property_name COLON value {
+  pin = 1
+  methods=[ getName ]
+  recoverWhile = brace_or_comma
+}
+
+array ::= L_BRACKET [array_elements] R_BRACKET { pin=1 }
+private array_elements ::= array_element (COMMA array_element)* { pin(".*")=1 }
+private array_element ::= value {
+  recoverWhile = bracket_or_comma
+}
+
+property_name ::= string_literal
+value ::= object | array | literal
+
+string_literal ::= STRING
+number_literal ::= NUMBER
+boolean_literal ::= TRUE | FALSE
+null_literal ::= NULL
+
+literal ::= string_literal | number_literal | boolean_literal | null_literal
+
+private bracket_or_comma ::= !(R_BRACKET|COMMA)
+private brace_or_comma ::= !(R_CURLY|COMMA)
\ No newline at end of file