From f7c090d846a472685ca50f0a56427a1fae3e8e70 Mon Sep 17 00:00:00 2001 From: giraud Date: Fri, 21 Sep 2018 08:31:58 +0200 Subject: [PATCH] Intention "Add braces to blockless function" is now working with React record fields (like render) --- .../java/com/reason/lang/ParserScopeEnum.java | 2 +- .../com/reason/lang/PsiElementFactory.java | 2 ++ .../reason/lang/core/psi/PsiMixinField.java | 16 ++++++++++ .../reason/lang/core/psi/type/MlTypes.java | 4 +-- .../java/com/reason/lang/ocaml/OclTypes.java | 1 + .../com/reason/lang/reason/RmlParser.java | 30 +++++++++++++------ .../java/com/reason/lang/reason/RmlTypes.java | 1 + .../com/reason/ide/folding/TypeFolding.re | 3 -- .../reason/ide/folding/TypeFoldingTest.java | 20 ------------- .../intention/FunctionBracesIntention.java | 15 ++++++++-- .../reason/lang/reason/RecordParsingTest.java | 21 +++++++++++++ 11 files changed, 77 insertions(+), 38 deletions(-) create mode 100644 src/main/java/com/reason/lang/core/psi/PsiMixinField.java delete mode 100644 testData/com/reason/ide/folding/TypeFolding.re delete mode 100644 tests/com/reason/ide/folding/TypeFoldingTest.java create mode 100644 tests/com/reason/lang/reason/RecordParsingTest.java diff --git a/src/main/java/com/reason/lang/ParserScopeEnum.java b/src/main/java/com/reason/lang/ParserScopeEnum.java index daf3259a5..292056af8 100644 --- a/src/main/java/com/reason/lang/ParserScopeEnum.java +++ b/src/main/java/com/reason/lang/ParserScopeEnum.java @@ -102,5 +102,5 @@ public enum ParserScopeEnum { executable, maybeLetFunction, maybeLetFunctionParameters, - valNamedSymbol, struct, matchException, beginScope, ifElseStatement, bracketGt, moduleDeclaration, moduleInstanciation, tryWith, moduleNamedColonWith, moduleNamedWithType, doLoop, tryWithScope, letBinding, scope, moduleNamedSignatureEq, array, signature, objectScope, clazzDeclaration, clazz, clazzNamed, clazzNamedEq, clazzBody, clazzBodyScope, clazzConstructor, clazzField, clazzFieldNamed, clazzMethod, clazzMethodNamed, clazzNamedParameters, clazzNamedConstructor, name + valNamedSymbol, struct, matchException, beginScope, ifElseStatement, bracketGt, moduleDeclaration, moduleInstanciation, tryWith, moduleNamedColonWith, moduleNamedWithType, doLoop, tryWithScope, letBinding, scope, moduleNamedSignatureEq, array, signature, objectScope, clazzDeclaration, clazz, clazzNamed, clazzNamedEq, clazzBody, clazzBodyScope, clazzConstructor, clazzField, clazzFieldNamed, clazzMethod, clazzMethodNamed, clazzNamedParameters, clazzNamedConstructor, record, mixin, name } diff --git a/src/main/java/com/reason/lang/PsiElementFactory.java b/src/main/java/com/reason/lang/PsiElementFactory.java index f32f9b89d..5e589dac5 100644 --- a/src/main/java/com/reason/lang/PsiElementFactory.java +++ b/src/main/java/com/reason/lang/PsiElementFactory.java @@ -68,6 +68,8 @@ public static PsiElement createElement(MlTypes types, ASTNode node) { return new PsiPatternMatch(node); } else if (type == types.RECORD_EXPR) { return new PsiRecord(node); + } else if (type == types.MIXIN_FIELD) { + return new PsiMixinField(node); } else if (type == types.RECORD_FIELD) { return new PsiRecordField(node); } else if (type == types.INTERPOLATION_EXPR) { diff --git a/src/main/java/com/reason/lang/core/psi/PsiMixinField.java b/src/main/java/com/reason/lang/core/psi/PsiMixinField.java new file mode 100644 index 000000000..4fb60f199 --- /dev/null +++ b/src/main/java/com/reason/lang/core/psi/PsiMixinField.java @@ -0,0 +1,16 @@ +package com.reason.lang.core.psi; + +import com.intellij.extapi.psi.ASTWrapperPsiElement; +import com.intellij.lang.ASTNode; + +public class PsiMixinField extends ASTWrapperPsiElement { + + public PsiMixinField(ASTNode node) { + super(node); + } + + @Override + public boolean canNavigate() { + return false; + } +} diff --git a/src/main/java/com/reason/lang/core/psi/type/MlTypes.java b/src/main/java/com/reason/lang/core/psi/type/MlTypes.java index 66d7613e2..deef02d0f 100644 --- a/src/main/java/com/reason/lang/core/psi/type/MlTypes.java +++ b/src/main/java/com/reason/lang/core/psi/type/MlTypes.java @@ -1,11 +1,8 @@ package com.reason.lang.core.psi.type; -import com.intellij.lang.Language; import com.intellij.psi.tree.IElementType; -import org.jetbrains.annotations.NotNull; public abstract class MlTypes { - // Composite element types public IElementType EXTERNAL_STMT; @@ -92,6 +89,7 @@ public abstract class MlTypes { public MlTokenElementType INITIALIZER; public MlTokenElementType LAZY; public MlTokenElementType LET; + public MlTokenElementType MIXIN_FIELD; public MlTokenElementType MODULE; public MlTokenElementType MUTABLE; public MlTokenElementType NEW; diff --git a/src/main/java/com/reason/lang/ocaml/OclTypes.java b/src/main/java/com/reason/lang/ocaml/OclTypes.java index f13c70246..f27526caf 100644 --- a/src/main/java/com/reason/lang/ocaml/OclTypes.java +++ b/src/main/java/com/reason/lang/ocaml/OclTypes.java @@ -126,6 +126,7 @@ private OclTypes() { MATCH = new MlTokenElementType("MATCH", OclLanguage.INSTANCE); MINUS = new MlTokenElementType("MINUS", OclLanguage.INSTANCE); MINUSDOT = new MlTokenElementType("MINUSDOT", OclLanguage.INSTANCE); + MIXIN_FIELD = new MlTokenElementType("MIXIN_FIELD", OclLanguage.INSTANCE); MODULE = new MlTokenElementType("MODULE", OclLanguage.INSTANCE); MUTABLE = new MlTokenElementType("MUTABLE", OclLanguage.INSTANCE); NONE = new MlTokenElementType("NONE", OclLanguage.INSTANCE); diff --git a/src/main/java/com/reason/lang/reason/RmlParser.java b/src/main/java/com/reason/lang/reason/RmlParser.java index f312bf612..352a132b1 100644 --- a/src/main/java/com/reason/lang/reason/RmlParser.java +++ b/src/main/java/com/reason/lang/reason/RmlParser.java @@ -86,6 +86,8 @@ else if (tokenType == m_types.SEMI) { parseAssert(builder, state); } else if (tokenType == m_types.IF) { parseIf(builder, state); + } else if (tokenType == m_types.DOTDOTDOT) { + parseDotDotDot(builder, state); } // ( ... ) else if (tokenType == m_types.LPAREN) { @@ -174,6 +176,16 @@ private void parseIf(PsiBuilder builder, ParserState state) { state.add(markComplete(builder, ifThenStatement, m_types.IF_STMT)); } + private void parseDotDotDot(PsiBuilder builder, ParserState state) { + if (state.previousTokenElementType == m_types.LBRACE) { + // Mixin: ... LBRACE LIDENT ... + state.updateCurrentResolution(recordBinding); + state.updateCurrentContext(record); + state.updateCurrentCompositeElementType(m_types.RECORD_EXPR); + state.add(mark(builder, recordBinding, mixin, m_types.MIXIN_FIELD)); + } + } + private void parseAssert(PsiBuilder builder, ParserState state) { state.add(markComplete(builder, assert_, m_types.ASSERT_STMT)); state.dontMove = advance(builder); @@ -210,6 +222,8 @@ private void parseComma(ParserState state) { state.complete(); state.endUntilContext(recordField); state.popEnd(); + } else if (state.isCurrentResolution(mixin)) { + state.popEnd(); } } @@ -504,6 +518,8 @@ private void parseLIdent(PsiBuilder builder, ParserState state) { }); } else if (state.isCurrentResolution(recordBinding)) { state.add(mark(builder, recordField, m_types.RECORD_FIELD)); + } else if (state.isCurrentResolution(mixin)) { + state.complete(); } else if (shouldStartExpression(state)) { state.add(mark(builder, genericExpression, builder.getTokenType())); } else { @@ -562,16 +578,12 @@ private void parseLBrace(PsiBuilder builder, ParserState state) { state.add(markScope(builder, scope, brace, m_types.SCOPED_EXPR, m_types.LBRACE)); } else if (state.isCurrentResolution(clazzNamedEq)) { state.add(markScope(builder, clazzBodyScope, m_types.SCOPED_EXPR, m_types.LBRACE)); + } else if (state.isCurrentResolution(switchBinaryCondition)) { + ParserScope switchScope = state.endUntilContext(switch_); + boolean isSwitch = switchScope != null && switchScope.isResolution(switch_); + state.add(markScope(builder, isSwitch ? switchBody : brace, m_types.SCOPED_EXPR, isSwitch ? m_types.SWITCH : m_types.LBRACE)); } else { - ParserScope switchScope; - if (state.isCurrentResolution(switchBinaryCondition)) { - switchScope = state.endUntilContext(switch_); - boolean isSwitch = switchScope != null && switchScope.isResolution(switch_); - state.add(markScope(builder, isSwitch ? switchBody : brace, m_types.SCOPED_EXPR, isSwitch ? m_types.SWITCH : m_types.LBRACE)); - } else { - state.add(markScope(builder, scope, brace, m_types.SCOPED_EXPR, m_types.LBRACE)); - } - + state.add(markScope(builder, scope, brace, m_types.SCOPED_EXPR, m_types.LBRACE)); } } diff --git a/src/main/java/com/reason/lang/reason/RmlTypes.java b/src/main/java/com/reason/lang/reason/RmlTypes.java index 5826ad850..b3d94fbba 100644 --- a/src/main/java/com/reason/lang/reason/RmlTypes.java +++ b/src/main/java/com/reason/lang/reason/RmlTypes.java @@ -123,6 +123,7 @@ private RmlTypes() { MATCH = new MlTokenElementType("MATCH", RmlLanguage.INSTANCE); MINUS = new MlTokenElementType("MINUS", RmlLanguage.INSTANCE); MINUSDOT = new MlTokenElementType("MINUSDOT", RmlLanguage.INSTANCE); + MIXIN_FIELD = new MlTokenElementType("MIXIN_FIELD", RmlLanguage.INSTANCE); MODULE = new MlTokenElementType("MODULE", RmlLanguage.INSTANCE); MUTABLE = new MlTokenElementType("MUTABLE", RmlLanguage.INSTANCE); NONE = new MlTokenElementType("NONE", RmlLanguage.INSTANCE); diff --git a/testData/com/reason/ide/folding/TypeFolding.re b/testData/com/reason/ide/folding/TypeFolding.re deleted file mode 100644 index a45403122..000000000 --- a/testData/com/reason/ide/folding/TypeFolding.re +++ /dev/null @@ -1,3 +0,0 @@ -type variant = - | B - | C; \ No newline at end of file diff --git a/tests/com/reason/ide/folding/TypeFoldingTest.java b/tests/com/reason/ide/folding/TypeFoldingTest.java deleted file mode 100644 index 77702661b..000000000 --- a/tests/com/reason/ide/folding/TypeFoldingTest.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.reason.ide.folding; - -import com.intellij.testFramework.fixtures.LightPlatformCodeInsightFixtureTestCase; - -public class TypeFoldingTest extends LightPlatformCodeInsightFixtureTestCase { - @Override - protected void setUp() throws Exception { - super.setUp(); - } - - @Override - protected String getTestDataPath() { - return "testData/com/reason/ide/folding"; - } - - public void testModuleLetCompletion() { - // NOT WORKING - // myFixture.testFolding(getTestDataPath() + "/TypeFolding.re"); - } -} \ No newline at end of file diff --git a/tests/com/reason/ide/intention/FunctionBracesIntention.java b/tests/com/reason/ide/intention/FunctionBracesIntention.java index ca2c834d2..a12c44d8f 100644 --- a/tests/com/reason/ide/intention/FunctionBracesIntention.java +++ b/tests/com/reason/ide/intention/FunctionBracesIntention.java @@ -4,19 +4,30 @@ import com.intellij.testFramework.fixtures.LightPlatformCodeInsightFixtureTestCase; import com.reason.ide.files.RmlFileType; +@SuppressWarnings("ConstantConditions") public class FunctionBracesIntention extends LightPlatformCodeInsightFixtureTestCase { public void testBasic() { myFixture.configureByText(RmlFileType.INSTANCE, "let add = (x, y) => x + y;"); - IntentionAction bracesAction = (IntentionAction) myFixture.getAvailableIntention("Add braces to blockless function"); + IntentionAction bracesAction = myFixture.getAvailableIntention("Add braces to blockless function"); myFixture.launchAction(bracesAction); + myFixture.checkResult("let add = (x, y) => { x + y; };"); } // https://github.com/reasonml-editor/reasonml-idea-plugin/issues/67 public void testInnerFunction() { myFixture.configureByText(RmlFileType.INSTANCE, "Js.Promise.( Api.all() |> then_(result => Js.log(result)) );"); - IntentionAction bracesAction = (IntentionAction) myFixture.getAvailableIntention("Add braces to blockless function"); + IntentionAction bracesAction = myFixture.getAvailableIntention("Add braces to blockless function"); myFixture.launchAction(bracesAction); + myFixture.checkResult("Js.Promise.( Api.all() |> then_(result => { Js.log(result); }) );"); } + + public void testReactComponentFunction() { + myFixture.configureByText(RmlFileType.INSTANCE, "let make = (children) => { ...component, render: self => v/>, };"); + IntentionAction bracesAction = myFixture.getAvailableIntention("Add braces to blockless function"); + myFixture.launchAction(bracesAction); + + myFixture.checkResult("let make = (children) => { ...component, render: self => {
; }, };"); + } } \ No newline at end of file diff --git a/tests/com/reason/lang/reason/RecordParsingTest.java b/tests/com/reason/lang/reason/RecordParsingTest.java new file mode 100644 index 000000000..1332192cd --- /dev/null +++ b/tests/com/reason/lang/reason/RecordParsingTest.java @@ -0,0 +1,21 @@ +package com.reason.lang.reason; + +import com.reason.lang.BaseParsingTestCase; +import com.reason.lang.core.psi.PsiLet; +import com.reason.lang.core.psi.PsiRecord; +import com.reason.lang.core.psi.PsiRecordField; + +public class RecordParsingTest extends BaseParsingTestCase { + public RecordParsingTest() { + super("", "re", new RmlParserDefinition()); + } + + public void testMixin() { + PsiLet let = first(letExpressions(parseCode("let x = {...component, otherField: 1};", true))); + + PsiRecord record = (PsiRecord) let.getBinding().getFirstChild(); + PsiRecordField field = record.getFields().iterator().next(); + assertEquals(field.getName(), "otherField"); + } + +}