diff --git a/gen/gdscript/parser/ScriptParser.java b/gen/gdscript/parser/ScriptParser.java index 0d982bf..0b38a9a 100644 --- a/gen/gdscript/parser/ScriptParser.java +++ b/gen/gdscript/parser/ScriptParser.java @@ -256,7 +256,36 @@ public static boolean await_statement(PsiBuilder b, int l) { } /* ********************************************************** */ - // CLASS id (EXTENDS type)? COLON + // 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 && 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 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; @@ -266,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; } @@ -288,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) { @@ -575,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; @@ -584,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) { @@ -968,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 + // 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; @@ -987,6 +1043,8 @@ 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); + r = r && block(b, l + 1); exit_section_(b, l, m, r, false, null); return r; } @@ -1116,6 +1174,13 @@ private static boolean func_statement_12_0(PsiBuilder b, int l) { return r; } + // LINE_BREAK? + private static boolean func_statement_14(PsiBuilder b, int l) { + if (!recursion_guard_(b, l, "func_statement_14")) return false; + consumeToken(b, LINE_BREAK); + return true; + } + /* ********************************************************** */ // AT_ICON L_PAREN string R_PAREN public static boolean icon_statement(PsiBuilder b, int l) { @@ -1147,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; @@ -1156,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) { @@ -1473,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; @@ -1481,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; } @@ -1507,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; @@ -1517,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) { @@ -2164,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; @@ -2173,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 new file mode 100644 index 0000000..b28a4de --- /dev/null +++ b/gen/gdscript/psi/ScriptBlock.java @@ -0,0 +1,79 @@ +// 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 { + + @NotNull + List getAssertStatementList(); + + @NotNull + List getAssignStatementList(); + + @NotNull + List getAwaitStatementList(); + + @NotNull + List getClassStatementList(); + + @NotNull + List getClassnameStatementList(); + + @NotNull + List getConstStatementList(); + + @NotNull + List getElifStatementList(); + + @NotNull + List getElseStatementList(); + + @NotNull + List getEnumStatementList(); + + @NotNull + List getExpressionStatementList(); + + @NotNull + List getExtendsStatementList(); + + @NotNull + List getForStatementList(); + + @NotNull + List getFuncStatementList(); + + @NotNull + List getIconStatementList(); + + @NotNull + List getIfStatementList(); + + @NotNull + List getMatchLabelStatementList(); + + @NotNull + List getMatchStatementList(); + + @NotNull + List getReturnStatementList(); + + @NotNull + List getSignalStatementList(); + + @NotNull + List getToolStatementList(); + + @NotNull + List getVarStatementList(); + + @NotNull + List getWhileStatementList(); + + @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/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/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 477031d..c6e96fb 100644 --- a/gen/gdscript/psi/ScriptFuncStatement.java +++ b/gen/gdscript/psi/ScriptFuncStatement.java @@ -7,6 +7,9 @@ public interface ScriptFuncStatement extends PsiElement { + @NotNull + ScriptBlock getBlock(); + @NotNull List getFuncArgumentList(); 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/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/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 new file mode 100644 index 0000000..ef8a33a --- /dev/null +++ b/gen/gdscript/psi/impl/ScriptBlockImpl.java @@ -0,0 +1,168 @@ +// 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 + @NotNull + public List getAssertStatementList() { + return PsiTreeUtil.getChildrenOfTypeAsList(this, ScriptAssertStatement.class); + } + + @Override + @NotNull + public List getAssignStatementList() { + return PsiTreeUtil.getChildrenOfTypeAsList(this, ScriptAssignStatement.class); + } + + @Override + @NotNull + public List getAwaitStatementList() { + return PsiTreeUtil.getChildrenOfTypeAsList(this, ScriptAwaitStatement.class); + } + + @Override + @NotNull + public List getClassStatementList() { + return PsiTreeUtil.getChildrenOfTypeAsList(this, ScriptClassStatement.class); + } + + @Override + @NotNull + public List getClassnameStatementList() { + return PsiTreeUtil.getChildrenOfTypeAsList(this, ScriptClassnameStatement.class); + } + + @Override + @NotNull + public List getConstStatementList() { + return PsiTreeUtil.getChildrenOfTypeAsList(this, ScriptConstStatement.class); + } + + @Override + @NotNull + public List getElifStatementList() { + return PsiTreeUtil.getChildrenOfTypeAsList(this, ScriptElifStatement.class); + } + + @Override + @NotNull + public List getElseStatementList() { + return PsiTreeUtil.getChildrenOfTypeAsList(this, ScriptElseStatement.class); + } + + @Override + @NotNull + public List getEnumStatementList() { + return PsiTreeUtil.getChildrenOfTypeAsList(this, ScriptEnumStatement.class); + } + + @Override + @NotNull + public List getExpressionStatementList() { + return PsiTreeUtil.getChildrenOfTypeAsList(this, ScriptExpressionStatement.class); + } + + @Override + @NotNull + public List getExtendsStatementList() { + return PsiTreeUtil.getChildrenOfTypeAsList(this, ScriptExtendsStatement.class); + } + + @Override + @NotNull + public List getForStatementList() { + return PsiTreeUtil.getChildrenOfTypeAsList(this, ScriptForStatement.class); + } + + @Override + @NotNull + public List getFuncStatementList() { + return PsiTreeUtil.getChildrenOfTypeAsList(this, ScriptFuncStatement.class); + } + + @Override + @NotNull + public List getIconStatementList() { + return PsiTreeUtil.getChildrenOfTypeAsList(this, ScriptIconStatement.class); + } + + @Override + @NotNull + public List getIfStatementList() { + return PsiTreeUtil.getChildrenOfTypeAsList(this, ScriptIfStatement.class); + } + + @Override + @NotNull + public List getMatchLabelStatementList() { + return PsiTreeUtil.getChildrenOfTypeAsList(this, ScriptMatchLabelStatement.class); + } + + @Override + @NotNull + public List getMatchStatementList() { + return PsiTreeUtil.getChildrenOfTypeAsList(this, ScriptMatchStatement.class); + } + + @Override + @NotNull + public List getReturnStatementList() { + return PsiTreeUtil.getChildrenOfTypeAsList(this, ScriptReturnStatement.class); + } + + @Override + @NotNull + public List getSignalStatementList() { + return PsiTreeUtil.getChildrenOfTypeAsList(this, ScriptSignalStatement.class); + } + + @Override + @NotNull + public List getToolStatementList() { + return PsiTreeUtil.getChildrenOfTypeAsList(this, ScriptToolStatement.class); + } + + @Override + @NotNull + public List getVarStatementList() { + return PsiTreeUtil.getChildrenOfTypeAsList(this, ScriptVarStatement.class); + } + + @Override + @NotNull + public List getWhileStatementList() { + return PsiTreeUtil.getChildrenOfTypeAsList(this, ScriptWhileStatement.class); + } + + @Override + @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 a71d311..7f833b4 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 + @NotNull + public ScriptBlock getBlock() { + return findNotNullChildByClass(ScriptBlock.class); + } + @Override @NotNull public List getFuncArgumentList() { 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 907a2b1..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 +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,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/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 new file mode 100644 index 0000000..2d1d1f3 --- /dev/null +++ b/src/main/kotlin/gdscript/lexer/ScriptIndentLexer.kt @@ -0,0 +1,105 @@ +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.* + +class ScriptIndentLexer : LexerBase() { + + private val delegate = ScriptLexerAdapter() + 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 = tokenType + prevEnd = tokenEnd + if (pending.isEmpty()) + advanceAndProduce() + else + pending.removeFirst() + } + + private fun advanceAndProduce() { + delegate.advance() + updateBraceLevel() + if (prevToken == LINE_BREAK && bracesLevel == 0) { + val depth = calcDepth() + 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 + } + } + + private fun calcDepth(): Int { + if (tokenType == WHITE_SPACE) + return tokenText + .map { c -> if (c == '\t') 4 else 1 } + .sum() + 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 + return pending.first() + } + + 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 prevEnd + } + + override fun getTokenEnd(): Int { + if (pending.isEmpty()) + return delegate.tokenEnd + return prevEnd + } + + override fun getBufferSequence(): CharSequence = + delegate.bufferSequence + + 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 new file mode 100644 index 0000000..662e16b --- /dev/null +++ b/src/test/kotlin/gdscript/lexer/ScriptIndentLexerTest.kt @@ -0,0 +1,159 @@ +package gdscript.lexer + +import com.intellij.testFramework.LexerTestCase +import junit.framework.TestCase + +class ScriptIndentLexerTest : TestCase() { + + fun `test class with var and func`() { + val code = """ + class Xyz: + var x = 1 + func y(): + return 2 + + """.trimIndent() + 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")