Skip to content

Commit d6a50f0

Browse files
committed
Simple cypher formatter
1 parent 82749e6 commit d6a50f0

File tree

8 files changed

+348
-1
lines changed

8 files changed

+348
-1
lines changed

config/checkstyle/suppression.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@
88
<suppress files="[\\/]gen[\\/]" checks="[a-zA-Z0-9]*"/>
99
<suppress files="_CypherLexer.java" checks="[a-zA-Z0-9]*"/>
1010
<suppress files="DataSource.java" checks="VisibilityModifier"/>
11-
<suppress files="DataSource.java" checks="VisibilityModifier"/>
1211
<suppress files="DataSourceV\d+.java" checks="VisibilityModifier"/>
1312
<suppress files="DataSourcesComponentState.java" checks="VisibilityModifier"/>
1413
<suppress files="DataSourceContainerV\d+.java" checks="VisibilityModifier"/>
1514
<suppress files="[\w_]+Test.java" checks="[a-zA-Z0-9]*"/>
15+
<suppress files="CypherCodeStyleSettings.java" checks="MemberName"/>
16+
<suppress files="CypherCodeStyleSettings.java" checks="VisibilityModifier"/>
1617
</suppressions>

graph-database-support-plugin/src/main/resources/META-INF/plugin.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,10 @@
157157
<localInspection language="Cypher" displayName="Cypher EXPLAIN warning inspection" groupPath="Cypher"
158158
groupName="General" enabledByDefault="true" level="WARNING"
159159
implementationClass="com.neueda.jetbrains.plugin.graphdb.jetbrains.inspection.CypherExplainWarningInspection"/>
160+
<codeStyleSettingsProvider implementation="com.neueda.jetbrains.plugin.graphdb.jetbrains.formatter.CypherCodeStyleSettingsProvider"/>
161+
<langCodeStyleSettingsProvider implementation="com.neueda.jetbrains.plugin.graphdb.jetbrains.formatter.CypherLanguageCodeStyleSettingsProvider"/>
162+
163+
<lang.formatter language="Cypher" implementationClass="com.neueda.jetbrains.plugin.graphdb.jetbrains.formatter.CypherFormattingModelBuilder"/>
160164
</extensions>
161165

