From 8c1dfac552bc6c2867307828934ea5162a748698 Mon Sep 17 00:00:00 2001 From: Jakub Blach Date: Thu, 11 Nov 2021 16:28:06 +0100 Subject: [PATCH 1/3] WIP: Produce and parse indent/dedent tokens --- gen/gdscript/parser/ScriptParser.java | 24 ++- gen/gdscript/psi/ScriptBlock.java | 82 +++++++++ gen/gdscript/psi/ScriptElementTypes.java | 6 + gen/gdscript/psi/ScriptFuncStatement.java | 3 + gen/gdscript/psi/ScriptVisitor.java | 4 + gen/gdscript/psi/impl/ScriptBlockImpl.java | 174 ++++++++++++++++++ .../psi/impl/ScriptFuncStatementImpl.java | 6 + grammar/ScriptParser.bnf | 3 +- .../gdscript/lexer/ScriptIndentLexer.kt | 67 +++++++ .../gdscript/lexer/ScriptIndentLexerTest.kt | 23 +++ 10 files changed, 390 insertions(+), 2 deletions(-) create mode 100644 gen/gdscript/psi/ScriptBlock.java create mode 100644 gen/gdscript/psi/impl/ScriptBlockImpl.java create mode 100644 src/main/kotlin/gdscript/lexer/ScriptIndentLexer.kt create mode 100644 src/test/kotlin/gdscript/lexer/ScriptIndentLexerTest.kt diff --git a/gen/gdscript/parser/ScriptParser.java b/gen/gdscript/parser/ScriptParser.java index 0d982bf..26e0bdf 100644 --- a/gen/gdscript/parser/ScriptParser.java +++ b/gen/gdscript/parser/ScriptParser.java @@ -255,6 +255,20 @@ public static boolean await_statement(PsiBuilder b, int l) { return r; } + /* ********************************************************** */ + // INDENT statement DEDENT + public static boolean block(PsiBuilder b, int l) { + if (!recursion_guard_(b, l, "block")) return false; + if (!nextTokenIs(b, INDENT)) return false; + boolean r; + Marker m = enter_section_(b); + r = consumeToken(b, INDENT); + r = r && statement(b, l + 1); + r = r && consumeToken(b, DEDENT); + exit_section_(b, m, BLOCK, r); + return r; + } + /* ********************************************************** */ // CLASS id (EXTENDS type)? COLON public static boolean class_statement(PsiBuilder b, int l) { @@ -968,7 +982,7 @@ private static boolean func_argument_2_0_0(PsiBuilder b, int l) { } /* ********************************************************** */ - // STATIC? network_modifier? FUNC id L_PAREN LINE_BREAK* func_argument? LINE_BREAK* (COMMA LINE_BREAK* func_argument LINE_BREAK*)* COMMA? LINE_BREAK* R_PAREN (ARROW type)? COLON + // STATIC? network_modifier? FUNC id L_PAREN LINE_BREAK* func_argument? LINE_BREAK* (COMMA LINE_BREAK* func_argument LINE_BREAK*)* COMMA? LINE_BREAK* R_PAREN (ARROW type)? COLON block? public static boolean func_statement(PsiBuilder b, int l) { if (!recursion_guard_(b, l, "func_statement")) return false; boolean r; @@ -987,6 +1001,7 @@ public static boolean func_statement(PsiBuilder b, int l) { r = r && consumeToken(b, R_PAREN); r = r && func_statement_12(b, l + 1); r = r && consumeToken(b, COLON); + r = r && func_statement_14(b, l + 1); exit_section_(b, l, m, r, false, null); return r; } @@ -1116,6 +1131,13 @@ private static boolean func_statement_12_0(PsiBuilder b, int l) { return r; } + // block? + private static boolean func_statement_14(PsiBuilder b, int l) { + if (!recursion_guard_(b, l, "func_statement_14")) return false; + block(b, l + 1); + return true; + } + /* ********************************************************** */ // AT_ICON L_PAREN string R_PAREN public static boolean icon_statement(PsiBuilder b, int l) { diff --git a/gen/gdscript/psi/ScriptBlock.java b/gen/gdscript/psi/ScriptBlock.java new file mode 100644 index 0000000..de2a91a --- /dev/null +++ b/gen/gdscript/psi/ScriptBlock.java @@ -0,0 +1,82 @@ +// This is a generated file. Not intended for manual editing. +package gdscript.psi; + +import java.util.List; +import org.jetbrains.annotations.*; +import com.intellij.psi.PsiElement; + +public interface ScriptBlock extends PsiElement { + + @Nullable + ScriptAssertStatement getAssertStatement(); + + @Nullable + ScriptAssignStatement getAssignStatement(); + + @Nullable + ScriptAwaitStatement getAwaitStatement(); + + @Nullable + ScriptClassStatement getClassStatement(); + + @Nullable + ScriptClassnameStatement getClassnameStatement(); + + @Nullable + ScriptConstStatement getConstStatement(); + + @Nullable + ScriptElifStatement getElifStatement(); + + @Nullable + ScriptElseStatement getElseStatement(); + + @Nullable + ScriptEnumStatement getEnumStatement(); + + @Nullable + ScriptExpressionStatement getExpressionStatement(); + + @Nullable + ScriptExtendsStatement getExtendsStatement(); + + @Nullable + ScriptForStatement getForStatement(); + + @Nullable + ScriptFuncStatement getFuncStatement(); + + @Nullable + ScriptIconStatement getIconStatement(); + + @Nullable + ScriptIfStatement getIfStatement(); + + @Nullable + ScriptMatchLabelStatement getMatchLabelStatement(); + + @Nullable + ScriptMatchStatement getMatchStatement(); + + @Nullable + ScriptReturnStatement getReturnStatement(); + + @Nullable + ScriptSignalStatement getSignalStatement(); + + @Nullable + ScriptToolStatement getToolStatement(); + + @Nullable + ScriptVarStatement getVarStatement(); + + @Nullable + ScriptWhileStatement getWhileStatement(); + + @Nullable + ScriptYieldStatement getYieldStatement(); + + @Nullable + PsiElement getLineBreak(); + +} diff --git a/gen/gdscript/psi/ScriptElementTypes.java b/gen/gdscript/psi/ScriptElementTypes.java index 39594db..12a8e28 100644 --- a/gen/gdscript/psi/ScriptElementTypes.java +++ b/gen/gdscript/psi/ScriptElementTypes.java @@ -13,6 +13,7 @@ public interface ScriptElementTypes { IElementType ASSERT_STATEMENT = new ScriptElementType("ASSERT_STATEMENT"); IElementType ASSIGN_STATEMENT = new ScriptElementType("ASSIGN_STATEMENT"); IElementType AWAIT_STATEMENT = new ScriptElementType("AWAIT_STATEMENT"); + IElementType BLOCK = new ScriptElementType("BLOCK"); IElementType CLASSNAME_STATEMENT = new ScriptElementType("CLASSNAME_STATEMENT"); IElementType CLASS_STATEMENT = new ScriptElementType("CLASS_STATEMENT"); IElementType CONST_STATEMENT = new ScriptElementType("CONST_STATEMENT"); @@ -101,6 +102,7 @@ public interface ScriptElementTypes { IElementType COMMA = new ScriptTokenType(","); IElementType CONST = new ScriptTokenType("const"); IElementType CONTINUE = new ScriptTokenType("continue"); + IElementType DEDENT = new ScriptTokenType("DEDENT"); IElementType DOT = new ScriptTokenType("."); IElementType DOT_DOT = new ScriptTokenType(".."); IElementType DOUBLE_QUOTED_STRING = new ScriptTokenType("DOUBLE_QUOTED_STRING"); @@ -123,6 +125,7 @@ public interface ScriptElementTypes { IElementType IDENTIFIER = new ScriptTokenType("IDENTIFIER"); IElementType IF = new ScriptTokenType("if"); IElementType IN = new ScriptTokenType("in"); + IElementType INDENT = new ScriptTokenType("INDENT"); IElementType INFER = new ScriptTokenType(":="); IElementType INT = new ScriptTokenType("int"); IElementType IS = new ScriptTokenType("is"); @@ -202,6 +205,9 @@ else if (type == ASSIGN_STATEMENT) { else if (type == AWAIT_STATEMENT) { return new ScriptAwaitStatementImpl(node); } + else if (type == BLOCK) { + return new ScriptBlockImpl(node); + } else if (type == CLASSNAME_STATEMENT) { return new ScriptClassnameStatementImpl(node); } diff --git a/gen/gdscript/psi/ScriptFuncStatement.java b/gen/gdscript/psi/ScriptFuncStatement.java index 477031d..f351b2f 100644 --- a/gen/gdscript/psi/ScriptFuncStatement.java +++ b/gen/gdscript/psi/ScriptFuncStatement.java @@ -7,6 +7,9 @@ public interface ScriptFuncStatement extends PsiElement { + @Nullable + ScriptBlock getBlock(); + @NotNull List getFuncArgumentList(); diff --git a/gen/gdscript/psi/ScriptVisitor.java b/gen/gdscript/psi/ScriptVisitor.java index fd79870..49bc93b 100644 --- a/gen/gdscript/psi/ScriptVisitor.java +++ b/gen/gdscript/psi/ScriptVisitor.java @@ -27,6 +27,10 @@ public void visitAwaitStatement(@NotNull ScriptAwaitStatement o) { visitPsiElement(o); } + public void visitBlock(@NotNull ScriptBlock o) { + visitPsiElement(o); + } + public void visitClassStatement(@NotNull ScriptClassStatement o) { visitPsiElement(o); } diff --git a/gen/gdscript/psi/impl/ScriptBlockImpl.java b/gen/gdscript/psi/impl/ScriptBlockImpl.java new file mode 100644 index 0000000..85d1049 --- /dev/null +++ b/gen/gdscript/psi/impl/ScriptBlockImpl.java @@ -0,0 +1,174 @@ +// This is a generated file. Not intended for manual editing. +package gdscript.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 gdscript.psi.ScriptElementTypes.*; +import gdscript.psi.ScriptBaseElement; +import gdscript.psi.*; + +public class ScriptBlockImpl extends ScriptBaseElement implements ScriptBlock { + + public ScriptBlockImpl(@NotNull ASTNode node) { + super(node); + } + + public void accept(@NotNull ScriptVisitor visitor) { + visitor.visitBlock(this); + } + + @Override + public void accept(@NotNull PsiElementVisitor visitor) { + if (visitor instanceof ScriptVisitor) accept((ScriptVisitor)visitor); + else super.accept(visitor); + } + + @Override + @Nullable + public ScriptAssertStatement getAssertStatement() { + return findChildByClass(ScriptAssertStatement.class); + } + + @Override + @Nullable + public ScriptAssignStatement getAssignStatement() { + return findChildByClass(ScriptAssignStatement.class); + } + + @Override + @Nullable + public ScriptAwaitStatement getAwaitStatement() { + return findChildByClass(ScriptAwaitStatement.class); + } + + @Override + @Nullable + public ScriptClassStatement getClassStatement() { + return findChildByClass(ScriptClassStatement.class); + } + + @Override + @Nullable + public ScriptClassnameStatement getClassnameStatement() { + return findChildByClass(ScriptClassnameStatement.class); + } + + @Override + @Nullable + public ScriptConstStatement getConstStatement() { + return findChildByClass(ScriptConstStatement.class); + } + + @Override + @Nullable + public ScriptElifStatement getElifStatement() { + return findChildByClass(ScriptElifStatement.class); + } + + @Override + @Nullable + public ScriptElseStatement getElseStatement() { + return findChildByClass(ScriptElseStatement.class); + } + + @Override + @Nullable + public ScriptEnumStatement getEnumStatement() { + return findChildByClass(ScriptEnumStatement.class); + } + + @Override + @Nullable + public ScriptExpressionStatement getExpressionStatement() { + return findChildByClass(ScriptExpressionStatement.class); + } + + @Override + @Nullable + public ScriptExtendsStatement getExtendsStatement() { + return findChildByClass(ScriptExtendsStatement.class); + } + + @Override + @Nullable + public ScriptForStatement getForStatement() { + return findChildByClass(ScriptForStatement.class); + } + + @Override + @Nullable + public ScriptFuncStatement getFuncStatement() { + return findChildByClass(ScriptFuncStatement.class); + } + + @Override + @Nullable + public ScriptIconStatement getIconStatement() { + return findChildByClass(ScriptIconStatement.class); + } + + @Override + @Nullable + public ScriptIfStatement getIfStatement() { + return findChildByClass(ScriptIfStatement.class); + } + + @Override + @Nullable + public ScriptMatchLabelStatement getMatchLabelStatement() { + return findChildByClass(ScriptMatchLabelStatement.class); + } + + @Override + @Nullable + public ScriptMatchStatement getMatchStatement() { + return findChildByClass(ScriptMatchStatement.class); + } + + @Override + @Nullable + public ScriptReturnStatement getReturnStatement() { + return findChildByClass(ScriptReturnStatement.class); + } + + @Override + @Nullable + public ScriptSignalStatement getSignalStatement() { + return findChildByClass(ScriptSignalStatement.class); + } + + @Override + @Nullable + public ScriptToolStatement getToolStatement() { + return findChildByClass(ScriptToolStatement.class); + } + + @Override + @Nullable + public ScriptVarStatement getVarStatement() { + return findChildByClass(ScriptVarStatement.class); + } + + @Override + @Nullable + public ScriptWhileStatement getWhileStatement() { + return findChildByClass(ScriptWhileStatement.class); + } + + @Override + @Nullable + public ScriptYieldStatement getYieldStatement() { + return findChildByClass(ScriptYieldStatement.class); + } + + @Override + @Nullable + public PsiElement getLineBreak() { + return findChildByType(LINE_BREAK); + } + +} diff --git a/gen/gdscript/psi/impl/ScriptFuncStatementImpl.java b/gen/gdscript/psi/impl/ScriptFuncStatementImpl.java index a71d311..11b1ff9 100644 --- a/gen/gdscript/psi/impl/ScriptFuncStatementImpl.java +++ b/gen/gdscript/psi/impl/ScriptFuncStatementImpl.java @@ -27,6 +27,12 @@ public void accept(@NotNull PsiElementVisitor visitor) { else super.accept(visitor); } + @Override + @Nullable + public ScriptBlock getBlock() { + return findChildByClass(ScriptBlock.class); + } + @Override @NotNull public List getFuncArgumentList() { diff --git a/grammar/ScriptParser.bnf b/grammar/ScriptParser.bnf index 907a2b1..52d9de8 100644 --- a/grammar/ScriptParser.bnf +++ b/grammar/ScriptParser.bnf @@ -211,7 +211,7 @@ const_statement ::= CONST id (COLON type)? (EQUAL expression) extends_statement ::= EXTENDS (type | string) (DOT IDENTIFIER)* -func_statement ::= STATIC? network_modifier? FUNC id L_PAREN LINE_BREAK* func_argument? LINE_BREAK* (COMMA LINE_BREAK* func_argument LINE_BREAK*)* COMMA? LINE_BREAK* R_PAREN (ARROW type)? COLON +func_statement ::= STATIC? network_modifier? FUNC id L_PAREN LINE_BREAK* func_argument? LINE_BREAK* (COMMA LINE_BREAK* func_argument LINE_BREAK*)* COMMA? LINE_BREAK* R_PAREN (ARROW type)? COLON block? func_argument ::= id (COLON type)? ((EQUAL | INFER) expression)? enum_statement ::= ENUM id? (L_BRACE LINE_BREAK* enum_entry? LINE_BREAK* (COMMA LINE_BREAK* enum_entry LINE_BREAK*)* COMMA? LINE_BREAK* R_BRACE)? @@ -328,6 +328,7 @@ lambda_expression ::= FUNC id? L_PAREN id? (COMMA id)* R_PAREN COLON expression key ::= expression type ::= id (L_BRACKET id R_BRACKET)? +block ::= INDENT statement DEDENT id ::= IDENTIFIER | INT | FLOAT | BOOL | VOID | NODE_PATH {methods=[getReference]} string ::= SINGLE_QUOTED_STRING | DOUBLE_QUOTED_STRING {methods=[getReference]} diff --git a/src/main/kotlin/gdscript/lexer/ScriptIndentLexer.kt b/src/main/kotlin/gdscript/lexer/ScriptIndentLexer.kt new file mode 100644 index 0000000..4e8c59e --- /dev/null +++ b/src/main/kotlin/gdscript/lexer/ScriptIndentLexer.kt @@ -0,0 +1,67 @@ +package gdscript.lexer + +import com.intellij.lexer.LexerBase +import com.intellij.psi.tree.IElementType +import gdscript.psi.ScriptElementTypes.* + +class ScriptIndentLexer : LexerBase() { + + private val delegate = ScriptLexerAdapter() + private val pending = mutableListOf() + private var lastEnd = 0 + + init { + pending += INDENT + pending += DEDENT + pending += INDENT + pending += DEDENT + } + + override fun advance() { + lastEnd = tokenEnd + if (pending.isEmpty()) + advanceAndProduce() + else + pending.removeLast() + } + + private fun advanceAndProduce() { + if (tokenType == LINE_BREAK) { + pending += INDENT + pending += DEDENT + } + delegate.advance() + } + + override fun getTokenType(): IElementType? { + if (pending.isEmpty()) + return delegate.tokenType + return pending.last() + } + + override fun start(buffer: CharSequence, startOffset: Int, endOffset: Int, initialState: Int) { + delegate.start(buffer, startOffset, endOffset, initialState) + } + + override fun getState(): Int = + delegate.state + + override fun getTokenStart(): Int { + if (pending.isEmpty()) + return delegate.tokenStart + return lastEnd + } + + override fun getTokenEnd(): Int { + if (pending.isEmpty()) + return delegate.tokenEnd + return lastEnd + } + + override fun getBufferSequence(): CharSequence = + delegate.bufferSequence + + override fun getBufferEnd(): Int = + delegate.bufferEnd + +} \ No newline at end of file diff --git a/src/test/kotlin/gdscript/lexer/ScriptIndentLexerTest.kt b/src/test/kotlin/gdscript/lexer/ScriptIndentLexerTest.kt new file mode 100644 index 0000000..2fd8895 --- /dev/null +++ b/src/test/kotlin/gdscript/lexer/ScriptIndentLexerTest.kt @@ -0,0 +1,23 @@ +package gdscript.lexer + +import com.intellij.testFramework.LexerTestCase +import junit.framework.TestCase + +class ScriptIndentLexerTest : TestCase() { + + fun `test indent`() { + val lexer = ScriptIndentLexer() + val code = """ + a + b + c + d + e + f + """.trimIndent() + val printed = LexerTestCase.printTokens(code, 0, lexer) + println(printed) +// assertEquals(expected, printed) + } + +} \ No newline at end of file From b6e01e9c027b9c2f0fd0d8bf9cf04c9afaf92b09 Mon Sep 17 00:00:00 2001 From: Jakub Blach Date: Thu, 11 Nov 2021 17:24:20 +0100 Subject: [PATCH 2/3] Count spaces and tabs --- .../gdscript/lexer/ScriptIndentLexer.kt | 36 +++++++++++-------- .../gdscript/lexer/ScriptIndentLexerTest.kt | 10 +++--- 2 files changed, 27 insertions(+), 19 deletions(-) diff --git a/src/main/kotlin/gdscript/lexer/ScriptIndentLexer.kt b/src/main/kotlin/gdscript/lexer/ScriptIndentLexer.kt index 4e8c59e..1319b35 100644 --- a/src/main/kotlin/gdscript/lexer/ScriptIndentLexer.kt +++ b/src/main/kotlin/gdscript/lexer/ScriptIndentLexer.kt @@ -1,6 +1,7 @@ package gdscript.lexer import com.intellij.lexer.LexerBase +import com.intellij.psi.TokenType.WHITE_SPACE import com.intellij.psi.tree.IElementType import gdscript.psi.ScriptElementTypes.* @@ -8,35 +9,40 @@ class ScriptIndentLexer : LexerBase() { private val delegate = ScriptLexerAdapter() private val pending = mutableListOf() - private var lastEnd = 0 - - init { - pending += INDENT - pending += DEDENT - pending += INDENT - pending += DEDENT - } + private var prevEnd = 0 + private var prevToken: IElementType? = null override fun advance() { - lastEnd = tokenEnd + prevToken = delegate.tokenType + prevEnd = delegate.tokenEnd if (pending.isEmpty()) advanceAndProduce() else - pending.removeLast() + pending.removeFirst() } private fun advanceAndProduce() { - if (tokenType == LINE_BREAK) { + delegate.advance() + if (prevToken == LINE_BREAK) { + val depth = calcDepth() + println("current $tokenType, prev $prevToken, depth: $depth") pending += INDENT pending += DEDENT } - delegate.advance() + } + + private fun calcDepth(): Int { + if (tokenType == WHITE_SPACE) + return tokenText + .map { c -> if (c == '\t') 4 else 1 } + .sum() + return 0 } override fun getTokenType(): IElementType? { if (pending.isEmpty()) return delegate.tokenType - return pending.last() + return pending.first() } override fun start(buffer: CharSequence, startOffset: Int, endOffset: Int, initialState: Int) { @@ -49,13 +55,13 @@ class ScriptIndentLexer : LexerBase() { override fun getTokenStart(): Int { if (pending.isEmpty()) return delegate.tokenStart - return lastEnd + return prevEnd } override fun getTokenEnd(): Int { if (pending.isEmpty()) return delegate.tokenEnd - return lastEnd + return prevEnd } override fun getBufferSequence(): CharSequence = diff --git a/src/test/kotlin/gdscript/lexer/ScriptIndentLexerTest.kt b/src/test/kotlin/gdscript/lexer/ScriptIndentLexerTest.kt index 2fd8895..07f444b 100644 --- a/src/test/kotlin/gdscript/lexer/ScriptIndentLexerTest.kt +++ b/src/test/kotlin/gdscript/lexer/ScriptIndentLexerTest.kt @@ -9,11 +9,13 @@ class ScriptIndentLexerTest : TestCase() { val lexer = ScriptIndentLexer() val code = """ a - b + b c - d - e - f + d + ${"\t\t"}e + f + g + h """.trimIndent() val printed = LexerTestCase.printTokens(code, 0, lexer) println(printed) From 6a8610c0cfa67562183b40a279d50f1d5291a300 Mon Sep 17 00:00:00 2001 From: Jakub Blach Date: Fri, 12 Nov 2021 00:45:53 +0100 Subject: [PATCH 3/3] Parse indend/dedents as code blocks --- gen/gdscript/parser/ScriptParser.java | 103 +++++++++-- gen/gdscript/psi/ScriptBlock.java | 95 +++++------ gen/gdscript/psi/ScriptClassStatement.java | 6 + gen/gdscript/psi/ScriptElifStatement.java | 6 + gen/gdscript/psi/ScriptElseStatement.java | 6 + gen/gdscript/psi/ScriptFuncStatement.java | 2 +- gen/gdscript/psi/ScriptIfStatement.java | 6 + .../psi/ScriptMatchLabelStatement.java | 6 + gen/gdscript/psi/ScriptMatchStatement.java | 6 + gen/gdscript/psi/ScriptWhileStatement.java | 6 + gen/gdscript/psi/impl/ScriptBlockImpl.java | 144 ++++++++-------- .../psi/impl/ScriptClassStatementImpl.java | 12 ++ .../psi/impl/ScriptElifStatementImpl.java | 12 ++ .../psi/impl/ScriptElseStatementImpl.java | 12 ++ .../psi/impl/ScriptFuncStatementImpl.java | 4 +- .../psi/impl/ScriptIfStatementImpl.java | 12 ++ .../impl/ScriptMatchLabelStatementImpl.java | 12 ++ .../psi/impl/ScriptMatchStatementImpl.java | 12 ++ .../psi/impl/ScriptWhileStatementImpl.java | 12 ++ grammar/ScriptParser.bnf | 18 +- .../kotlin/gdscript/ScriptParserDefinition.kt | 3 +- .../gdscript/lexer/ScriptIndentLexer.kt | 42 ++++- .../gdscript/lexer/ScriptIndentLexerTest.kt | 160 ++++++++++++++++-- .../gdscript/lexer/ScriptLexerAdapterTest.kt | 80 +++++---- .../gdscript/parser/ScriptParserTest.kt | 81 ++++++++- src/test/kotlin/utils/assertions.kt | 8 +- src/test/kotlin/utils/fixtures.kt | 8 +- 27 files changed, 664 insertions(+), 210 deletions(-) diff --git a/gen/gdscript/parser/ScriptParser.java b/gen/gdscript/parser/ScriptParser.java index 26e0bdf..0b38a9a 100644 --- a/gen/gdscript/parser/ScriptParser.java +++ b/gen/gdscript/parser/ScriptParser.java @@ -256,21 +256,36 @@ public static boolean await_statement(PsiBuilder b, int l) { } /* ********************************************************** */ - // INDENT statement DEDENT + // INDENT statement+ DEDENT public static boolean block(PsiBuilder b, int l) { if (!recursion_guard_(b, l, "block")) return false; if (!nextTokenIs(b, INDENT)) return false; boolean r; Marker m = enter_section_(b); r = consumeToken(b, INDENT); - r = r && statement(b, l + 1); + r = r && block_1(b, l + 1); r = r && consumeToken(b, DEDENT); exit_section_(b, m, BLOCK, r); return r; } + // statement+ + private static boolean block_1(PsiBuilder b, int l) { + if (!recursion_guard_(b, l, "block_1")) return false; + boolean r; + Marker m = enter_section_(b); + r = statement(b, l + 1); + while (r) { + int c = current_position_(b); + if (!statement(b, l + 1)) break; + if (!empty_element_parsed_guard_(b, "block_1", c)) break; + } + exit_section_(b, m, null, r); + return r; + } + /* ********************************************************** */ - // CLASS id (EXTENDS type)? COLON + // CLASS id (EXTENDS type)? COLON LINE_BREAK? block public static boolean class_statement(PsiBuilder b, int l) { if (!recursion_guard_(b, l, "class_statement")) return false; if (!nextTokenIs(b, CLASS)) return false; @@ -280,6 +295,8 @@ public static boolean class_statement(PsiBuilder b, int l) { r = r && id(b, l + 1); r = r && class_statement_2(b, l + 1); r = r && consumeToken(b, COLON); + r = r && class_statement_4(b, l + 1); + r = r && block(b, l + 1); exit_section_(b, m, CLASS_STATEMENT, r); return r; } @@ -302,6 +319,13 @@ private static boolean class_statement_2_0(PsiBuilder b, int l) { return r; } + // LINE_BREAK? + private static boolean class_statement_4(PsiBuilder b, int l) { + if (!recursion_guard_(b, l, "class_statement_4")) return false; + consumeToken(b, LINE_BREAK); + return true; + } + /* ********************************************************** */ // CLASS_NAME id (COMMA string)? public static boolean classname_statement(PsiBuilder b, int l) { @@ -589,7 +613,7 @@ private static boolean dictionary_expression_6(PsiBuilder b, int l) { } /* ********************************************************** */ - // ELIF expression COLON + // ELIF expression COLON LINE_BREAK? block public static boolean elif_statement(PsiBuilder b, int l) { if (!recursion_guard_(b, l, "elif_statement")) return false; if (!nextTokenIs(b, ELIF)) return false; @@ -598,22 +622,40 @@ public static boolean elif_statement(PsiBuilder b, int l) { r = consumeToken(b, ELIF); r = r && expression(b, l + 1); r = r && consumeToken(b, COLON); + r = r && elif_statement_3(b, l + 1); + r = r && block(b, l + 1); exit_section_(b, m, ELIF_STATEMENT, r); return r; } + // LINE_BREAK? + private static boolean elif_statement_3(PsiBuilder b, int l) { + if (!recursion_guard_(b, l, "elif_statement_3")) return false; + consumeToken(b, LINE_BREAK); + return true; + } + /* ********************************************************** */ - // ELSE COLON + // ELSE COLON LINE_BREAK? block public static boolean else_statement(PsiBuilder b, int l) { if (!recursion_guard_(b, l, "else_statement")) return false; if (!nextTokenIs(b, ELSE)) return false; boolean r; Marker m = enter_section_(b); r = consumeTokens(b, 0, ELSE, COLON); + r = r && else_statement_2(b, l + 1); + r = r && block(b, l + 1); exit_section_(b, m, ELSE_STATEMENT, r); return r; } + // LINE_BREAK? + private static boolean else_statement_2(PsiBuilder b, int l) { + if (!recursion_guard_(b, l, "else_statement_2")) return false; + consumeToken(b, LINE_BREAK); + return true; + } + /* ********************************************************** */ // id (EQUAL expression)? public static boolean enum_entry(PsiBuilder b, int l) { @@ -982,7 +1024,7 @@ private static boolean func_argument_2_0_0(PsiBuilder b, int l) { } /* ********************************************************** */ - // STATIC? network_modifier? FUNC id L_PAREN LINE_BREAK* func_argument? LINE_BREAK* (COMMA LINE_BREAK* func_argument LINE_BREAK*)* COMMA? LINE_BREAK* R_PAREN (ARROW type)? COLON block? + // STATIC? network_modifier? FUNC id L_PAREN LINE_BREAK* func_argument? LINE_BREAK* (COMMA LINE_BREAK* func_argument LINE_BREAK*)* COMMA? LINE_BREAK* R_PAREN (ARROW type)? COLON LINE_BREAK? block public static boolean func_statement(PsiBuilder b, int l) { if (!recursion_guard_(b, l, "func_statement")) return false; boolean r; @@ -1002,6 +1044,7 @@ public static boolean func_statement(PsiBuilder b, int l) { r = r && func_statement_12(b, l + 1); r = r && consumeToken(b, COLON); r = r && func_statement_14(b, l + 1); + r = r && block(b, l + 1); exit_section_(b, l, m, r, false, null); return r; } @@ -1131,10 +1174,10 @@ private static boolean func_statement_12_0(PsiBuilder b, int l) { return r; } - // block? + // LINE_BREAK? private static boolean func_statement_14(PsiBuilder b, int l) { if (!recursion_guard_(b, l, "func_statement_14")) return false; - block(b, l + 1); + consumeToken(b, LINE_BREAK); return true; } @@ -1169,7 +1212,7 @@ public static boolean id(PsiBuilder b, int l) { } /* ********************************************************** */ - // IF expression COLON + // IF expression COLON LINE_BREAK? block public static boolean if_statement(PsiBuilder b, int l) { if (!recursion_guard_(b, l, "if_statement")) return false; if (!nextTokenIs(b, IF)) return false; @@ -1178,10 +1221,19 @@ public static boolean if_statement(PsiBuilder b, int l) { r = consumeToken(b, IF); r = r && expression(b, l + 1); r = r && consumeToken(b, COLON); + r = r && if_statement_3(b, l + 1); + r = r && block(b, l + 1); exit_section_(b, m, IF_STATEMENT, r); return r; } + // LINE_BREAK? + private static boolean if_statement_3(PsiBuilder b, int l) { + if (!recursion_guard_(b, l, "if_statement_3")) return false; + consumeToken(b, LINE_BREAK); + return true; + } + /* ********************************************************** */ // id? L_PAREN LINE_BREAK* expression? LINE_BREAK* (COMMA LINE_BREAK* expression LINE_BREAK*)* R_PAREN public static boolean invocation_expression(PsiBuilder b, int l) { @@ -1495,7 +1547,7 @@ public static boolean match_label_argument(PsiBuilder b, int l) { } /* ********************************************************** */ - // match_label_argument (COMMA match_label_argument)* COLON + // match_label_argument (COMMA match_label_argument)* COLON LINE_BREAK? block public static boolean match_label_statement(PsiBuilder b, int l) { if (!recursion_guard_(b, l, "match_label_statement")) return false; boolean r; @@ -1503,6 +1555,8 @@ public static boolean match_label_statement(PsiBuilder b, int l) { r = match_label_argument(b, l + 1); r = r && match_label_statement_1(b, l + 1); r = r && consumeToken(b, COLON); + r = r && match_label_statement_3(b, l + 1); + r = r && block(b, l + 1); exit_section_(b, l, m, r, false, null); return r; } @@ -1529,8 +1583,15 @@ private static boolean match_label_statement_1_0(PsiBuilder b, int l) { return r; } + // LINE_BREAK? + private static boolean match_label_statement_3(PsiBuilder b, int l) { + if (!recursion_guard_(b, l, "match_label_statement_3")) return false; + consumeToken(b, LINE_BREAK); + return true; + } + /* ********************************************************** */ - // MATCH expression COLON + // MATCH expression COLON LINE_BREAK? block public static boolean match_statement(PsiBuilder b, int l) { if (!recursion_guard_(b, l, "match_statement")) return false; if (!nextTokenIs(b, MATCH)) return false; @@ -1539,10 +1600,19 @@ public static boolean match_statement(PsiBuilder b, int l) { r = consumeToken(b, MATCH); r = r && expression(b, l + 1); r = r && consumeToken(b, COLON); + r = r && match_statement_3(b, l + 1); + r = r && block(b, l + 1); exit_section_(b, m, MATCH_STATEMENT, r); return r; } + // LINE_BREAK? + private static boolean match_statement_3(PsiBuilder b, int l) { + if (!recursion_guard_(b, l, "match_statement_3")) return false; + consumeToken(b, LINE_BREAK); + return true; + } + /* ********************************************************** */ // MULTILINE_SINGLE_QUOTED_STRING | MULTILINE_DOUBLE_QUOTED_STRING static boolean multiline_string(PsiBuilder b, int l) { @@ -2186,7 +2256,7 @@ private static boolean var_statement_7_0_2_0(PsiBuilder b, int l) { } /* ********************************************************** */ - // WHILE expression COLON + // WHILE expression COLON LINE_BREAK? block public static boolean while_statement(PsiBuilder b, int l) { if (!recursion_guard_(b, l, "while_statement")) return false; if (!nextTokenIs(b, WHILE)) return false; @@ -2195,10 +2265,19 @@ public static boolean while_statement(PsiBuilder b, int l) { r = consumeToken(b, WHILE); r = r && expression(b, l + 1); r = r && consumeToken(b, COLON); + r = r && while_statement_3(b, l + 1); + r = r && block(b, l + 1); exit_section_(b, m, WHILE_STATEMENT, r); return r; } + // LINE_BREAK? + private static boolean while_statement_3(PsiBuilder b, int l) { + if (!recursion_guard_(b, l, "while_statement_3")) return false; + consumeToken(b, LINE_BREAK); + return true; + } + /* ********************************************************** */ // YIELD expression public static boolean yield_statement(PsiBuilder b, int l) { diff --git a/gen/gdscript/psi/ScriptBlock.java b/gen/gdscript/psi/ScriptBlock.java index de2a91a..b28a4de 100644 --- a/gen/gdscript/psi/ScriptBlock.java +++ b/gen/gdscript/psi/ScriptBlock.java @@ -7,76 +7,73 @@ public interface ScriptBlock extends PsiElement { - @Nullable - ScriptAssertStatement getAssertStatement(); + @NotNull + List getAssertStatementList(); - @Nullable - ScriptAssignStatement getAssignStatement(); + @NotNull + List getAssignStatementList(); - @Nullable - ScriptAwaitStatement getAwaitStatement(); + @NotNull + List getAwaitStatementList(); - @Nullable - ScriptClassStatement getClassStatement(); + @NotNull + List getClassStatementList(); - @Nullable - ScriptClassnameStatement getClassnameStatement(); + @NotNull + List getClassnameStatementList(); - @Nullable - ScriptConstStatement getConstStatement(); + @NotNull + List getConstStatementList(); - @Nullable - ScriptElifStatement getElifStatement(); + @NotNull + List getElifStatementList(); - @Nullable - ScriptElseStatement getElseStatement(); + @NotNull + List getElseStatementList(); - @Nullable - ScriptEnumStatement getEnumStatement(); + @NotNull + List getEnumStatementList(); - @Nullable - ScriptExpressionStatement getExpressionStatement(); + @NotNull + List getExpressionStatementList(); - @Nullable - ScriptExtendsStatement getExtendsStatement(); + @NotNull + List getExtendsStatementList(); - @Nullable - ScriptForStatement getForStatement(); + @NotNull + List getForStatementList(); - @Nullable - ScriptFuncStatement getFuncStatement(); + @NotNull + List getFuncStatementList(); - @Nullable - ScriptIconStatement getIconStatement(); + @NotNull + List getIconStatementList(); - @Nullable - ScriptIfStatement getIfStatement(); + @NotNull + List getIfStatementList(); - @Nullable - ScriptMatchLabelStatement getMatchLabelStatement(); + @NotNull + List getMatchLabelStatementList(); - @Nullable - ScriptMatchStatement getMatchStatement(); + @NotNull + List getMatchStatementList(); - @Nullable - ScriptReturnStatement getReturnStatement(); + @NotNull + List getReturnStatementList(); - @Nullable - ScriptSignalStatement getSignalStatement(); + @NotNull + List getSignalStatementList(); - @Nullable - ScriptToolStatement getToolStatement(); + @NotNull + List getToolStatementList(); - @Nullable - ScriptVarStatement getVarStatement(); + @NotNull + List getVarStatementList(); - @Nullable - ScriptWhileStatement getWhileStatement(); + @NotNull + List getWhileStatementList(); - @Nullable - ScriptYieldStatement getYieldStatement(); - - @Nullable - PsiElement getLineBreak(); + @NotNull + List getYieldStatementList(); } diff --git a/gen/gdscript/psi/ScriptClassStatement.java b/gen/gdscript/psi/ScriptClassStatement.java index 97d82ff..55f0f2e 100644 --- a/gen/gdscript/psi/ScriptClassStatement.java +++ b/gen/gdscript/psi/ScriptClassStatement.java @@ -7,10 +7,16 @@ public interface ScriptClassStatement extends PsiElement { + @NotNull + ScriptBlock getBlock(); + @NotNull ScriptId getId(); @Nullable ScriptType getType(); + @Nullable + PsiElement getLineBreak(); + } diff --git a/gen/gdscript/psi/ScriptElifStatement.java b/gen/gdscript/psi/ScriptElifStatement.java index a4eaedf..1f2d076 100644 --- a/gen/gdscript/psi/ScriptElifStatement.java +++ b/gen/gdscript/psi/ScriptElifStatement.java @@ -7,7 +7,13 @@ public interface ScriptElifStatement extends PsiElement { + @NotNull + ScriptBlock getBlock(); + @NotNull ScriptExpression getExpression(); + @Nullable + PsiElement getLineBreak(); + } diff --git a/gen/gdscript/psi/ScriptElseStatement.java b/gen/gdscript/psi/ScriptElseStatement.java index f93f2f6..56e8a9b 100644 --- a/gen/gdscript/psi/ScriptElseStatement.java +++ b/gen/gdscript/psi/ScriptElseStatement.java @@ -7,4 +7,10 @@ public interface ScriptElseStatement extends PsiElement { + @NotNull + ScriptBlock getBlock(); + + @Nullable + PsiElement getLineBreak(); + } diff --git a/gen/gdscript/psi/ScriptFuncStatement.java b/gen/gdscript/psi/ScriptFuncStatement.java index f351b2f..c6e96fb 100644 --- a/gen/gdscript/psi/ScriptFuncStatement.java +++ b/gen/gdscript/psi/ScriptFuncStatement.java @@ -7,7 +7,7 @@ public interface ScriptFuncStatement extends PsiElement { - @Nullable + @NotNull ScriptBlock getBlock(); @NotNull diff --git a/gen/gdscript/psi/ScriptIfStatement.java b/gen/gdscript/psi/ScriptIfStatement.java index 13aac82..621b2f8 100644 --- a/gen/gdscript/psi/ScriptIfStatement.java +++ b/gen/gdscript/psi/ScriptIfStatement.java @@ -7,7 +7,13 @@ public interface ScriptIfStatement extends PsiElement { + @NotNull + ScriptBlock getBlock(); + @NotNull ScriptExpression getExpression(); + @Nullable + PsiElement getLineBreak(); + } diff --git a/gen/gdscript/psi/ScriptMatchLabelStatement.java b/gen/gdscript/psi/ScriptMatchLabelStatement.java index c01a838..e5ac445 100644 --- a/gen/gdscript/psi/ScriptMatchLabelStatement.java +++ b/gen/gdscript/psi/ScriptMatchLabelStatement.java @@ -7,7 +7,13 @@ public interface ScriptMatchLabelStatement extends PsiElement { + @NotNull + ScriptBlock getBlock(); + @NotNull List getMatchLabelArgumentList(); + @Nullable + PsiElement getLineBreak(); + } diff --git a/gen/gdscript/psi/ScriptMatchStatement.java b/gen/gdscript/psi/ScriptMatchStatement.java index c5b5987..7d2f8b5 100644 --- a/gen/gdscript/psi/ScriptMatchStatement.java +++ b/gen/gdscript/psi/ScriptMatchStatement.java @@ -7,7 +7,13 @@ public interface ScriptMatchStatement extends PsiElement { + @NotNull + ScriptBlock getBlock(); + @NotNull ScriptExpression getExpression(); + @Nullable + PsiElement getLineBreak(); + } diff --git a/gen/gdscript/psi/ScriptWhileStatement.java b/gen/gdscript/psi/ScriptWhileStatement.java index d2b58ce..818200f 100644 --- a/gen/gdscript/psi/ScriptWhileStatement.java +++ b/gen/gdscript/psi/ScriptWhileStatement.java @@ -7,7 +7,13 @@ public interface ScriptWhileStatement extends PsiElement { + @NotNull + ScriptBlock getBlock(); + @NotNull ScriptExpression getExpression(); + @Nullable + PsiElement getLineBreak(); + } diff --git a/gen/gdscript/psi/impl/ScriptBlockImpl.java b/gen/gdscript/psi/impl/ScriptBlockImpl.java index 85d1049..ef8a33a 100644 --- a/gen/gdscript/psi/impl/ScriptBlockImpl.java +++ b/gen/gdscript/psi/impl/ScriptBlockImpl.java @@ -28,147 +28,141 @@ public void accept(@NotNull PsiElementVisitor visitor) { } @Override - @Nullable - public ScriptAssertStatement getAssertStatement() { - return findChildByClass(ScriptAssertStatement.class); + @NotNull + public List getAssertStatementList() { + return PsiTreeUtil.getChildrenOfTypeAsList(this, ScriptAssertStatement.class); } @Override - @Nullable - public ScriptAssignStatement getAssignStatement() { - return findChildByClass(ScriptAssignStatement.class); + @NotNull + public List getAssignStatementList() { + return PsiTreeUtil.getChildrenOfTypeAsList(this, ScriptAssignStatement.class); } @Override - @Nullable - public ScriptAwaitStatement getAwaitStatement() { - return findChildByClass(ScriptAwaitStatement.class); + @NotNull + public List getAwaitStatementList() { + return PsiTreeUtil.getChildrenOfTypeAsList(this, ScriptAwaitStatement.class); } @Override - @Nullable - public ScriptClassStatement getClassStatement() { - return findChildByClass(ScriptClassStatement.class); + @NotNull + public List getClassStatementList() { + return PsiTreeUtil.getChildrenOfTypeAsList(this, ScriptClassStatement.class); } @Override - @Nullable - public ScriptClassnameStatement getClassnameStatement() { - return findChildByClass(ScriptClassnameStatement.class); + @NotNull + public List getClassnameStatementList() { + return PsiTreeUtil.getChildrenOfTypeAsList(this, ScriptClassnameStatement.class); } @Override - @Nullable - public ScriptConstStatement getConstStatement() { - return findChildByClass(ScriptConstStatement.class); + @NotNull + public List getConstStatementList() { + return PsiTreeUtil.getChildrenOfTypeAsList(this, ScriptConstStatement.class); } @Override - @Nullable - public ScriptElifStatement getElifStatement() { - return findChildByClass(ScriptElifStatement.class); + @NotNull + public List getElifStatementList() { + return PsiTreeUtil.getChildrenOfTypeAsList(this, ScriptElifStatement.class); } @Override - @Nullable - public ScriptElseStatement getElseStatement() { - return findChildByClass(ScriptElseStatement.class); + @NotNull + public List getElseStatementList() { + return PsiTreeUtil.getChildrenOfTypeAsList(this, ScriptElseStatement.class); } @Override - @Nullable - public ScriptEnumStatement getEnumStatement() { - return findChildByClass(ScriptEnumStatement.class); + @NotNull + public List getEnumStatementList() { + return PsiTreeUtil.getChildrenOfTypeAsList(this, ScriptEnumStatement.class); } @Override - @Nullable - public ScriptExpressionStatement getExpressionStatement() { - return findChildByClass(ScriptExpressionStatement.class); + @NotNull + public List getExpressionStatementList() { + return PsiTreeUtil.getChildrenOfTypeAsList(this, ScriptExpressionStatement.class); } @Override - @Nullable - public ScriptExtendsStatement getExtendsStatement() { - return findChildByClass(ScriptExtendsStatement.class); + @NotNull + public List getExtendsStatementList() { + return PsiTreeUtil.getChildrenOfTypeAsList(this, ScriptExtendsStatement.class); } @Override - @Nullable - public ScriptForStatement getForStatement() { - return findChildByClass(ScriptForStatement.class); + @NotNull + public List getForStatementList() { + return PsiTreeUtil.getChildrenOfTypeAsList(this, ScriptForStatement.class); } @Override - @Nullable - public ScriptFuncStatement getFuncStatement() { - return findChildByClass(ScriptFuncStatement.class); + @NotNull + public List getFuncStatementList() { + return PsiTreeUtil.getChildrenOfTypeAsList(this, ScriptFuncStatement.class); } @Override - @Nullable - public ScriptIconStatement getIconStatement() { - return findChildByClass(ScriptIconStatement.class); + @NotNull + public List getIconStatementList() { + return PsiTreeUtil.getChildrenOfTypeAsList(this, ScriptIconStatement.class); } @Override - @Nullable - public ScriptIfStatement getIfStatement() { - return findChildByClass(ScriptIfStatement.class); + @NotNull + public List getIfStatementList() { + return PsiTreeUtil.getChildrenOfTypeAsList(this, ScriptIfStatement.class); } @Override - @Nullable - public ScriptMatchLabelStatement getMatchLabelStatement() { - return findChildByClass(ScriptMatchLabelStatement.class); + @NotNull + public List getMatchLabelStatementList() { + return PsiTreeUtil.getChildrenOfTypeAsList(this, ScriptMatchLabelStatement.class); } @Override - @Nullable - public ScriptMatchStatement getMatchStatement() { - return findChildByClass(ScriptMatchStatement.class); + @NotNull + public List getMatchStatementList() { + return PsiTreeUtil.getChildrenOfTypeAsList(this, ScriptMatchStatement.class); } @Override - @Nullable - public ScriptReturnStatement getReturnStatement() { - return findChildByClass(ScriptReturnStatement.class); + @NotNull + public List getReturnStatementList() { + return PsiTreeUtil.getChildrenOfTypeAsList(this, ScriptReturnStatement.class); } @Override - @Nullable - public ScriptSignalStatement getSignalStatement() { - return findChildByClass(ScriptSignalStatement.class); + @NotNull + public List getSignalStatementList() { + return PsiTreeUtil.getChildrenOfTypeAsList(this, ScriptSignalStatement.class); } @Override - @Nullable - public ScriptToolStatement getToolStatement() { - return findChildByClass(ScriptToolStatement.class); + @NotNull + public List getToolStatementList() { + return PsiTreeUtil.getChildrenOfTypeAsList(this, ScriptToolStatement.class); } @Override - @Nullable - public ScriptVarStatement getVarStatement() { - return findChildByClass(ScriptVarStatement.class); + @NotNull + public List getVarStatementList() { + return PsiTreeUtil.getChildrenOfTypeAsList(this, ScriptVarStatement.class); } @Override - @Nullable - public ScriptWhileStatement getWhileStatement() { - return findChildByClass(ScriptWhileStatement.class); + @NotNull + public List getWhileStatementList() { + return PsiTreeUtil.getChildrenOfTypeAsList(this, ScriptWhileStatement.class); } @Override - @Nullable - public ScriptYieldStatement getYieldStatement() { - return findChildByClass(ScriptYieldStatement.class); - } - - @Override - @Nullable - public PsiElement getLineBreak() { - return findChildByType(LINE_BREAK); + @NotNull + public List getYieldStatementList() { + return PsiTreeUtil.getChildrenOfTypeAsList(this, ScriptYieldStatement.class); } } diff --git a/gen/gdscript/psi/impl/ScriptClassStatementImpl.java b/gen/gdscript/psi/impl/ScriptClassStatementImpl.java index 508cc39..f0ebdd0 100644 --- a/gen/gdscript/psi/impl/ScriptClassStatementImpl.java +++ b/gen/gdscript/psi/impl/ScriptClassStatementImpl.java @@ -27,6 +27,12 @@ public void accept(@NotNull PsiElementVisitor visitor) { else super.accept(visitor); } + @Override + @NotNull + public ScriptBlock getBlock() { + return findNotNullChildByClass(ScriptBlock.class); + } + @Override @NotNull public ScriptId getId() { @@ -39,4 +45,10 @@ public ScriptType getType() { return findChildByClass(ScriptType.class); } + @Override + @Nullable + public PsiElement getLineBreak() { + return findChildByType(LINE_BREAK); + } + } diff --git a/gen/gdscript/psi/impl/ScriptElifStatementImpl.java b/gen/gdscript/psi/impl/ScriptElifStatementImpl.java index 0e9fcd9..40c9917 100644 --- a/gen/gdscript/psi/impl/ScriptElifStatementImpl.java +++ b/gen/gdscript/psi/impl/ScriptElifStatementImpl.java @@ -27,10 +27,22 @@ public void accept(@NotNull PsiElementVisitor visitor) { else super.accept(visitor); } + @Override + @NotNull + public ScriptBlock getBlock() { + return findNotNullChildByClass(ScriptBlock.class); + } + @Override @NotNull public ScriptExpression getExpression() { return findNotNullChildByClass(ScriptExpression.class); } + @Override + @Nullable + public PsiElement getLineBreak() { + return findChildByType(LINE_BREAK); + } + } diff --git a/gen/gdscript/psi/impl/ScriptElseStatementImpl.java b/gen/gdscript/psi/impl/ScriptElseStatementImpl.java index 40dc0ad..05a5475 100644 --- a/gen/gdscript/psi/impl/ScriptElseStatementImpl.java +++ b/gen/gdscript/psi/impl/ScriptElseStatementImpl.java @@ -27,4 +27,16 @@ public void accept(@NotNull PsiElementVisitor visitor) { else super.accept(visitor); } + @Override + @NotNull + public ScriptBlock getBlock() { + return findNotNullChildByClass(ScriptBlock.class); + } + + @Override + @Nullable + public PsiElement getLineBreak() { + return findChildByType(LINE_BREAK); + } + } diff --git a/gen/gdscript/psi/impl/ScriptFuncStatementImpl.java b/gen/gdscript/psi/impl/ScriptFuncStatementImpl.java index 11b1ff9..7f833b4 100644 --- a/gen/gdscript/psi/impl/ScriptFuncStatementImpl.java +++ b/gen/gdscript/psi/impl/ScriptFuncStatementImpl.java @@ -28,9 +28,9 @@ public void accept(@NotNull PsiElementVisitor visitor) { } @Override - @Nullable + @NotNull public ScriptBlock getBlock() { - return findChildByClass(ScriptBlock.class); + return findNotNullChildByClass(ScriptBlock.class); } @Override diff --git a/gen/gdscript/psi/impl/ScriptIfStatementImpl.java b/gen/gdscript/psi/impl/ScriptIfStatementImpl.java index 60590e8..4387df1 100644 --- a/gen/gdscript/psi/impl/ScriptIfStatementImpl.java +++ b/gen/gdscript/psi/impl/ScriptIfStatementImpl.java @@ -27,10 +27,22 @@ public void accept(@NotNull PsiElementVisitor visitor) { else super.accept(visitor); } + @Override + @NotNull + public ScriptBlock getBlock() { + return findNotNullChildByClass(ScriptBlock.class); + } + @Override @NotNull public ScriptExpression getExpression() { return findNotNullChildByClass(ScriptExpression.class); } + @Override + @Nullable + public PsiElement getLineBreak() { + return findChildByType(LINE_BREAK); + } + } diff --git a/gen/gdscript/psi/impl/ScriptMatchLabelStatementImpl.java b/gen/gdscript/psi/impl/ScriptMatchLabelStatementImpl.java index 132df87..356f9f4 100644 --- a/gen/gdscript/psi/impl/ScriptMatchLabelStatementImpl.java +++ b/gen/gdscript/psi/impl/ScriptMatchLabelStatementImpl.java @@ -27,10 +27,22 @@ public void accept(@NotNull PsiElementVisitor visitor) { else super.accept(visitor); } + @Override + @NotNull + public ScriptBlock getBlock() { + return findNotNullChildByClass(ScriptBlock.class); + } + @Override @NotNull public List getMatchLabelArgumentList() { return PsiTreeUtil.getChildrenOfTypeAsList(this, ScriptMatchLabelArgument.class); } + @Override + @Nullable + public PsiElement getLineBreak() { + return findChildByType(LINE_BREAK); + } + } diff --git a/gen/gdscript/psi/impl/ScriptMatchStatementImpl.java b/gen/gdscript/psi/impl/ScriptMatchStatementImpl.java index 53e1d46..10d50ef 100644 --- a/gen/gdscript/psi/impl/ScriptMatchStatementImpl.java +++ b/gen/gdscript/psi/impl/ScriptMatchStatementImpl.java @@ -27,10 +27,22 @@ public void accept(@NotNull PsiElementVisitor visitor) { else super.accept(visitor); } + @Override + @NotNull + public ScriptBlock getBlock() { + return findNotNullChildByClass(ScriptBlock.class); + } + @Override @NotNull public ScriptExpression getExpression() { return findNotNullChildByClass(ScriptExpression.class); } + @Override + @Nullable + public PsiElement getLineBreak() { + return findChildByType(LINE_BREAK); + } + } diff --git a/gen/gdscript/psi/impl/ScriptWhileStatementImpl.java b/gen/gdscript/psi/impl/ScriptWhileStatementImpl.java index 2752060..1011407 100644 --- a/gen/gdscript/psi/impl/ScriptWhileStatementImpl.java +++ b/gen/gdscript/psi/impl/ScriptWhileStatementImpl.java @@ -27,10 +27,22 @@ public void accept(@NotNull PsiElementVisitor visitor) { else super.accept(visitor); } + @Override + @NotNull + public ScriptBlock getBlock() { + return findNotNullChildByClass(ScriptBlock.class); + } + @Override @NotNull public ScriptExpression getExpression() { return findNotNullChildByClass(ScriptExpression.class); } + @Override + @Nullable + public PsiElement getLineBreak() { + return findChildByType(LINE_BREAK); + } + } diff --git a/grammar/ScriptParser.bnf b/grammar/ScriptParser.bnf index 52d9de8..ef9426e 100644 --- a/grammar/ScriptParser.bnf +++ b/grammar/ScriptParser.bnf @@ -211,17 +211,17 @@ const_statement ::= CONST id (COLON type)? (EQUAL expression) extends_statement ::= EXTENDS (type | string) (DOT IDENTIFIER)* -func_statement ::= STATIC? network_modifier? FUNC id L_PAREN LINE_BREAK* func_argument? LINE_BREAK* (COMMA LINE_BREAK* func_argument LINE_BREAK*)* COMMA? LINE_BREAK* R_PAREN (ARROW type)? COLON block? +func_statement ::= STATIC? network_modifier? FUNC id L_PAREN LINE_BREAK* func_argument? LINE_BREAK* (COMMA LINE_BREAK* func_argument LINE_BREAK*)* COMMA? LINE_BREAK* R_PAREN (ARROW type)? COLON LINE_BREAK? block func_argument ::= id (COLON type)? ((EQUAL | INFER) expression)? enum_statement ::= ENUM id? (L_BRACE LINE_BREAK* enum_entry? LINE_BREAK* (COMMA LINE_BREAK* enum_entry LINE_BREAK*)* COMMA? LINE_BREAK* R_BRACE)? enum_entry ::= id (EQUAL expression)? -if_statement ::= IF expression COLON +if_statement ::= IF expression COLON LINE_BREAK? block -elif_statement ::= ELIF expression COLON +elif_statement ::= ELIF expression COLON LINE_BREAK? block -else_statement ::= ELSE COLON +else_statement ::= ELSE COLON LINE_BREAK? block tool_statement ::= (TOOL | AT_TOOL) @@ -235,9 +235,9 @@ signal_statement ::= SIGNAL id (L_PAREN id? (COMMA id)* R_PAREN)? classname_statement ::= CLASS_NAME id (COMMA string)? -class_statement ::= CLASS id (EXTENDS type)? COLON +class_statement ::= CLASS id (EXTENDS type)? COLON LINE_BREAK? block -while_statement ::= WHILE expression COLON +while_statement ::= WHILE expression COLON LINE_BREAK? block assert_statement ::= ASSERT expression @@ -245,9 +245,9 @@ await_statement ::= AWAIT expression yield_statement ::= YIELD expression -match_statement ::= MATCH expression COLON +match_statement ::= MATCH expression COLON LINE_BREAK? block -match_label_statement ::= match_label_argument (COMMA match_label_argument)* COLON +match_label_statement ::= match_label_argument (COMMA match_label_argument)* COLON LINE_BREAK? block match_label_argument ::= expression | UNDERSCORE assign_statement ::= expression assign_operator expression @@ -328,7 +328,7 @@ lambda_expression ::= FUNC id? L_PAREN id? (COMMA id)* R_PAREN COLON expression key ::= expression type ::= id (L_BRACKET id R_BRACKET)? -block ::= INDENT statement DEDENT +block ::= INDENT statement+ DEDENT id ::= IDENTIFIER | INT | FLOAT | BOOL | VOID | NODE_PATH {methods=[getReference]} string ::= SINGLE_QUOTED_STRING | DOUBLE_QUOTED_STRING {methods=[getReference]} diff --git a/src/main/kotlin/gdscript/ScriptParserDefinition.kt b/src/main/kotlin/gdscript/ScriptParserDefinition.kt index bde71e7..9b9f87f 100644 --- a/src/main/kotlin/gdscript/ScriptParserDefinition.kt +++ b/src/main/kotlin/gdscript/ScriptParserDefinition.kt @@ -10,6 +10,7 @@ import com.intellij.psi.PsiElement import com.intellij.psi.PsiFile import com.intellij.psi.tree.IFileElementType import com.intellij.psi.tree.TokenSet +import gdscript.lexer.ScriptIndentLexer import gdscript.lexer.ScriptLexerAdapter import gdscript.parser.ScriptParser import gdscript.psi.ScriptElementTypes.* @@ -18,7 +19,7 @@ import gdscript.psi.ScriptElementTypes.* class ScriptParserDefinition : ParserDefinition { override fun createLexer(project: Project): Lexer = - ScriptLexerAdapter() + ScriptIndentLexer() override fun createParser(project: Project?): PsiParser = ScriptParser() diff --git a/src/main/kotlin/gdscript/lexer/ScriptIndentLexer.kt b/src/main/kotlin/gdscript/lexer/ScriptIndentLexer.kt index 1319b35..2d1d1f3 100644 --- a/src/main/kotlin/gdscript/lexer/ScriptIndentLexer.kt +++ b/src/main/kotlin/gdscript/lexer/ScriptIndentLexer.kt @@ -11,10 +11,13 @@ class ScriptIndentLexer : LexerBase() { private val pending = mutableListOf() private var prevEnd = 0 private var prevToken: IElementType? = null + private val depths = mutableListOf() + private var unfinishedIndent = false + private var bracesLevel = 0 override fun advance() { - prevToken = delegate.tokenType - prevEnd = delegate.tokenEnd + prevToken = tokenType + prevEnd = tokenEnd if (pending.isEmpty()) advanceAndProduce() else @@ -23,11 +26,26 @@ class ScriptIndentLexer : LexerBase() { private fun advanceAndProduce() { delegate.advance() - if (prevToken == LINE_BREAK) { + updateBraceLevel() + if (prevToken == LINE_BREAK && bracesLevel == 0) { val depth = calcDepth() - println("current $tokenType, prev $prevToken, depth: $depth") - pending += INDENT + val maxDepth = depths.maxOrNull() ?: 0 + if (depth > maxDepth) { + pending += INDENT + depths += depth + unfinishedIndent = true + } else if (depth < maxDepth) { + val iter = depths.iterator() + while (iter.hasNext() && iter.next() > depth) { + iter.remove() + pending += DEDENT + unfinishedIndent = false + } + } + } + if (tokenEnd == bufferEnd && tokenType == null && unfinishedIndent) { pending += DEDENT + unfinishedIndent = true } } @@ -39,6 +57,13 @@ class ScriptIndentLexer : LexerBase() { return 0 } + private fun updateBraceLevel() { + if (tokenType in OPENING_BRACES) + bracesLevel += 1 + if (tokenType in CLOSING_BRACES) + bracesLevel -= 1 + } + override fun getTokenType(): IElementType? { if (pending.isEmpty()) return delegate.tokenType @@ -70,4 +95,11 @@ class ScriptIndentLexer : LexerBase() { override fun getBufferEnd(): Int = delegate.bufferEnd + private companion object { + + val OPENING_BRACES = listOf(L_BRACE, L_BRACKET, L_PAREN) + val CLOSING_BRACES = listOf(R_BRACE, R_BRACKET, R_PAREN) + + } + } \ No newline at end of file diff --git a/src/test/kotlin/gdscript/lexer/ScriptIndentLexerTest.kt b/src/test/kotlin/gdscript/lexer/ScriptIndentLexerTest.kt index 07f444b..662e16b 100644 --- a/src/test/kotlin/gdscript/lexer/ScriptIndentLexerTest.kt +++ b/src/test/kotlin/gdscript/lexer/ScriptIndentLexerTest.kt @@ -5,21 +5,155 @@ import junit.framework.TestCase class ScriptIndentLexerTest : TestCase() { - fun `test indent`() { - val lexer = ScriptIndentLexer() + fun `test class with var and func`() { val code = """ - a - b - c - d - ${"\t\t"}e - f - g - h + class Xyz: + var x = 1 + func y(): + return 2 + """.trimIndent() - val printed = LexerTestCase.printTokens(code, 0, lexer) - println(printed) -// assertEquals(expected, printed) + assertTokensEquals(""" + class ('class') + WHITE_SPACE (' ') + IDENTIFIER ('Xyz') + : (':') + LINE_BREAK ('\n') + INDENT ('') + WHITE_SPACE (' ') + var ('var') + WHITE_SPACE (' ') + IDENTIFIER ('x') + WHITE_SPACE (' ') + = ('=') + WHITE_SPACE (' ') + REAL_NUMBER ('1') + LINE_BREAK ('\n') + WHITE_SPACE (' ') + func ('func') + WHITE_SPACE (' ') + IDENTIFIER ('y') + ( ('(') + ) (')') + : (':') + LINE_BREAK ('\n') + INDENT ('') + WHITE_SPACE (' ') + return ('return') + WHITE_SPACE (' ') + REAL_NUMBER ('2') + LINE_BREAK ('\n') + DEDENT ('') + DEDENT ('') + """.trimIndent(), code) + } + + fun `test func with indent`() { + val code = """ + func x(): + pass + """.trimIndent() + assertTokensEquals(""" + func ('func') + WHITE_SPACE (' ') + IDENTIFIER ('x') + ( ('(') + ) (')') + : (':') + LINE_BREAK ('\n') + INDENT ('') + WHITE_SPACE (' ') + pass ('pass') + DEDENT ('') + """.trimIndent(), code) + } + + fun `test spaces between two functions`() { + val code = """ + func x(): + pass + + + func y(): + pass + """.trimIndent() + assertTokensEquals(""" + func ('func') + WHITE_SPACE (' ') + IDENTIFIER ('x') + ( ('(') + ) (')') + : (':') + LINE_BREAK ('\n') + INDENT ('') + WHITE_SPACE (' ') + pass ('pass') + LINE_BREAK ('\n') + DEDENT ('') + LINE_BREAK ('\n') + LINE_BREAK ('\n') + func ('func') + WHITE_SPACE (' ') + IDENTIFIER ('y') + ( ('(') + ) (')') + : (':') + LINE_BREAK ('\n') + INDENT ('') + WHITE_SPACE (' ') + pass ('pass') + DEDENT ('') + """.trimIndent(), code) + } + + fun `test var`() { + val code = "var x = 0" + assertTokensEquals(""" + var ('var') + WHITE_SPACE (' ') + IDENTIFIER ('x') + WHITE_SPACE (' ') + = ('=') + WHITE_SPACE (' ') + REAL_NUMBER ('0') + """.trimIndent(), code) + } + + fun `test multiline nested arrays don't produce indents`() { + val code = """ + var x = [ + (1), + {2} + ] + """.trimIndent() + assertTokensEquals(""" + var ('var') + WHITE_SPACE (' ') + IDENTIFIER ('x') + WHITE_SPACE (' ') + = ('=') + WHITE_SPACE (' ') + [ ('[') + LINE_BREAK ('\n') + WHITE_SPACE (' ') + ( ('(') + REAL_NUMBER ('1') + ) (')') + , (',') + WHITE_SPACE (' ') + LINE_BREAK ('\n') + WHITE_SPACE (' ') + { ('{') + REAL_NUMBER ('2') + } ('}') + LINE_BREAK ('\n') + ] (']') + """.trimIndent(), code) + } + + private fun assertTokensEquals(expectedTokens: String, actualCode: String) { + val actualTokens = LexerTestCase.printTokens(actualCode, 0, ScriptIndentLexer()).trim() + assertEquals(expectedTokens, actualTokens) } } \ No newline at end of file diff --git a/src/test/kotlin/gdscript/lexer/ScriptLexerAdapterTest.kt b/src/test/kotlin/gdscript/lexer/ScriptLexerAdapterTest.kt index 2e51bd2..6a9fd5b 100644 --- a/src/test/kotlin/gdscript/lexer/ScriptLexerAdapterTest.kt +++ b/src/test/kotlin/gdscript/lexer/ScriptLexerAdapterTest.kt @@ -1,74 +1,86 @@ package gdscript.lexer -import BaseTest import com.intellij.testFramework.LexerTestCase +import junit.framework.TestCase -class ScriptLexerAdapterTest : BaseTest() { +class ScriptLexerAdapterTest : TestCase() { + + fun `test var statement`() { + val code = "var x = 0" + assertTokensEquals(""" + var ('var') + WHITE_SPACE (' ') + IDENTIFIER ('x') + WHITE_SPACE (' ') + = ('=') + WHITE_SPACE (' ') + REAL_NUMBER ('0') + """.trimIndent(), code) + } fun `test identifier`() { - assertTokenEquals("IDENTIFIER ('_x')", "_x") - assertTokenEquals("IDENTIFIER ('position_2')", "position_2") - assertTokenEquals("IDENTIFIER ('SomeClass')", "SomeClass") - assertTokenEquals("IDENTIFIER ('UPPER_CASE')", "UPPER_CASE") + assertTokensEquals("IDENTIFIER ('_x')", "_x") + assertTokensEquals("IDENTIFIER ('position_2')", "position_2") + assertTokensEquals("IDENTIFIER ('SomeClass')", "SomeClass") + assertTokensEquals("IDENTIFIER ('UPPER_CASE')", "UPPER_CASE") } fun `test node path`() { - assertTokenEquals("NODE_PATH ('\$MyNode')", "\$MyNode") - assertTokenEquals("NODE_PATH ('\$Outer/Inner')", "\$Outer/Inner") - assertTokenEquals("NODE_PATH ('\$\"../BodyPivot\"')", "\$\"../BodyPivot\"") + assertTokensEquals("NODE_PATH ('\$MyNode')", "\$MyNode") + assertTokensEquals("NODE_PATH ('\$Outer/Inner')", "\$Outer/Inner") + assertTokensEquals("NODE_PATH ('\$\"../BodyPivot\"')", "\$\"../BodyPivot\"") } fun `test real number`() { - assertTokenEquals("REAL_NUMBER ('3.14')", "3.14") - assertTokenEquals("REAL_NUMBER ('3.141_592_7')", "3.141_592_7") - assertTokenEquals("REAL_NUMBER ('58.1e-10')", "58.1e-10") - assertTokenEquals("REAL_NUMBER ('1e10')", "1e10") - assertTokenEquals("REAL_NUMBER ('.01')", ".01") - assertTokenEquals("REAL_NUMBER ('1.')", "1.") + assertTokensEquals("REAL_NUMBER ('3.14')", "3.14") + assertTokensEquals("REAL_NUMBER ('3.141_592_7')", "3.141_592_7") + assertTokensEquals("REAL_NUMBER ('58.1e-10')", "58.1e-10") + assertTokensEquals("REAL_NUMBER ('1e10')", "1e10") + assertTokensEquals("REAL_NUMBER ('.01')", ".01") + assertTokensEquals("REAL_NUMBER ('1.')", "1.") } fun `test hexadecimal number`() { - assertTokenEquals("HEXADECIMAL_NUMBER ('0x')", "0x") - assertTokenEquals("HEXADECIMAL_NUMBER ('0x8f51')", "0x8f51") - assertTokenEquals("HEXADECIMAL_NUMBER ('0x8080_0000_ffff')", "0x8080_0000_ffff") + assertTokensEquals("HEXADECIMAL_NUMBER ('0x')", "0x") + assertTokensEquals("HEXADECIMAL_NUMBER ('0x8f51')", "0x8f51") + assertTokensEquals("HEXADECIMAL_NUMBER ('0x8080_0000_ffff')", "0x8080_0000_ffff") } fun `test binary number`() { - assertTokenEquals("BINARY_NUMBER ('0b')", "0b") - assertTokenEquals("BINARY_NUMBER ('0b101010')", "0b101010") - assertTokenEquals("BINARY_NUMBER ('0b11_00_11_00')", "0b11_00_11_00") + assertTokensEquals("BINARY_NUMBER ('0b')", "0b") + assertTokensEquals("BINARY_NUMBER ('0b101010')", "0b101010") + assertTokensEquals("BINARY_NUMBER ('0b11_00_11_00')", "0b11_00_11_00") } fun `test double quoted string`() { - assertTokenEquals("DOUBLE_QUOTED_STRING ('\"\"')", "\"\"") - assertTokenEquals("DOUBLE_QUOTED_STRING ('\"aaa\"')", "\"aaa\"") + assertTokensEquals("DOUBLE_QUOTED_STRING ('\"\"')", "\"\"") + assertTokensEquals("DOUBLE_QUOTED_STRING ('\"aaa\"')", "\"aaa\"") } fun `test single quoted string`() { - assertTokenEquals("SINGLE_QUOTED_STRING ('''')", "''") - assertTokenEquals("SINGLE_QUOTED_STRING (''aaa'')", "'aaa'") + assertTokensEquals("SINGLE_QUOTED_STRING ('''')", "''") + assertTokensEquals("SINGLE_QUOTED_STRING (''aaa'')", "'aaa'") } fun `test unfinished string to line break`() { val expected = "DOUBLE_QUOTED_STRING ('\"a')\n" + "LINE_BREAK ('\\n')" - assertTokenEquals(expected, "\"a\n") + assertTokensEquals(expected, "\"a\n") } fun `test multiline double quoted string`() { - assertTokenEquals("MULTILINE_DOUBLE_QUOTED_STRING ('\"\"\"abc\"\"\"')", "\"\"\"abc\"\"\"") - assertTokenEquals("MULTILINE_DOUBLE_QUOTED_STRING ('\"\"\"\\nabc\\n\"\"\"')", "\"\"\"\nabc\n\"\"\"") + assertTokensEquals("MULTILINE_DOUBLE_QUOTED_STRING ('\"\"\"abc\"\"\"')", "\"\"\"abc\"\"\"") + assertTokensEquals("MULTILINE_DOUBLE_QUOTED_STRING ('\"\"\"\\nabc\\n\"\"\"')", "\"\"\"\nabc\n\"\"\"") } fun `test multiline single quoted string`() { - assertTokenEquals("MULTILINE_SINGLE_QUOTED_STRING ('\'\'\'abc\'\'\'')", "\'\'\'abc\'\'\'") - assertTokenEquals("MULTILINE_SINGLE_QUOTED_STRING ('\'\'\'\\nabc\\n\'\'\'')", "\'\'\'\nabc\n\'\'\'") + assertTokensEquals("MULTILINE_SINGLE_QUOTED_STRING ('\'\'\'abc\'\'\'')", "\'\'\'abc\'\'\'") + assertTokensEquals("MULTILINE_SINGLE_QUOTED_STRING ('\'\'\'\\nabc\\n\'\'\'')", "\'\'\'\nabc\n\'\'\'") } - private fun assertTokenEquals(expected: String, code: String) { - val adapter = ScriptLexerAdapter() - val printed = LexerTestCase.printTokens(code, 0, adapter).trim() - assertEquals(expected, printed) + private fun assertTokensEquals(expectedTokens: String, actualCode: String) { + val actualTokens = LexerTestCase.printTokens(actualCode, 0, ScriptLexerAdapter()).trim() + assertEquals(expectedTokens, actualTokens) } } diff --git a/src/test/kotlin/gdscript/parser/ScriptParserTest.kt b/src/test/kotlin/gdscript/parser/ScriptParserTest.kt index b094bc0..df38323 100644 --- a/src/test/kotlin/gdscript/parser/ScriptParserTest.kt +++ b/src/test/kotlin/gdscript/parser/ScriptParserTest.kt @@ -1,16 +1,89 @@ package gdscript.parser import BaseTest +import com.intellij.psi.impl.DebugUtil import com.intellij.testFramework.ParsingTestCase +import com.jetbrains.rd.util.string.print +import junit.framework.TestCase +import utils.openScript class ScriptParserTest : BaseTest() { - fun `test asd`() { + fun `test two func statements`() { val code = """ - var x: Vector2 = 1 + func x(): + pass + + + func x(): + pass + """.trimIndent() - val file = environment.configureByText("test.gd", code) - ParsingTestCase.ensureParsed(file) + assertPsiEquals(""" + FILE + ScriptFuncStatementImpl(FUNC_STATEMENT) + PsiElement(func)('func') + ScriptIdImpl(ID) + PsiElement(IDENTIFIER)('x') + PsiElement(()('(') + PsiElement())(')') + PsiElement(:)(':') + PsiElement(LINE_BREAK)('\n') + ScriptBlockImpl(BLOCK) + PsiElement(pass)('pass') + PsiElement(LINE_BREAK)('\n') + PsiElement(LINE_BREAK)('\n') + PsiElement(LINE_BREAK)('\n') + ScriptFuncStatementImpl(FUNC_STATEMENT) + PsiElement(func)('func') + ScriptIdImpl(ID) + PsiElement(IDENTIFIER)('x') + PsiElement(()('(') + PsiElement())(')') + PsiElement(:)(':') + PsiElement(LINE_BREAK)('\n') + ScriptBlockImpl(BLOCK) + PsiElement(pass)('pass') + PsiElement(LINE_BREAK)('\n') + """.trimIndent(), code) + } + + + fun `test func with if statement inside`() { + val code = """ + func x(): + if y: + pass + + """.trimIndent() + assertPsiEquals(""" + FILE + ScriptFuncStatementImpl(FUNC_STATEMENT) + PsiElement(func)('func') + ScriptIdImpl(ID) + PsiElement(IDENTIFIER)('x') + PsiElement(()('(') + PsiElement())(')') + PsiElement(:)(':') + PsiElement(LINE_BREAK)('\n') + ScriptBlockImpl(BLOCK) + ScriptIfStatementImpl(IF_STATEMENT) + PsiElement(if)('if') + ScriptExpressionImpl(EXPRESSION) + ScriptIdImpl(ID) + PsiElement(IDENTIFIER)('y') + PsiElement(:)(':') + PsiElement(LINE_BREAK)('\n') + ScriptBlockImpl(BLOCK) + PsiElement(pass)('pass') + PsiElement(LINE_BREAK)('\n') + """.trimIndent(), code) + } + + private fun assertPsiEquals(expectedPsi: String, actualCode: String) { + val file = environment.openScript(actualCode) + val actualPsi = DebugUtil.psiToString(file, true, false).trim() + assertEquals(expectedPsi, actualPsi) } } diff --git a/src/test/kotlin/utils/assertions.kt b/src/test/kotlin/utils/assertions.kt index ffe5303..2413e3c 100644 --- a/src/test/kotlin/utils/assertions.kt +++ b/src/test/kotlin/utils/assertions.kt @@ -1,11 +1,15 @@ package utils import com.intellij.codeInsight.daemon.impl.HighlightInfo -import junit.framework.TestCase.fail +import com.intellij.lexer.Lexer +import com.intellij.testFramework.LexerTestCase +import gdscript.lexer.ScriptLexerAdapter import junit.framework.TestCase.assertEquals +import junit.framework.TestCase.fail inline fun assertThrows( - invoke: () -> Unit, expectedMessage: String + invoke: () -> Unit, + expectedMessage: String ) { try { invoke() diff --git a/src/test/kotlin/utils/fixtures.kt b/src/test/kotlin/utils/fixtures.kt index 17f35aa..83fd0dd 100644 --- a/src/test/kotlin/utils/fixtures.kt +++ b/src/test/kotlin/utils/fixtures.kt @@ -1,5 +1,6 @@ package utils +import com.intellij.psi.PsiFile import gdscript.ScriptFileType import com.intellij.testFramework.fixtures.CodeInsightTestFixture as Fixture @@ -7,13 +8,14 @@ fun Fixture.configureEmpty(filename: String) { configureByText(filename, "") } +// todo rename: configureScript fun Fixture.openScript(filename: String, content: String) { configureByText(filename, content) } -fun Fixture.openScript(content: String) { - configureByText(ScriptFileType, content) -} +// todo configureScript +fun Fixture.openScript(content: String): PsiFile + = configureByText(ScriptFileType, content) fun Fixture.addFile(filename: String) { addFileToProject(filename, "bla bla")