Skip to content

Commit

Permalink
loop labels rename, completion, break and continue only inside loop
Browse files Browse the repository at this point in the history
  • Loading branch information
mkurnikov committed Oct 28, 2024
1 parent 11e9701 commit 4028c73
Show file tree
Hide file tree
Showing 15 changed files with 159 additions and 28 deletions.
12 changes: 6 additions & 6 deletions src/main/grammars/MoveParser.bnf
Original file line number Diff line number Diff line change
Expand Up @@ -841,7 +841,7 @@ private SpecExpr_first ::= include | apply | pragma | emits | assume | assert |
///////////////////////////////////////////////////////////////////////////////////////////////////
// Blocks
///////////////////////////////////////////////////////////////////////////////////////////////////
private AnyBlock ::= CodeBlock | InlineBlock
private AnyBlockImpl ::= CodeBlock | InlineBlock

InlineBlock ::= Expr

Expand Down Expand Up @@ -1141,29 +1141,29 @@ private MatchArm_with_recover ::= !'}' MatchArm
}
private MatchArm_recover ::= !(Pat_first | Attr_first | '}')

IfExpr ::= if Condition AnyBlock ElseBlock? { pin = 1 }
IfExpr ::= if Condition AnyBlockImpl ElseBlock? { pin = 1 }

Condition ::= '(' ConditionBody ')' { pin = 1 }
private ConditionBody ::= Expr { recoverWhile = "ConditionBody_recover" }
private ConditionBody_recover ::= !')'

ElseBlock ::= else AnyBlock { pin = 1 }
ElseBlock ::= else AnyBlockImpl { pin = 1 }

