Skip to content

Commit edcd0b8

Browse files
authored
Merge pull request #29 from anboralabs/minor_changes
Minor changes
2 parents d738101 + 3a0e5b9 commit edcd0b8

File tree

13 files changed

+220
-74
lines changed

13 files changed

+220
-74
lines changed

build.gradle

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ grammarKit {
1313
}
1414

1515
group 'co.anbora.labs'
16-
version '2.0.0-SNAPSHOT'
16+
version '2.1.0-SNAPSHOT'
1717

1818
repositories {
1919
mavenCentral()
@@ -75,6 +75,13 @@ publishPlugin {
7575
patchPluginXml {
7676
changeNotes """
7777
<ul>
78+
<li><b>2.1.0</b> <em>(2021-01-10)</em> - Added Intellij formatting code support</li>
79+
<ul>
80+
<li>Added formatting code support </li>
81+
<li>Added oficial firebase icons </li>
82+
<li>Improved language bnf rules </li>
83+
</ul>
84+
</li>
7885
<li><b>2.0.0</b> <em>(2020-12-12)</em> - Added support block comments</li>
7986
<ul>
8087
<li>Added support for block comments </li>

src/main/grammar/FirebaseRules.bnf

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
tokenTypeClass="co.anbora.labs.firebase.lang.core.psi.FirebaseTokenType"
1515

1616
tokens = [
17-
WHITE_SPACE='regexp:\s+'
17+
WHITE_SPACE='WHITE_SPACE'
1818
LP = '('
1919
RP = ')'
2020
LB = '['
@@ -62,7 +62,11 @@ ConditionalExpression ::= Expression (BooleanOperator Expression)*
6262

6363
ObjectStatement ::= IDENTIFIER(DOT IDENTIFIER)*
6464

65-
CallFunctionStatement ::= (ArrayLiteralStatement|IDENTIFIER)(DOT IDENTIFIER)?FunctionParameterStatement
65+
ObjectMethodCallStatement ::= (ArrayLiteralStatement (DOT IDENTIFIER)*|ObjectStatement)FunctionParameterStatement
66+
67+
CallFunctionStatement ::= ObjectMethodCallStatement (DOT ObjectMethodCallStatement)*
68+
69+
FunctionDeclarationStatement ::= IDENTIFIER FunctionParameterStatement
6670

6771
FunctionParameterStatement ::= LP ParameterStatement? RP
6872

@@ -78,7 +82,7 @@ ExistBuiltInFunctionStatement ::= EXITS_KEYWORD LP FullBuiltInParameterStatement
7882

7983
BuiltInFunctionStatement ::= (GetBuiltInFunctionStatement|ExistBuiltInFunctionStatement)
8084

81-
FunctionStatement ::= FUNCTION_KEYWORD CallFunctionStatement LEFT_BRACE ReturnStatement RIGHT_BRACE
85+
FunctionStatement ::= FUNCTION_KEYWORD FunctionDeclarationStatement LEFT_BRACE ReturnStatement RIGHT_BRACE
8286

8387
ReturnStatement ::= RETURN_KEYWORD ConditionalExpression DOT_COMMA
8488

src/main/grammar/FirebaseRules.flex

Lines changed: 27 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -32,23 +32,21 @@ import com.intellij.psi.TokenType;
3232
}
3333
%}
3434

35-
%xstate COMMENT
35+
%xstate COMMENT TYPE_PENDING
3636

37-
EOL="\r"|"\n"|"\r\n"
38-
LINE_WS=[\ \t\f]
39-
WHITE_SPACE=({LINE_WS}|{EOL})+
37+
WhiteSpace = [ \n\t\f]+
4038

41-
NUMBER=[0-9]+(\.[0-9]*)?
42-
STRING=('([^'\\]|\\.)*'|\"([^\"\\]|\\.)*\")
43-
SLASH=\/
44-
LINE_COMMENT=(("//")[^\r\n]*|{EOL})+
39+
Number=[0-9]+(\.[0-9]*)?
40+
String=('([^'\\]|\\.)*'|\"([^\"\\]|\\.)*\")
41+
Slash=\/
42+
LineComment=("//")[^\r\n]*
4543

46-
SERVICE_NAME=(cloud.firestore|firebase.storage)
47-
RULES_VERSION=rules_version
48-
VERSIONS=('1'|'2')
49-
IDENTIFIER=[a-zA-Z_\-0-9]+
50-
PATH_VARIABLE=[{][a-zA-Z_\-0-9]+(=\*\*)?[}]
51-
PATH_BUILT_IN=[$][(][a-zA-Z_\-0-9]+[a-zA-Z_\.\-0-9]*[)]
44+
ServiceName=(cloud.firestore|firebase.storage)
45+
RulesVersion=rules_version
46+
Versions=('1'|'2')
47+
Identifier=[a-zA-Z_\-0-9]+
48+
PathVariable=[{][a-zA-Z_\-0-9]+(=\*\*)?[}]
49+
PathBuiltIn=[$][(][a-zA-Z_\-0-9]+[a-zA-Z_\.\-0-9]*[)]
5250

5351
%%
5452

@@ -68,27 +66,30 @@ PATH_BUILT_IN=[$][(][a-zA-Z_\-0-9]+[a-zA-Z_\.\-0-9]*[)]
6866
[^] { }
6967
}
7068

69+
<TYPE_PENDING> {
70+
{WhiteSpace} { return TokenType.WHITE_SPACE; }
71+
}
72+
7173
<YYINITIAL> {
72-
{WHITE_SPACE} { return com.intellij.psi.TokenType.WHITE_SPACE; }
73-
{LINE_COMMENT} { return LINE_COMMENT; }
74+
{LineComment} { return LINE_COMMENT; }
7475
"/*" {
7576
startComment();
7677
}
7778

78-
{PATH_BUILT_IN} { return PATH_BUILT_IN; }
79-
{PATH_VARIABLE} { return PATH_VARIABLE; }
79+
{PathBuiltIn} { return PATH_BUILT_IN; }
80+
{PathVariable} { return PATH_VARIABLE; }
8081
"true" { return TRUE_KEYWORD; }
8182
"false" { return FALSE_KEYWORD; }
8283
"if" { return IF_KEYWORD; }
8384
"null" { return NULL_KEYWORD; }
8485
"in" { return IN_KEYWORD; }
8586

8687
"service" { return SERVICE_KEYWORD; }
87-
{SERVICE_NAME} { return SERVICE_NAME; }
88+
{ServiceName} { return SERVICE_NAME; }
8889
"match" { return MATCH_KEYWORD; }
8990
"allow" { return ALLOW_KEYWORD; }
90-
{RULES_VERSION} { return RULES_VERSION; }
91-
{VERSIONS} { return VERSIONS; }
91+
{RulesVersion} { return RULES_VERSION; }
92+
{Versions} { return VERSIONS; }
9293
"function" { return FUNCTION_KEYWORD; }
9394
"return" { return RETURN_KEYWORD; }
9495

@@ -128,12 +129,12 @@ PATH_BUILT_IN=[$][(][a-zA-Z_\-0-9]+[a-zA-Z_\.\-0-9]*[)]
128129
"." { return DOT; }
129130
";" { return DOT_COMMA; }
130131

131-
{NUMBER} { return NUMBER; }
132-
{STRING} { return STRING; }
133-
{IDENTIFIER} { return IDENTIFIER; }
134-
{SLASH} { return SLASH; }
132+
{Number} { return NUMBER; }
133+
{String} { return STRING; }
134+
{Identifier} { return IDENTIFIER; }
135+
{Slash} { return SLASH; }
135136

136-
{WHITE_SPACE} { return WHITE_SPACE; }
137+
{WhiteSpace} { return com.intellij.psi.TokenType.WHITE_SPACE; }
137138

138139
[^] { return com.intellij.psi.TokenType.BAD_CHARACTER; }
139140
}

src/main/kotlin/co/anbora/labs/firebase/ide/color/FirebaseColorSettingPage.kt

Lines changed: 30 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -24,38 +24,36 @@ class FirebaseColorSettingPage: ColorSettingsPage {
2424
override fun getHighlighter(): SyntaxHighlighter = FirebaseSyntaxHighlighter()
2525

2626
override fun getDemoText(): String =
27-
"""
28-
rules_version = '2';
29-
service cloud.firestore {
30-
// Allow the requestor to read or delete any resource on a path under the
31-
// user directory.
32-
match /users/{userId}/{anyUserFile=**} {
33-
allow read, delete: if request.auth != null && request.auth.uid == userId;
34-
}
35-
36-
match /databases/{database}/documents {
37-
// True if the user is signed in or the requested data is 'public'
38-
function signedInOrPublic() {
39-
return request.auth.uid != null || resource.data.visibility == 'public';
40-
}
41-
match /{role}/{document=**} {
42-
allow read, write: if
43-
request.time < timestamp.date(2020, 9, 23) && role in request.auth.token.authorities;
44-
}
45-
}
46-
// Allow the requestor to create or update their own images.
47-
// When 'request.method' == 'delete' this rule and the one matching
48-
// any path under the user directory would both match and the `delete`
49-
// would be permitted.
50-
51-
match /users/{userId}/images/{imageId} {
52-
// Whether to permit the request depends on the logical OR of all
53-
// matched rules. This means that even if this rule did not explicitly
54-
// allow the 'delete' the earlier rule would have.
55-
allow write: if request.auth != null && request.auth.uid == userId && imageId.matches('*.png');
56-
}
57-
}
58-
"""
27+
"""rules_version = '2';
28+
service cloud.firestore {
29+
// Allow the requestor to read or delete any resource on a path under the
30+
// user directory.
31+
match /users/{userId}/{anyUserFile=**} {
32+
allow read, delete: if request.auth != null && request.auth.uid == userId;
33+
}
34+
35+
match /databases/{database}/documents {
36+
// True if the user is signed in or the requested data is 'public'
37+
function signedInOrPublic() {
38+
return request.auth.uid != null || resource.data.visibility == 'public';
39+
}
40+
match /{role}/{document=**} {
41+
allow read, write: if
42+
request.time < timestamp.date(2020, 9, 23) && role in request.auth.token.authorities;
43+
}
44+
}
45+
// Allow the requestor to create or update their own images.
46+
// When 'request.method' == 'delete' this rule and the one matching
47+
// any path under the user directory would both match and the `delete`
48+
// would be permitted.
49+
50+
match /users/{userId}/images/{imageId} {
51+
// Whether to permit the request depends on the logical OR of all
52+
// matched rules. This means that even if this rule did not explicitly
53+
// allow the 'delete' the earlier rule would have.
54+
allow write: if request.auth != null && request.auth.uid == userId && imageId.matches('*.png');
55+
}
56+
}"""
5957

6058
override fun getAdditionalHighlightingTagToDescriptorMap(): MutableMap<String, TextAttributesKey> = mutableMapOf()
6159
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package co.anbora.labs.firebase.ide.formatter
2+
3+
import com.intellij.formatting.*
4+
import com.intellij.lang.ASTNode
5+
import com.intellij.psi.formatter.common.AbstractBlock
6+
7+
class FirebaseFormatterBlock(
8+
node: ASTNode,
9+
wrap: Wrap? = null,
10+
alignment: Alignment? = null,
11+
val spacingBuilder: SpacingBuilder
12+
): AbstractBlock(node, wrap, alignment) {
13+
14+
override fun isLeaf(): Boolean = node.firstChildNode == null
15+
16+
override fun getIndent(): Indent?
17+
= computeIndent()
18+
19+
override fun getChildAttributes(newChildIndex: Int): ChildAttributes
20+
= computeChildAttributes()
21+
22+
override fun getSpacing(child1: Block?, child2: Block): Spacing?
23+
= computeSpacing(child1, child2)
24+
25+
override fun getSubBlocks(): List<Block> = mySubBlocks
26+
27+
private val mySubBlocks: List<Block> by lazy { buildChildren() }
28+
29+
override fun buildChildren(): List<Block> {
30+
return node.getChildren(null)
31+
.filter { !it.isWhitespaceOrEmpty() }
32+
.map { childNode: ASTNode ->
33+
FirebaseFormatterBlock(
34+
node = childNode,
35+
spacingBuilder = spacingBuilder
36+
)
37+
}
38+
}
39+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package co.anbora.labs.firebase.ide.formatter
2+
3+
import com.intellij.formatting.*
4+
5+
class FirebaseFormattingModelBuilder: FormattingModelBuilder {
6+
7+
override fun createModel(formattingContext: FormattingContext): FormattingModel {
8+
return FormattingModelProvider.createFormattingModelForPsiFile(
9+
formattingContext.containingFile,
10+
FirebaseFormatterBlock(
11+
node = formattingContext.node,
12+
spacingBuilder = createSpacingBuilder(formattingContext.codeStyleSettings)
13+
),
14+
formattingContext.codeStyleSettings
15+
)
16+
}
17+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package co.anbora.labs.firebase.ide.formatter
2+
3+
import co.anbora.labs.firebase.lang.core.FirebaseRulesLanguage
4+
import co.anbora.labs.firebase.lang.core.psi.FirebaseRulesTypes.*
5+
import com.intellij.formatting.*
6+
import com.intellij.lang.ASTNode
7+
import com.intellij.psi.TokenType
8+
import com.intellij.psi.codeStyle.CodeStyleSettings
9+
import com.intellij.psi.tree.IElementType
10+
11+
fun FirebaseFormatterBlock.computeIndent(): Indent? {
12+
val parent = node.treeParent
13+
return when {
14+
parent?.treeParent == null -> Indent.getNoneIndent()
15+
node.isBetweenBraces() -> Indent.getNormalIndent()
16+
else -> Indent.getNoneIndent()
17+
}
18+
}
19+
20+
fun FirebaseFormatterBlock.computeChildAttributes(): ChildAttributes {
21+
val indent = when {
22+
node.isComposeBlock() -> Indent.getNormalIndent()
23+
// Otherwise we don't want any indentation (null means continuation indent)
24+
else -> Indent.getNoneIndent()
25+
}
26+
return ChildAttributes(indent, null)
27+
}
28+
29+
fun FirebaseFormatterBlock.computeSpacing(child1: Block?, child2: Block): Spacing? = this.spacingBuilder.getSpacing(this, child1, child2)
30+
31+
fun createSpacingBuilder(commonSettings: CodeStyleSettings): SpacingBuilder {
32+
return SpacingBuilder(commonSettings, FirebaseRulesLanguage)
33+
//Rules Version
34+
.after(RULES_VERSION).spacing(1, 1, 0, false, 0)
35+
.after(EQ).spacing(1, 1, 0, false, 0)
36+
.after(VERSIONS).spacing(0,0,0,false,0)
37+
//Service Statement
38+
.after(SERVICE_KEYWORD).spacing(1, 1, 0, false, 0)
39+
.after(SERVICE_NAME).spacing(1, 1, 0, false, 0)
40+
//Function Statement
41+
.after(FUNCTION_KEYWORD).spacing(1, 1, 0, false, 0)
42+
.after(CALL_FUNCTION_STATEMENT).spacing(1, 1, 0, false, 0)
43+
//Match Statement
44+
.after(MATCH_KEYWORD).spacing(1, 1, 0, false, 0)
45+
.after(FULL_PATH_STATEMENT).spacing(1, 1, 0, false, 0)
46+
//Allow Statement
47+
.after(ALLOW_KEYWORD).spacing(1, 1, 0, false, 0)
48+
.after(PERMISSION_KEY_WORD).spacing(0,0,0,false,0)
49+
.after(COMMA).spacing(1, 1, 0, false, 0)
50+
.after(COLON).spacing(1, 1, 0, false, 0)
51+
.after(IF_KEYWORD).spacing(1, 1, 0, false, 0)
52+
.after(EXPRESSION).spacing(1, 1, 0, false, 0)
53+
.after(BOOLEAN_OPERATOR).spacing(1, 1, 0, false, 0)
54+
.before(DOT_COMMA).spacing(0,0,0,false,0)
55+
}
56+
57+
fun ASTNode.isBetweenBraces(): Boolean {
58+
val elementType: IElementType = this.elementType
59+
if (elementType === LEFT_BRACE || elementType === RIGHT_BRACE) return false
60+
61+
var sibling: ASTNode? = this.treePrev
62+
while (sibling != null) {
63+
if (sibling.elementType === LEFT_BRACE) return true
64+
sibling = sibling.treePrev
65+
}
66+
67+
return false
68+
}
69+
70+
fun ASTNode.isComposeBlock(): Boolean {
71+
val elementType: IElementType = this.elementType
72+
return elementType == SERVICE_STATEMENT ||
73+
elementType == MATCH_STATEMENT ||
74+
elementType == FUNCTION_STATEMENT
75+
}
76+
77+
fun ASTNode?.isWhitespaceOrEmpty() = this == null || textLength == 0 || elementType == TokenType.WHITE_SPACE

src/main/kotlin/co/anbora/labs/firebase/ide/highlight/FirebaseSyntaxHighlighter.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ class FirebaseSyntaxHighlighter: SyntaxHighlighterBase() {
1818
companion object {
1919
fun map(tokenType: IElementType?): FirebaseColors? =
2020
when (tokenType) {
21-
SERVICE_KEYWORD, MATCH_KEYWORD, ALLOW_KEYWORD,
21+
RULES_VERSION, DOT_COMMA, SERVICE_KEYWORD, MATCH_KEYWORD, ALLOW_KEYWORD,
2222
TRUE_KEYWORD, FALSE_KEYWORD, NULL_KEYWORD,
2323
IF_KEYWORD, FUNCTION_KEYWORD, RETURN_KEYWORD,
2424
IN_KEYWORD -> FirebaseColors.KEY_WORD

src/main/kotlin/co/anbora/labs/firebase/ide/icons/FirebaseIcons.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ import javax.swing.Icon
55

66
object FirebaseIcons {
77

8-
val FILE = getIcon("icon.png")
8+
val FILE = getIcon("icon_file.svg")
99

1010
private fun getIcon(path: String): Icon {
11-
return IconLoader.getIcon("/icons/$path")
11+
return IconLoader.findIcon("/icons/$path") as Icon
1212
}
1313
}

src/main/kotlin/co/anbora/labs/firebase/lang/core/FirebaseRulesLanguage.kt

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ package co.anbora.labs.firebase.lang.core
22

33
import co.anbora.labs.firebase.ide.icons.FirebaseIcons
44
import com.intellij.lang.Language
5-
import com.intellij.openapi.fileTypes.FileTypeConsumer
6-
import com.intellij.openapi.fileTypes.FileTypeFactory
75
import com.intellij.openapi.fileTypes.LanguageFileType
86
import javax.swing.Icon
97

@@ -13,7 +11,7 @@ object FirebaseFileType: LanguageFileType(FirebaseRulesLanguage) {
1311

1412
const val EXTENSION = "rules"
1513

16-
override fun getName(): String = "Firebase Rule File"
14+
override fun getName(): String = "Firebase Rules"
1715

1816
override fun getDescription(): String = "Firebase rules configurations"
1917

@@ -22,9 +20,3 @@ object FirebaseFileType: LanguageFileType(FirebaseRulesLanguage) {
2220
override fun getIcon(): Icon = FirebaseIcons.FILE
2321

2422
}
25-
26-
class FirebaseFileTypeFactory : FileTypeFactory() {
27-
override fun createFileTypes(consumer: FileTypeConsumer) {
28-
consumer.consume(FirebaseFileType, FirebaseFileType.EXTENSION)
29-
}
30-
}

0 commit comments

Comments
 (0)