162166
<actions>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package com.neueda.jetbrains.plugin.graphdb.test.integration.neo4j.tests.cypher.formatting;
2+
3+
import com.intellij.openapi.command.WriteCommandAction;
4+
import com.intellij.psi.PsiFile;
5+
import com.intellij.psi.codeStyle.CodeStyleManager;
6+
import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
7+
import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
8+
import com.neueda.jetbrains.plugin.graphdb.language.cypher.CypherLanguage;
9+
import com.neueda.jetbrains.plugin.graphdb.test.integration.neo4j.util.base.BaseIntegrationTest;
10+
11+
import static java.util.Collections.singletonList;
12+
13+
public class CypherFormattingTest extends BaseIntegrationTest {
14+
15+
public void testFormatter() {
16+
PsiFile file = myFixture.addFileToProject("test.cypher", "match (a:Person)-[]-(b) return a,b;");
17+
myFixture.configureFromExistingVirtualFile(file.getVirtualFile());
18+
19+
CommonCodeStyleSettings settings = CodeStyleSettingsManager.getSettings(getProject())
20+
.getCommonSettings(CypherLanguage.INSTANCE);
21+
settings.SPACE_AFTER_COLON = true;
22+
settings.SPACE_WITHIN_BRACKETS = true;
23+
settings.SPACE_AFTER_COMMA = true;
24+
25+
new WriteCommandAction.Simple(getProject()) {
26+
@Override
27+
protected void run() throws Throwable {
28+
PsiFile f = myFixture.getFile();
29+
CodeStyleManager.getInstance(getProject())
30+
.reformatText(f, singletonList(f.getTextRange()));
31+
}
32+
}.execute();
33+
34+
myFixture.checkResult("match ( a: Person )-[]-( b ) return a, b;");
35+
}
36+
}
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
package com.neueda.jetbrains.plugin.graphdb.jetbrains.formatter;
2+
3+
import com.intellij.formatting.ASTBlock;
4+
import com.intellij.formatting.Alignment;
5+
import com.intellij.formatting.Block;
6+
import com.intellij.formatting.ChildAttributes;
7+
import com.intellij.formatting.Indent;
8+
import com.intellij.formatting.Spacing;
9+
import com.intellij.formatting.SpacingBuilder;
10+
import com.intellij.formatting.Wrap;
11+
import com.intellij.lang.ASTNode;
12+
import com.intellij.openapi.util.TextRange;
13+
import com.intellij.psi.TokenType;
14+
import com.intellij.psi.codeStyle.CodeStyleSettings;
15+
import org.jetbrains.annotations.NotNull;
16+
import org.jetbrains.annotations.Nullable;
17+
18+
import java.util.List;
19+
import java.util.stream.Stream;
20+
21+
import static java.util.stream.Collectors.toList;
22+
23+
public class CypherBlock implements ASTBlock {
24+
25+
private ASTNode node;
26+
private Wrap wrap;
27+
private Indent indent;
28+
private Alignment alignment;
29+
private SpacingBuilder spacingBuilder;
30+
31+
private List<Block> subBlocks = null;
32+
private CodeStyleSettings codeStyleSettings;
33+
34+
CypherBlock(@NotNull ASTNode node,
35+
@NotNull CodeStyleSettings settings,
36+
@Nullable Wrap wrap,
37+
@Nullable Indent indent,
38+
@Nullable Alignment alignment,
39+
SpacingBuilder spacingBuilder) {
40+
this.node = node;
41+
this.codeStyleSettings = settings;
42+
this.wrap = wrap;
43+
this.indent = indent;
44+
this.alignment = alignment;
45+
this.spacingBuilder = spacingBuilder;
46+
}
47+
48+
@Override
49+
public ASTNode getNode() {
50+
return node;
51+
}
52+
53+
@NotNull
54+
@Override
55+
public TextRange getTextRange() {
56+
return node.getTextRange();
57+
}
58+
59+
@NotNull
60+
@Override
61+
public List<Block> getSubBlocks() {
62+
if (subBlocks == null) {
63+
subBlocks = Stream.of(node.getChildren(null))
64+
.filter(node -> !CypherBlock.isWhitespaceOrEmpty(node))
65+
.map(this::makeSubBlock)
66+
.collect(toList());
67+
}
68+
return subBlocks;
69+
}
70+
71+
private Block makeSubBlock(@NotNull ASTNode node) {
72+
return new CypherBlock(node, codeStyleSettings, null, Indent.getNoneIndent(), null, spacingBuilder);
73+
}
74+
75+
@Nullable
76+
@Override
77+
public Wrap getWrap() {
78+
return wrap;
79+
}
80+
81+
@Nullable
82+
@Override
83+
public Indent getIndent() {
84+
return indent;
85+
}
86+
87+
@Nullable
88+
@Override
89+
public Alignment getAlignment() {
90+
return alignment;
91+
}
92+
93+
@Nullable
94+
@Override
95+
public Spacing getSpacing(@Nullable Block child1, @NotNull Block child2) {
96+
return spacingBuilder.getSpacing(this, child1, child2);
97+
}
98+
99+
@NotNull
100+
@Override
101+
public ChildAttributes getChildAttributes(int newChildIndex) {
102+
return new ChildAttributes(null, null);
103+
}
104+
105+
@Override
106+
public boolean isIncomplete() {
107+
return false;
108+
}
109+
110+
@Override
111+
public boolean isLeaf() {
112+
return node.getFirstChildNode() == null;
113+
}
114+
115+
private static boolean isWhitespaceOrEmpty(ASTNode node) {
116+
return node.getElementType() == TokenType.WHITE_SPACE || node.getTextLength() == 0;
117+
}
118+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.neueda.jetbrains.plugin.graphdb.jetbrains.formatter;
2+
3+
import com.intellij.psi.codeStyle.CodeStyleSettings;
4+
import com.intellij.psi.codeStyle.CustomCodeStyleSettings;
5+
6+
public class CypherCodeStyleSettings extends CustomCodeStyleSettings {
7+
8+
public boolean SPACE_AFTER_COLON = true;
9+
public boolean SPACE_BEFORE_COLON = false;
10+
11+
CypherCodeStyleSettings(CodeStyleSettings container) {
12+
super("CypherCodeStyleSettings", container);
13+
}
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package com.neueda.jetbrains.plugin.graphdb.jetbrains.formatter;
2+
3+
import com.intellij.application.options.CodeStyleAbstractConfigurable;
4+
import com.intellij.application.options.CodeStyleAbstractPanel;
5+
import com.intellij.application.options.TabbedLanguageCodeStylePanel;
6+
import com.intellij.openapi.options.Configurable;
7+
import com.intellij.psi.codeStyle.CodeStyleSettings;
8+
import com.intellij.psi.codeStyle.CodeStyleSettingsProvider;
9+
import com.intellij.psi.codeStyle.CustomCodeStyleSettings;
10+
import com.neueda.jetbrains.plugin.graphdb.language.cypher.CypherLanguage;
11+
import org.jetbrains.annotations.NotNull;
12+
import org.jetbrains.annotations.Nullable;
13+
14+
public class CypherCodeStyleSettingsProvider extends CodeStyleSettingsProvider {
15+
16+
@Nullable
17+
@Override
18+
public CustomCodeStyleSettings createCustomSettings(CodeStyleSettings settings) {
19+
return new CypherCodeStyleSettings(settings);
20+
}
21+
22+
@Nullable
23+
@Override
24+
public String getConfigurableDisplayName() {
25+
return "Cypher";
26+
}
27+
28+
@NotNull
29+
@Override
30+
public Configurable createSettingsPage(CodeStyleSettings settings, CodeStyleSettings originalSettings) {
31+
return new CodeStyleAbstractConfigurable(settings, originalSettings, "Cypher") {
32+
@Override
33+
protected CodeStyleAbstractPanel createPanel(CodeStyleSettings settings) {
34+
return new CypherCodeStyleMainPanel(getCurrentSettings(), settings);
35+
}
36+
37+
@Nullable
38+
@Override
39+
public String getHelpTopic() {
40+
return null;
41+
}
42+
};
43+
}
44+
45+
private static class CypherCodeStyleMainPanel extends TabbedLanguageCodeStylePanel {
46+
CypherCodeStyleMainPanel(CodeStyleSettings currentSettings, CodeStyleSettings settings) {
47+
super(CypherLanguage.INSTANCE, currentSettings, settings);
48+
}
49+
}
50+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package com.neueda.jetbrains.plugin.graphdb.jetbrains.formatter;
2+
3+
import com.intellij.formatting.Alignment;
4+
import com.intellij.formatting.FormattingModel;
5+
import com.intellij.formatting.FormattingModelBuilder;
6+
import com.intellij.formatting.FormattingModelProvider;
7+
import com.intellij.formatting.Indent;
8+
import com.intellij.formatting.SpacingBuilder;
9+
import com.intellij.formatting.Wrap;
10+
import com.intellij.formatting.WrapType;
11+
import com.intellij.lang.ASTNode;
12+
import com.intellij.openapi.util.TextRange;
13+
import com.intellij.psi.PsiElement;
14+
import com.intellij.psi.PsiFile;
15+
import com.intellij.psi.codeStyle.CodeStyleSettings;
16+
import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
17+
import com.neueda.jetbrains.plugin.graphdb.language.cypher.CypherLanguage;
18+
import org.jetbrains.annotations.NotNull;
19+
import org.jetbrains.annotations.Nullable;
20+
21+
import static com.neueda.jetbrains.plugin.graphdb.language.cypher.psi.CypherTypes.*;
22+
23+
public class CypherFormattingModelBuilder implements FormattingModelBuilder {
24+
@NotNull
25+
@Override
26+
public FormattingModel createModel(PsiElement element, CodeStyleSettings settings) {
27+
CypherBlock block = new CypherBlock(element.getNode(), settings, Wrap.createWrap(WrapType.NONE, false),
28+
Indent.getNoneIndent(),
29+
Alignment.createAlignment(),
30+
createSpacingBuilder(settings));
31+
return FormattingModelProvider.createFormattingModelForPsiFile(element.getContainingFile(), block, settings);
32+
}
33+
34+
@Nullable
35+
@Override
36+
public TextRange getRangeAffectingIndent(PsiFile file, int offset, ASTNode elementAtOffset) {
37+
return null;
38+
}
39+
40+
private static SpacingBuilder createSpacingBuilder(CodeStyleSettings settings) {
41+
CypherCodeStyleSettings customSettings = settings.getCustomSettings(CypherCodeStyleSettings.class);
42+
CommonCodeStyleSettings commonSettings = settings.getCommonSettings(CypherLanguage.INSTANCE);
43+
44+
final int spacesBeforeComma = commonSettings.SPACE_BEFORE_COMMA ? 1 : 0;
45+
final int spacesBeforeColon = customSettings.SPACE_BEFORE_COLON ? 1 : 0;
46+
final int spacesAfterColon = customSettings.SPACE_AFTER_COLON ? 1 : 0;
47+
48+
return new SpacingBuilder(settings, CypherLanguage.INSTANCE)
49+
.before(OP_COLON).spacing(spacesBeforeColon, spacesBeforeColon, 0, false, 0)
50+
.after(OP_COLON).spacing(spacesAfterColon, spacesAfterColon, 0, false, 0)
51+
.withinPair(PARENTHESE_OPEN, PARENTHESE_CLOSE)
52+
.spaceIf(commonSettings.SPACE_WITHIN_BRACKETS, true)
53+
.withinPair(BRACKET_CURLYOPEN, BRACKET_CURLYCLOSE)
54+
.spaceIf(commonSettings.SPACE_WITHIN_BRACES, true)
55+
.before(OP_COMMA).spacing(spacesBeforeComma, spacesBeforeComma, 0, false, 0)
56+
.after(OP_COMMA).spaceIf(commonSettings.SPACE_AFTER_COMMA);
57+
}
58+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package com.neueda.jetbrains.plugin.graphdb.jetbrains.formatter;
2+
3+
import com.intellij.application.options.IndentOptionsEditor;
4+
import com.intellij.application.options.SmartIndentOptionsEditor;
5+
import com.intellij.lang.Language;
6+
import com.intellij.psi.codeStyle.CodeStyleSettingsCustomizable;
7+
import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
8+
import com.intellij.psi.codeStyle.LanguageCodeStyleSettingsProvider;
9+
import com.neueda.jetbrains.plugin.graphdb.language.cypher.CypherLanguage;
10+
import org.jetbrains.annotations.NotNull;
11+
import org.jetbrains.annotations.Nullable;
12+
13+
public class CypherLanguageCodeStyleSettingsProvider extends LanguageCodeStyleSettingsProvider {
14+
15+
@NotNull
16+
@Override
17+
public Language getLanguage() {
18+
return CypherLanguage.INSTANCE;
19+
}
20+
21+
@Override
22+
public void customizeSettings(@NotNull CodeStyleSettingsCustomizable consumer, @NotNull SettingsType settingsType) {
23+
if (settingsType == SettingsType.SPACING_SETTINGS) {
24+
consumer.showCustomOption(CypherCodeStyleSettings.class,
25+
"SPACE_BEFORE_COLON",
26+
"Before ':'",
27+
CodeStyleSettingsCustomizable.SPACES_OTHER);
28+
29+
consumer.showCustomOption(CypherCodeStyleSettings.class,
30+
"SPACE_AFTER_COLON",
31+
"After ':'",
32+
CodeStyleSettingsCustomizable.SPACES_OTHER);
33+
34+
consumer.showStandardOptions("SPACE_WITHIN_BRACKETS",
35+
"SPACE_WITHIN_BRACES",
36+
"SPACE_AFTER_COMMA",
37+
"SPACE_BEFORE_COMMA");
38+
} else if (settingsType == SettingsType.BLANK_LINES_SETTINGS) {
39+
consumer.showStandardOptions("KEEP_BLANK_LINES_IN_CODE");
40+
} else if (settingsType == SettingsType.WRAPPING_AND_BRACES_SETTINGS) {
41+
consumer.showStandardOptions("RIGHT_MARGIN",
42+
"WRAP_ON_TYPING",
43+
"KEEP_LINE_BREAKS",
44+
"WRAP_LONG_LINES");
45+
}
46+
}
47+
48+
@Nullable
49+
@Override
50+
public IndentOptionsEditor getIndentOptionsEditor() {
51+
return new SmartIndentOptionsEditor();
52+
}
53+
54+
@Nullable
55+
@Override
56+
public CommonCodeStyleSettings getDefaultCommonSettings() {
57+
return new CommonCodeStyleSettings(CypherLanguage.INSTANCE);
58+
}
59+
60+
@Override
61+
public String getCodeSample(@NotNull SettingsType settingsType) {
62+
return "MATCH (a:Person)-[r:WORKS_IN]->(b:Company)\n"
63+
+ "WHERE a.name='Peter'\n"
64+
+ "RETURN a,b,r;";
65+
}
66+
}

0 commit comments

Comments
 (0)