LoopExpr ::= LabelDecl? loop AnyBlock {
LoopExpr ::= LabelDecl? loop AnyBlockImpl {
pin = 2
implements = [
"org.move.lang.core.psi.ext.label.MvLabeledExpression"
"org.move.lang.core.psi.MvLoopLike"
]
}
WhileExpr ::= LabelDecl? while Condition AnyBlock SpecBlockExpr? {
WhileExpr ::= LabelDecl? while Condition AnyBlockImpl SpecBlockExpr? {
pin = 2
implements = [
"org.move.lang.core.psi.ext.label.MvLabeledExpression"
"org.move.lang.core.psi.MvLoopLike"
]
}
ForExpr ::= for ForIterCondition AnyBlock {
ForExpr ::= for ForIterCondition AnyBlockImpl {
pin = 1
implements = [ "org.move.lang.core.psi.MvLoopLike" ]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ import com.intellij.psi.PsiElement
import com.intellij.psi.impl.source.tree.LeafPsiElement
import org.move.cli.settings.moveSettings
import org.move.ide.colors.MvColor
import org.move.lang.MvElementTypes.HEX_INTEGER_LITERAL
import org.move.lang.MvElementTypes.IDENTIFIER
import org.move.lang.MvElementTypes.*
import org.move.lang.core.psi.*
import org.move.lang.core.psi.ext.*
import org.move.lang.core.types.infer.inference
Expand Down Expand Up @@ -49,6 +48,7 @@ class HighlightingAnnotator: MvAnnotatorBase() {
if (leafType.toString().endsWith("_kw")) return MvColor.KEYWORD
return when {
leafType == IDENTIFIER -> highlightIdentifier(parent)
leafType == QUOTE_IDENTIFIER -> MvColor.LABEL
leafType == HEX_INTEGER_LITERAL -> MvColor.NUMBER
parent is MvAssertMacroExpr -> MvColor.MACRO
parent is MvAttr -> MvColor.ATTRIBUTE
Expand Down
1 change: 1 addition & 0 deletions src/main/kotlin/org/move/ide/colors/MvColor.kt
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ enum class MvColor(humanName: String, default: TextAttributesKey? = null) {

MACRO("Functions//Macro", Default.IDENTIFIER),
ATTRIBUTE("Attribute", Default.METADATA),
LABEL("Label", Default.IDENTIFIER),

KEYWORD("Keywords//Keyword", Default.KEYWORD),
SELF_PARAMETER("Keywords//Self Parameter", Default.KEYWORD),
Expand Down
14 changes: 8 additions & 6 deletions src/main/kotlin/org/move/ide/refactoring/MvNamesValidator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,29 @@ import com.intellij.lang.refactoring.NamesValidator
import com.intellij.openapi.project.Project
import com.intellij.psi.tree.IElementType
import org.move.lang.MvElementTypes.IDENTIFIER
import org.move.lang.MvElementTypes.QUOTE_IDENTIFIER
import org.move.lang.core.MOVE_KEYWORDS
import org.move.lang.core.lexer.createMoveLexer

class MvNamesValidator : NamesValidator {
override fun isKeyword(name: String, project: Project?): Boolean {
return getLexerType(name) in MOVE_KEYWORDS
}
override fun isKeyword(name: String, project: Project?): Boolean = isKeyword(name)

override fun isIdentifier(name: String, project: Project?): Boolean = isIdentifier(name)

companion object {
fun isIdentifier(name: String): Boolean = when (getLexerType(name)) {
fun isIdentifier(name: String): Boolean = when (getLexerTokenType(name)) {
QUOTE_IDENTIFIER -> true
IDENTIFIER -> true
else -> false
}

fun isKeyword(name: String) = getLexerTokenType(name) in MOVE_KEYWORDS
}
}

fun isValidMoveVariableIdentifier(name: String): Boolean = getLexerType(name) == IDENTIFIER
fun isValidMoveVariableIdentifier(name: String): Boolean = getLexerTokenType(name) == IDENTIFIER

private fun getLexerType(text: String): IElementType? {
private fun getLexerTokenType(text: String): IElementType? {
val lexer = createMoveLexer()
lexer.start(text)
return if (lexer.tokenEnd == text.length) lexer.tokenType else null
Expand Down
16 changes: 16 additions & 0 deletions src/main/kotlin/org/move/lang/core/MvPsiPattern.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ object MvPsiPattern {
fun identifierStatementBeginningPattern(vararg startWords: String): PsiElementPattern.Capture<PsiElement> =
psiElement(IDENTIFIER).and(onStatementBeginning(*startWords))

// fun quoteIdentifierStatementBeginning(): PsiElementPattern.Capture<PsiElement> =
// psiElement(QUOTE_IDENTIFIER).and(onStatementBeginning())

// val onStatementBeginning: PsiElementPattern.Capture<PsiElement> =
// psiElement().with(OnStatementBeginning())

Expand Down Expand Up @@ -140,6 +143,19 @@ object MvPsiPattern {
return psiElement().withParent(simplePath)
}

val inAnyLoop: PsiElementPattern.Capture<PsiElement> =
psiElement().inside(
true,
psiElement<MvCodeBlock>().withParent(
or(
psiElement<MvForExpr>(),
psiElement<MvLoopExpr>(),
psiElement<MvWhileExpr>()
)
),
psiElement<MvLambdaExpr>()
)

class AfterSibling(val sibling: IElementType, val withPossibleError: Boolean = true):
PatternCondition<PsiElement>("afterSiblingKeywords") {
override fun accepts(t: PsiElement, context: ProcessingContext?): Boolean {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import com.intellij.patterns.PlatformPatterns.psiElement
import com.intellij.util.containers.addAllIfNotNull
import org.move.cli.settings.moveSettings
import org.move.lang.MvElementTypes.FUNCTION_PARAMETER_LIST
import org.move.lang.MvElementTypes.LABEL_DECL
import org.move.lang.core.FUNCTION_MODIFIERS
import org.move.lang.core.MvPsiPattern
import org.move.lang.core.MvPsiPattern.afterAnySibling
Expand All @@ -19,12 +20,14 @@ import org.move.lang.core.MvPsiPattern.identifierStatementBeginningPattern
import org.move.lang.core.MvPsiPattern.itemSpecStmt
import org.move.lang.core.MvPsiPattern.module
import org.move.lang.core.MvPsiPattern.moduleSpecBlock
import org.move.lang.core.MvPsiPattern.onStatementBeginning
import org.move.lang.core.MvPsiPattern.script
import org.move.lang.core.MvPsiPattern.toplevel
import org.move.lang.core.MvPsiPattern.typeParameter
import org.move.lang.core.TYPES
import org.move.lang.core.completion.providers.FunctionModifierCompletionProvider
import org.move.lang.core.completion.providers.KeywordCompletionProvider
import org.move.lang.core.tokenSetOf

class KeywordCompletionContributor: CompletionContributor() {
init {
Expand Down Expand Up @@ -102,8 +105,6 @@ class KeywordCompletionContributor: CompletionContributor() {
"let",
"loop",
"while",
"continue",
"break",
"if",
"else",
"abort",
Expand Down Expand Up @@ -155,6 +156,15 @@ class KeywordCompletionContributor: CompletionContributor() {
anySpecStart(),
KeywordCompletionProvider("module", "fun", "schema")
)
extend(
CompletionType.BASIC,
MvPsiPattern.inAnyLoop,
// .and(
//
// codeStatementPattern().and(identifierStatementBeginningPattern())
// ),
KeywordCompletionProvider("break", "continue")
)
}

override fun fillCompletionVariants(parameters: CompletionParameters, result: CompletionResultSet) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import com.intellij.patterns.PlatformPatterns
import com.intellij.psi.PsiElement
import com.intellij.util.ProcessingContext
import org.move.lang.core.completion.MvCompletionContext
import org.move.lang.core.psi.MvLabel
import org.move.lang.core.psi.MvPatBinding
import org.move.lang.core.psi.MvPatField
import org.move.lang.core.psi.ext.fieldNames
Expand All @@ -17,6 +18,7 @@ import org.move.lang.core.resolve.RsResolveProcessor
import org.move.lang.core.resolve.collectCompletionVariants
import org.move.lang.core.resolve.ref.MvReferenceElement
import org.move.lang.core.resolve.wrapWithFilter
import org.move.lang.core.resolve2.processLabelResolveVariants
import org.move.lang.core.resolve2.processPatBindingResolveVariants

object ReferenceCompletionProvider: MvCompletionProvider() {
Expand Down Expand Up @@ -57,6 +59,7 @@ object ReferenceCompletionProvider: MvCompletionProvider() {
val processor = skipAlreadyProvidedFields(element, processor0)
processPatBindingResolveVariants(element, true, processor)
}
is MvLabel -> processLabelResolveVariants(element, it)
}
}
}
Expand Down
4 changes: 1 addition & 3 deletions src/main/kotlin/org/move/lang/core/lexer/MoveLexer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ package org.move.lang.core.lexer
import com.intellij.lexer.FlexAdapter
import org.move.lang._MoveLexer

fun createMoveLexer(): MoveLexer {
return MoveLexer()
}
fun createMoveLexer(): MoveLexer = MoveLexer()

class MoveLexer : FlexAdapter(_MoveLexer(null))
2 changes: 1 addition & 1 deletion src/main/kotlin/org/move/lang/core/psi/MvPsiFactory.kt
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ class MvPsiFactory(val project: Project) {
?: error("Failed to create identifier: `$text`")

fun quoteIdentifier(identifierName: String): PsiElement =
createFromText<MvLabelDecl>("module 0x0::m { fun main() { '$identifierName: loop (); } }")
createFromText<MvLabelDecl>("module 0x0::m { fun main() { $identifierName: loop (); } }")
?.quoteIdentifier
?: error("Failed to create quote identifier: `$identifierName`")

Expand Down
2 changes: 2 additions & 0 deletions src/main/kotlin/org/move/utils/refactor.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package org.move.utils
import com.intellij.psi.PsiElement
import org.move.ide.refactoring.isValidMoveVariableIdentifier
import org.move.lang.MvElementTypes
import org.move.lang.MvElementTypes.QUOTE_IDENTIFIER
import org.move.lang.core.psi.MvPsiFactory
import org.move.lang.core.psi.ext.elementType

Expand All @@ -13,6 +14,7 @@ fun doRenameIdentifier(identifier: PsiElement, newName: String) {
if (!isValidMoveVariableIdentifier(newName)) return
factory.identifier(newName)
}
QUOTE_IDENTIFIER -> factory.quoteIdentifier(newName)
else -> error("Unsupported identifier type for `$newName` (${identifier.elementType})")
}
identifier.replace(newIdentifier)
Expand Down
6 changes: 6 additions & 0 deletions src/main/resources/colors/MoveDarcula.xml
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,10 @@
<option name="FOREGROUND" value="FFC66D" />
</value>
</option>
<option name="org.move.LABEL">
<value>
<option name="FOREGROUND" value="20999d"/>
<option name="FONT_TYPE" value="2"/>
</value>
</option>
</list>
6 changes: 6 additions & 0 deletions src/main/resources/colors/MoveDefault.xml
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,10 @@
<option name="FOREGROUND" value="DD6718" />
</value>
</option>
<option name="org.move.LABEL">
<value>
<option name="FOREGROUND" value="20999d"/>
<option name="FONT_TYPE" value="2"/>
</value>
</option>
</list>
Original file line number Diff line number Diff line change
Expand Up @@ -374,12 +374,14 @@ class HighlightingAnnotatorTest: AnnotatorTestCase(HighlightingAnnotator::class)
"""
)

// fun `test resource access control keywords highlighting`() = checkHighlighting(
// """
// module 0x1::m {
// fun f_multiple() <KEYWORD>reads</KEYWORD> R <KEYWORD>writes</KEYWORD> T, S <KEYWORD>reads</KEYWORD> G<u64> {}
// fun f_multiple2() <KEYWORD>pure</KEYWORD> {}
// }
// """
// )
@MoveV2
fun `test loop labels`() = checkHighlighting("""
module 0x1::m {
fun main() {
<LABEL>'label</LABEL>: loop {
break <LABEL>'label</LABEL>;
}
}
}
""")
}
18 changes: 18 additions & 0 deletions src/test/kotlin/org/move/ide/refactoring/RenameTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -559,6 +559,24 @@ class RenameTest : MvTestBase() {
"""
)

fun `test rename loop label`() = doTest("'mylabel", """
module 0x1::m {
fun main() {
'/*caret*/label: loop {
break 'label;
}
}
}
""", """
module 0x1::m {
fun main() {
'mylabel: loop {
break 'mylabel;
}
}
}
""")

private fun doTest(
newName: String,
@Language("Move") before: String,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package org.move.lang.completion.names

import org.move.utils.tests.completion.CompletionTestCase

class LoopCompletionTest: CompletionTestCase() {
fun `test no break keyword outside loop`() = checkNoCompletion("""
module 0x1::m {
fun main() {
bre/*caret*/
}
}
""")

fun `test break keyword inside loop`() = doSingleCompletion("""
module 0x1::m {
fun main() {
loop {
bre/*caret*/
}
}
}
""", """
module 0x1::m {
fun main() {
loop {
break /*caret*/
}
}
}
""")

fun `test complete loop label for break`() = doSingleCompletion("""
module 0x1::m {
fun main() {
'label: loop {
break 'la/*caret*/;
}
}
}
""", """
module 0x1::m {
fun main() {
'label: loop {
break 'label/*caret*/;
}
}
}
""")

fun `test complete loop label for break from single quote`() = doSingleCompletion("""
module 0x1::m {
fun main() {
'label: loop {
break '/*caret*/;
}
}
}
""", """
module 0x1::m {
fun main() {
'label: loop {
break 'label/*caret*/;
}
}
}
""")
}

0 comments on commit 4028c73

Please sign in to comment.