From 593719043e561c72b0210fa3d7827f8a61377586 Mon Sep 17 00:00:00 2001 From: chrisly42 Date: Sun, 8 Aug 2021 13:23:15 +0200 Subject: [PATCH] New: Code completion for local label definitions, suggesting undefined labels already referenced. --- README.md | 3 +- build.gradle | 1 + .../plugins/m68k/lexer/_M68kLexer.java | 428 +++++++++--------- .../plugins/m68k/parser/M68kParser.java | 12 +- .../M68kRegisterFlowDocumentationProvider.kt | 20 +- .../intellij/plugins/m68k/lexer/LexerUtil.kt | 2 +- .../plugins/m68k/lexer/_M68kLexer.flex | 2 + .../platon42/intellij/plugins/m68k/m68k.bnf | 4 +- .../plugins/m68k/psi/M68kAddressModeUtil.kt | 7 - .../plugins/m68k/psi/M68kPsiImplUtil.kt | 2 +- .../M68kLocalLabelDefCompletionContributor.kt | 68 +++ src/main/resources/META-INF/plugin.xml | 1 + ...kLocalLabelDefCompletionContributorTest.kt | 57 +++ 13 files changed, 371 insertions(+), 236 deletions(-) create mode 100644 src/main/java/de/platon42/intellij/plugins/m68k/refs/M68kLocalLabelDefCompletionContributor.kt create mode 100644 src/test/java/de/platon42/intellij/plugins/m68k/refs/M68kLocalLabelDefCompletionContributorTest.kt diff --git a/README.md b/README.md index 0269e83..bb257f3 100644 --- a/README.md +++ b/README.md @@ -135,7 +135,7 @@ make it work with JUnit 5. Feel free to use the code (in package ```de.platon42. ## Private TODO list -- code completion suggestion for unresolved local labels, global labels and symbols +- code completion suggestion for unresolved global labels and symbols - support `include` directive - support `opt` directive - suppression support via comments @@ -149,6 +149,7 @@ make it work with JUnit 5. Feel free to use the code (in package ```de.platon42. - Enhancement: `opt` and several other directives (`printt`, `fail` etc.) no longer causes a syntax error when unquoted. - Enhancement: `include`, `incdir` and `incbin` and `output` with `` quotes no longer cause syntax error. - New: Files in `include` directives can be referenced and renamed/refactored. +- New: Code completion for local label definitions, suggesting undefined labels already referenced. ### V0.5 (06-Aug-21) diff --git a/build.gradle b/build.gradle index a459569..bbacc73 100644 --- a/build.gradle +++ b/build.gradle @@ -62,6 +62,7 @@ patchPluginXml {
  • Enhancement: 'opt' and several other directives ('printt', 'fail' etc.) no longer causes a syntax error when unquoted.
  • Enhancement: 'include', 'incdir' and 'incbin' and 'output' with '<pathname>' quotes no longer cause syntax error.
  • New: Files in 'include' directives can be referenced and renamed/refactored. +
  • New: Code completion for local label definitions, suggesting undefined labels already referenced.

    V0.5 (06-Aug-21)

      diff --git a/src/main/gen/de/platon42/intellij/plugins/m68k/lexer/_M68kLexer.java b/src/main/gen/de/platon42/intellij/plugins/m68k/lexer/_M68kLexer.java index 0e10b22..73b3d0b 100644 --- a/src/main/gen/de/platon42/intellij/plugins/m68k/lexer/_M68kLexer.java +++ b/src/main/gen/de/platon42/intellij/plugins/m68k/lexer/_M68kLexer.java @@ -40,7 +40,7 @@ public class _M68kLexer implements FlexLexer { public static final int ASSIGNMENT = 12; public static final int EXPR = 14; public static final int EXPR_OP = 16; - public static final int MACROCALL = 18; + public static final int PLAINPARAMS = 18; public static final int WAITEOL = 20; public static final int MACRODEF = 22; public static final int MACROLINE = 24; @@ -194,23 +194,23 @@ public class _M68kLexer implements FlexLexer { private static final int[] ZZ_ACTION = zzUnpackAction(); private static final String ZZ_ACTION_PACKED_0 = - "\17\0\1\1\2\2\1\3\1\4\1\1\1\5\1\6" + - "\1\7\1\10\1\1\1\7\2\11\1\7\1\10\1\12" + - "\1\13\1\14\1\1\1\15\1\16\1\17\4\16\1\1" + - "\1\16\5\1\1\20\1\21\1\22\1\23\1\24\1\25" + - "\1\26\1\27\1\30\1\31\2\16\1\32\1\33\1\34" + - "\1\35\1\36\1\37\1\40\1\41\1\42\1\43\1\44" + - "\1\45\1\46\1\47\1\42\1\50\2\1\1\51\1\16" + - "\1\52\1\11\4\52\1\53\1\11\2\54\2\55\1\56" + - "\1\57\3\60\1\57\1\60\1\0\1\4\1\61\2\62" + - "\1\63\3\0\1\7\1\64\1\65\1\66\1\67\1\70" + - "\1\71\1\16\1\72\1\73\1\16\1\74\1\75\2\0" + - "\1\76\4\0\1\77\1\16\1\32\1\100\1\101\1\102" + - "\1\103\1\104\1\105\1\106\3\0\1\52\1\0\1\52" + - "\1\0\1\52\1\0\1\52\1\55\1\57\3\0\1\4" + - "\1\62\1\63\1\7\1\107\1\7\1\110\1\111\1\112" + - "\1\113\1\0\1\52\3\0\1\55\1\57\3\0\1\7" + - "\1\55\1\57\2\0\1\114\1\115\1\116\1\0\1\117"; + "\17\0\1\1\2\2\1\3\1\4\1\5\1\6\1\7" + + "\1\10\1\11\1\1\1\10\2\12\1\10\1\11\1\13" + + "\1\14\1\15\1\1\1\16\1\17\1\20\4\17\1\1" + + "\1\17\5\1\1\21\1\22\1\23\1\24\1\25\1\26" + + "\1\27\1\30\1\31\1\32\2\17\1\33\1\34\1\35" + + "\1\36\1\37\1\40\1\41\1\42\1\43\1\44\1\45" + + "\1\46\1\47\1\50\1\43\1\51\2\1\1\52\1\17" + + "\1\53\1\12\4\53\1\54\1\12\2\55\2\56\1\57" + + "\1\60\3\61\1\60\1\61\1\0\1\4\1\62\2\63" + + "\1\64\3\0\1\10\1\65\1\66\1\67\1\70\1\71" + + "\1\72\1\17\1\73\1\74\1\17\1\75\1\76\2\0" + + "\1\77\4\0\1\100\1\17\1\33\1\101\1\102\1\103" + + "\1\104\1\105\1\106\1\107\3\0\1\53\1\0\1\53" + + "\1\0\1\53\1\0\1\53\1\56\1\60\3\0\1\4" + + "\1\63\1\64\1\10\1\110\1\10\1\111\1\112\1\113" + + "\1\114\1\0\1\53\3\0\1\56\1\60\3\0\1\10" + + "\1\56\1\60\2\0\1\115\1\116\1\117\1\0\1\120"; private static int[] zzUnpackAction() { int[] result = new int[184]; @@ -814,14 +814,14 @@ public class _M68kLexer implements FlexLexer { return BAD_CHARACTER; } // fall through - case 80: + case 81: break; case 2: { yybegin(YYINITIAL); return WHITE_SPACE; } // fall through - case 81: + case 82: break; case 3: { yybegin(NOSOL); @@ -829,7 +829,7 @@ public class _M68kLexer implements FlexLexer { return WHITE_SPACE; } // fall through - case 82: + case 83: break; case 4: { yybegin(INSTRPART); @@ -838,22 +838,30 @@ public class _M68kLexer implements FlexLexer { return GLOBAL_LABEL_DEF; } // fall through - case 83: - break; - case 5: { - yybegin(YYINITIAL); - return COMMENT; - } - // fall through case 84: break; - case 6: { - return WHITE_SPACE; + case 5: { + yybegin(INSTRPART); + eatOneWhitespace = false; + return LOCAL_LABEL_DEF; } // fall through case 85: break; + case 6: { + yybegin(YYINITIAL); + return COMMENT; + } + // fall through + case 86: + break; case 7: { + return WHITE_SPACE; + } + // fall through + case 87: + break; + case 8: { if (isAsmMnemonicWithSize(yytext())) { yybegin(ASMINSTR); yypushback(2); @@ -868,7 +876,7 @@ public class _M68kLexer implements FlexLexer { return DATA_DIRECTIVE; } if (isPlainDirective(yytext())) { - yybegin(MACROCALL); + yybegin(PLAINPARAMS); return OTHER_DIRECTIVE; } if (isOtherDirective(yytext())) { @@ -878,498 +886,498 @@ public class _M68kLexer implements FlexLexer { return handleMacroMode(this); } // fall through - case 86: - break; - case 8: { - return handleMacroMode(this); - } - // fall through - case 87: - break; - case 9: { - yybegin(YYINITIAL); - return EOL; - } - // fall through case 88: break; - case 10: { - return COLON; + case 9: { + return handleMacroMode(this); } // fall through case 89: break; - case 11: { - yybegin(WAITEOL); - return COMMENT; + case 10: { + yybegin(YYINITIAL); + return EOL; } // fall through case 90: break; - case 12: { - startExpr(ASMOPS, ASMOPS_OP); - return WHITE_SPACE; + case 11: { + return COLON; } // fall through case 91: break; - case 13: { - return handleEolCommentWhitespace(this); + case 12: { + yybegin(WAITEOL); + return COMMENT; } // fall through case 92: break; - case 14: { - yybegin(exprOpState); - return SYMBOL; + case 13: { + startExpr(ASMOPS, ASMOPS_OP); + return WHITE_SPACE; } // fall through case 93: break; - case 15: { - yybegin(exprOpState); - return DECIMAL; + case 14: { + return handleEolCommentWhitespace(this); } // fall through case 94: break; - case 16: { - return SEPARATOR; + case 15: { + yybegin(exprOpState); + return SYMBOL; } // fall through case 95: break; - case 17: { - return HASH; + case 16: { + yybegin(exprOpState); + return DECIMAL; } // fall through case 96: break; - case 18: { - return OP_BITWISE_XOR; + case 17: { + return SEPARATOR; } // fall through case 97: break; - case 19: { - return ROUND_L; + case 18: { + return HASH; } // fall through case 98: break; - case 20: { - yybegin(exprOpState); - return ROUND_R; + case 19: { + return OP_BITWISE_XOR; } // fall through case 99: break; - case 21: { - return OP_UNARY_NOT; + case 20: { + return ROUND_L; } // fall through case 100: break; - case 22: { - return OP_UNARY_COMPL; + case 21: { + yybegin(exprOpState); + return ROUND_R; } // fall through case 101: break; - case 23: { - return OP_PLUS; + case 22: { + return OP_UNARY_NOT; } // fall through case 102: break; - case 24: { - return OP_MINUS; + case 23: { + return OP_UNARY_COMPL; } // fall through case 103: break; - case 25: { - yybegin(exprOpState); - return CURRENT_PC_SYMBOL; + case 24: { + return OP_PLUS; } // fall through case 104: break; - case 26: { - yybegin(exprState); - return OP_CMP_EQ; + case 25: { + return OP_MINUS; } // fall through case 105: break; - case 27: { - yybegin(exprState); - return OP_AR_MOD; + case 26: { + yybegin(exprOpState); + return CURRENT_PC_SYMBOL; } // fall through case 106: break; - case 28: { + case 27: { yybegin(exprState); - return OP_CMP_LT; + return OP_CMP_EQ; } // fall through case 107: break; - case 29: { + case 28: { yybegin(exprState); - return OP_CMP_GT; + return OP_AR_MOD; } // fall through case 108: break; - case 30: { + case 29: { yybegin(exprState); - return SEPARATOR; + return OP_CMP_LT; } // fall through case 109: break; - case 31: { + case 30: { yybegin(exprState); - return OP_BITWISE_XOR; + return OP_CMP_GT; } // fall through case 110: break; - case 32: { + case 31: { yybegin(exprState); - return ROUND_L; + return SEPARATOR; } // fall through case 111: break; - case 33: { - return ROUND_R; + case 32: { + yybegin(exprState); + return OP_BITWISE_XOR; } // fall through case 112: break; - case 34: { + case 33: { yybegin(exprState); - return OP_BITWISE_OR; + return ROUND_L; } // fall through case 113: break; - case 35: { - yybegin(exprState); - return OP_UNARY_COMPL; + case 34: { + return ROUND_R; } // fall through case 114: break; - case 36: { + case 35: { yybegin(exprState); - return OP_PLUS; + return OP_BITWISE_OR; } // fall through case 115: break; - case 37: { + case 36: { yybegin(exprState); - return OP_MINUS; + return OP_UNARY_COMPL; } // fall through case 116: break; - case 38: { + case 37: { yybegin(exprState); - return OP_AR_MUL; + return OP_PLUS; } // fall through case 117: break; - case 39: { + case 38: { yybegin(exprState); - return OP_BITWISE_AND; + return OP_MINUS; } // fall through case 118: break; - case 40: { + case 39: { yybegin(exprState); - return OP_AR_DIV; + return OP_AR_MUL; } // fall through case 119: break; - case 41: { - startExpr(EXPR, EXPR_OP); - return OP_ASSIGN; + case 40: { + yybegin(exprState); + return OP_BITWISE_AND; } // fall through case 120: break; - case 42: { - return STRINGLIT; + case 41: { + yybegin(exprState); + return OP_AR_DIV; } // fall through case 121: break; - case 43: { - return COMMENT; + case 42: { + startExpr(EXPR, EXPR_OP); + return OP_ASSIGN; } // fall through case 122: break; + case 43: { + return STRINGLIT; + } + // fall through + case 123: + break; case 44: { + return COMMENT; + } + // fall through + case 124: + break; + case 45: { yybegin(MACROLINE); macroLines = 0; return WHITE_SPACE; } // fall through - case 123: - break; - case 45: { - return MACRO_NAME; - } - // fall through - case 124: - break; - case 46: { - yybegin(MACROWAITEOL); - return COMMENT; - } - // fall through case 125: break; - case 47: { - return MACRO_LINE; + case 46: { + return MACRO_NAME; } // fall through case 126: break; - case 48: { - return handleMacroLineEol(this); + case 47: { + yybegin(MACROWAITEOL); + return COMMENT; } // fall through case 127: break; + case 48: { + return MACRO_LINE; + } + // fall through + case 128: + break; case 49: { + return handleMacroLineEol(this); + } + // fall through + case 129: + break; + case 50: { yybegin(ASSIGNMENT); eatOneWhitespace = true; yypushback(pushbackAssignment(yytext())); return SYMBOLDEF; } // fall through - case 128: + case 130: break; - case 50: { + case 51: { yybegin(INSTRPART); eatOneWhitespace = false; yypushback(pushbackLabelColons(yytext())); return LOCAL_LABEL_DEF; } // fall through - case 129: + case 131: break; - case 51: { + case 52: { yybegin(INSTRPART); yypushback(pushbackLabelColons(yytext())); return GLOBAL_LABEL_DEF; } // fall through - case 130: - break; - case 52: { - return OPSIZE_BS; - } - // fall through - case 131: - break; - case 53: { - return OPSIZE_W; - } - // fall through case 132: break; - case 54: { - return OPSIZE_L; + case 53: { + return OPSIZE_BS; } // fall through case 133: break; - case 55: { - yybegin(exprOpState); - return AREG; + case 54: { + return OPSIZE_W; } // fall through case 134: break; - case 56: { - yybegin(exprOpState); - return REG_SP; + case 55: { + return OPSIZE_L; } // fall through case 135: break; - case 57: { + case 56: { yybegin(exprOpState); - return DREG; + return AREG; } // fall through case 136: break; - case 58: { + case 57: { yybegin(exprOpState); - return REG_SR; + return REG_SP; } // fall through case 137: break; - case 59: { + case 58: { yybegin(exprOpState); - return HEXADECIMAL; + return DREG; } // fall through case 138: break; - case 60: { + case 59: { yybegin(exprOpState); - return BINARY; + return REG_SR; } // fall through case 139: break; - case 61: { + case 60: { yybegin(exprOpState); - return OCTAL; + return HEXADECIMAL; } // fall through case 140: break; - case 62: { + case 61: { yybegin(exprOpState); - return STRINGLIT; + return BINARY; } // fall through case 141: break; - case 63: { + case 62: { yybegin(exprOpState); - return PC; + return OCTAL; } // fall through case 142: break; - case 64: { - yybegin(exprState); - return OP_CMP_LT_EQ; + case 63: { + yybegin(exprOpState); + return STRINGLIT; } // fall through case 143: break; - case 65: { - yybegin(exprState); - return OP_AR_SHIFT_L; + case 64: { + yybegin(exprOpState); + return PC; } // fall through case 144: break; - case 66: { + case 65: { yybegin(exprState); - return OP_CMP_NOT_EQ; + return OP_CMP_LT_EQ; } // fall through case 145: break; - case 67: { + case 66: { yybegin(exprState); - return OP_CMP_GT_EQ; + return OP_AR_SHIFT_L; } // fall through case 146: break; - case 68: { + case 67: { yybegin(exprState); - return OP_AR_SHIFT_R; + return OP_CMP_NOT_EQ; } // fall through case 147: break; - case 69: { + case 68: { yybegin(exprState); - return OP_LOGICAL_AND; + return OP_CMP_GT_EQ; } // fall through case 148: break; - case 70: { + case 69: { yybegin(exprState); - return OP_LOGICAL_OR; + return OP_AR_SHIFT_R; } // fall through case 149: break; + case 70: { + yybegin(exprState); + return OP_LOGICAL_AND; + } + // fall through + case 150: + break; case 71: { + yybegin(exprState); + return OP_LOGICAL_OR; + } + // fall through + case 151: + break; + case 72: { yybegin(INSTRPART); yypushback(pushbackLabelColons(yytext())); return LOCAL_LABEL_DEF; } // fall through - case 150: + case 152: break; - case 72: { + case 73: { yybegin(exprOpState); return REG_USP; } // fall through - case 151: + case 153: break; - case 73: { + case 74: { yybegin(exprOpState); return REG_CCR; } // fall through - case 152: + case 154: break; - case 74: { + case 75: { yybegin(exprOpState); return REG_VBR; } // fall through - case 153: + case 155: break; - case 75: { + case 76: { startExpr(EXPR, EXPR_OP); return EQU; } // fall through - case 154: + case 156: break; - case 76: { + case 77: { yybegin(MACRODEF); return MACRO_TAG; } // fall through - case 155: + case 157: break; - case 77: { + case 78: { return MACRO_TAG; } // fall through - case 156: + case 158: break; - case 78: { + case 79: { yybegin(MACROTERMINATION); return MACRO_END_TAG; } // fall through - case 157: + case 159: break; - case 79: { + case 80: { yybegin(MACRODEF); yypushback(pushbackAfterFirstToken(yytext())); return MACRO_NAME; } // fall through - case 158: + case 160: break; default: zzScanError(ZZ_NO_MATCH); diff --git a/src/main/gen/de/platon42/intellij/plugins/m68k/parser/M68kParser.java b/src/main/gen/de/platon42/intellij/plugins/m68k/parser/M68kParser.java index 0e5b698..db53253 100644 --- a/src/main/gen/de/platon42/intellij/plugins/m68k/parser/M68kParser.java +++ b/src/main/gen/de/platon42/intellij/plugins/m68k/parser/M68kParser.java @@ -1239,11 +1239,21 @@ public class M68kParser implements PsiParser, LightPsiParser { if (!recursion_guard_(b, l, "statement_recover")) return false; boolean r; Marker m = enter_section_(b, l, _NOT_); - r = !consumeToken(b, EOL); + r = !statement_recover_0(b, l + 1); exit_section_(b, l, m, r, false, null); return r; } + // (EOL) + private static boolean statement_recover_0(PsiBuilder b, int l) { + if (!recursion_guard_(b, l, "statement_recover_0")) return false; + boolean r; + Marker m = enter_section_(b); + r = consumeTokenFast(b, EOL); + exit_section_(b, m, null, r); + return r; + } + /* ********************************************************** */ // Expression root: expr // Operator priority table: diff --git a/src/main/java/de/platon42/intellij/plugins/m68k/documentation/M68kRegisterFlowDocumentationProvider.kt b/src/main/java/de/platon42/intellij/plugins/m68k/documentation/M68kRegisterFlowDocumentationProvider.kt index 87dd973..10fd9f6 100644 --- a/src/main/java/de/platon42/intellij/plugins/m68k/documentation/M68kRegisterFlowDocumentationProvider.kt +++ b/src/main/java/de/platon42/intellij/plugins/m68k/documentation/M68kRegisterFlowDocumentationProvider.kt @@ -79,18 +79,12 @@ class M68kRegisterFlowDocumentationProvider : AbstractDocumentationProvider() { ) backtrace.addAll(analyseFlow(register, missingBits, true, initialStatement, linesLimit) { - PsiTreeUtil.getPrevSiblingOfType( - it, - M68kStatement::class.java - ) + PsiTreeUtil.getPrevSiblingOfType(it, M68kStatement::class.java) }) backtrace.reverse() val traceBits = (cursorRwm or (cursorRwm ushr RWM_MODIFY_SHIFT) or (cursorRwm ushr RWM_READ_SHIFT)) and RWM_SIZE_MASK backtrace.addAll(analyseFlow(register, traceBits, false, initialStatement, linesLimit) { - PsiTreeUtil.getNextSiblingOfType( - it, - M68kStatement::class.java - ) + PsiTreeUtil.getNextSiblingOfType(it, M68kStatement::class.java) }) val statementRows = HtmlBuilder() @@ -113,7 +107,7 @@ class M68kRegisterFlowDocumentationProvider : AbstractDocumentationProvider() { var currStatement = startingStatement val statementLines = ArrayList() val rn = register.regname - var addAbrevDots = false + var addAbbrevDots = false var lines = 0 while (missingBits > 0) { val globalLabel = PsiTreeUtil.findChildOfType(currStatement, M68kGlobalLabel::class.java) @@ -127,17 +121,17 @@ class M68kRegisterFlowDocumentationProvider : AbstractDocumentationProvider() { currStatement = direction.invoke(currStatement) ?: break val currAsmInstruction = PsiTreeUtil.getChildOfType(currStatement, M68kAsmInstruction::class.java) ?: continue if (checkIfInstructionUsesRegister(currAsmInstruction, register)) { - if (addAbrevDots) { + if (addAbbrevDots) { ++lines statementLines.add(createAbbreviationDots()) } if (++lines > linesLimit) { - if (!addAbrevDots) { + if (!addAbbrevDots) { statementLines.add(createAbbreviationDots()) } break } - addAbrevDots = false + addAbbrevDots = false val (_, currAdrMode) = findExactIsaDataAndAllowedAdrModeForInstruction(currAsmInstruction) ?: continue val localLabelName = PsiTreeUtil.findChildOfType(currStatement, M68kLocalLabel::class.java)?.name ?: "        " @@ -163,7 +157,7 @@ class M68kRegisterFlowDocumentationProvider : AbstractDocumentationProvider() { .children(lineBuilder.wrapWith(DocumentationMarkup.SECTION_CONTENT_CELL)) ) } else { - addAbrevDots = true + addAbbrevDots = true } } return statementLines diff --git a/src/main/java/de/platon42/intellij/plugins/m68k/lexer/LexerUtil.kt b/src/main/java/de/platon42/intellij/plugins/m68k/lexer/LexerUtil.kt index 5d5d28a..5db5551 100644 --- a/src/main/java/de/platon42/intellij/plugins/m68k/lexer/LexerUtil.kt +++ b/src/main/java/de/platon42/intellij/plugins/m68k/lexer/LexerUtil.kt @@ -55,7 +55,7 @@ object LexerUtil { @JvmStatic fun handleMacroMode(lexer: _M68kLexer): IElementType { if (lexer.lexerPrefs.macroParametersUnparsed) { - lexer.yybegin(_M68kLexer.MACROCALL) + lexer.yybegin(_M68kLexer.PLAINPARAMS) } else { lexer.yybegin(_M68kLexer.ASMOPS) } diff --git a/src/main/java/de/platon42/intellij/plugins/m68k/lexer/_M68kLexer.flex b/src/main/java/de/platon42/intellij/plugins/m68k/lexer/_M68kLexer.flex index 089e527..3fc4322 100644 --- a/src/main/java/de/platon42/intellij/plugins/m68k/lexer/_M68kLexer.flex +++ b/src/main/java/de/platon42/intellij/plugins/m68k/lexer/_M68kLexer.flex @@ -1,5 +1,6 @@ package de.platon42.intellij.plugins.m68k.lexer; +import com.intellij.lexer.FlexLexer; import com.intellij.psi.tree.IElementType; import static com.intellij.psi.TokenType.BAD_CHARACTER; @@ -101,6 +102,7 @@ PLAIN_MACRO_LINE=[^;\r\n]+ {MACRO_DEF_LEFT} { yybegin(MACRODEF); yypushback(pushbackAfterFirstToken(yytext())); return MACRO_NAME; } {LOCAL_LABEL} { yybegin(INSTRPART); eatOneWhitespace = false; yypushback(pushbackLabelColons(yytext())); return LOCAL_LABEL_DEF; } {GLOBAL_LABEL} { yybegin(INSTRPART); eatOneWhitespace = false; yypushback(pushbackLabelColons(yytext())); return GLOBAL_LABEL_DEF; } + "." { yybegin(INSTRPART); eatOneWhitespace = false; return LOCAL_LABEL_DEF; } } { diff --git a/src/main/java/de/platon42/intellij/plugins/m68k/m68k.bnf b/src/main/java/de/platon42/intellij/plugins/m68k/m68k.bnf index 306e37d..e07cf14 100644 --- a/src/main/java/de/platon42/intellij/plugins/m68k/m68k.bnf +++ b/src/main/java/de/platon42/intellij/plugins/m68k/m68k.bnf @@ -123,9 +123,9 @@ private line ::= !<> (MacroDefinition | statement) (<>|EOL) statement ::= (Assignment | PreprocessorDirective | LabelInsts) - {pin=1 recoverWhile=statement_recover}; + {pin = 1 recoverWhile = statement_recover} -private statement_recover ::= !(EOL) +private statement_recover ::= !(EOL) { consumeTokenMethod = "consumeTokenFast" } SymbolDefinition ::= SYMBOLDEF { implements = "de.platon42.intellij.plugins.m68k.psi.M68kNamedElement" diff --git a/src/main/java/de/platon42/intellij/plugins/m68k/psi/M68kAddressModeUtil.kt b/src/main/java/de/platon42/intellij/plugins/m68k/psi/M68kAddressModeUtil.kt index d0f51f7..599b282 100644 --- a/src/main/java/de/platon42/intellij/plugins/m68k/psi/M68kAddressModeUtil.kt +++ b/src/main/java/de/platon42/intellij/plugins/m68k/psi/M68kAddressModeUtil.kt @@ -62,11 +62,4 @@ object M68kAddressModeUtil { else -> throw IllegalArgumentException("Unknown addressing mode $addressingMode") } } - - fun mergeReadWriteModifyRegisters(regset: Set>): Set> { - if (regset.size <= 1) return regset - return regset.groupBy({ it.first }) { it.second } - .map { it.key to if (it.value.size == 1) it.value.single() else it.value.reduce(Int::or) } - .toSet() - } } \ No newline at end of file diff --git a/src/main/java/de/platon42/intellij/plugins/m68k/psi/M68kPsiImplUtil.kt b/src/main/java/de/platon42/intellij/plugins/m68k/psi/M68kPsiImplUtil.kt index ba3ce20..2d7165c 100644 --- a/src/main/java/de/platon42/intellij/plugins/m68k/psi/M68kPsiImplUtil.kt +++ b/src/main/java/de/platon42/intellij/plugins/m68k/psi/M68kPsiImplUtil.kt @@ -29,7 +29,7 @@ object M68kPsiImplUtil { // Local Label @JvmStatic - fun getName(element: M68kLocalLabel): String? = element.firstChild.text + fun getName(element: M68kLocalLabel): String = element.firstChild?.text ?: "" @JvmStatic fun setName(element: M68kLocalLabel, name: String): PsiElement { diff --git a/src/main/java/de/platon42/intellij/plugins/m68k/refs/M68kLocalLabelDefCompletionContributor.kt b/src/main/java/de/platon42/intellij/plugins/m68k/refs/M68kLocalLabelDefCompletionContributor.kt new file mode 100644 index 0000000..51931d0 --- /dev/null +++ b/src/main/java/de/platon42/intellij/plugins/m68k/refs/M68kLocalLabelDefCompletionContributor.kt @@ -0,0 +1,68 @@ +package de.platon42.intellij.plugins.m68k.refs + +import com.intellij.codeInsight.completion.* +import com.intellij.codeInsight.lookup.LookupElementBuilder +import com.intellij.patterns.PlatformPatterns +import com.intellij.psi.PsiElement +import com.intellij.psi.util.PsiTreeUtil +import com.intellij.util.ProcessingContext +import de.platon42.intellij.plugins.m68k.psi.* + +class M68kLocalLabelDefCompletionContributor : CompletionContributor() { + + init { + extend( + CompletionType.BASIC, + PlatformPatterns.or(PlatformPatterns.psiElement(M68kTypes.LOCAL_LABEL_DEF), PlatformPatterns.psiElement(M68kTypes.GLOBAL_LABEL_DEF)), + object : CompletionProvider() { + override fun addCompletions(parameters: CompletionParameters, context: ProcessingContext, resultSet: CompletionResultSet) { + var topLevelElement = parameters.originalFile.findElementAt(parameters.offset) + while (topLevelElement?.parent !is M68kFile) { + topLevelElement = topLevelElement?.parent ?: return + } + // TODO find out if we can cache this somehow + val affectedStatements = ArrayList() + val definedLocalLabels = HashSet() + val referencedLocalLabels = HashSet() + findUndefinedLocalLabels(topLevelElement, affectedStatements, definedLocalLabels, referencedLocalLabels) { + PsiTreeUtil.getNextSiblingOfType(it, M68kStatement::class.java) + } + findUndefinedLocalLabels(topLevelElement, affectedStatements, definedLocalLabels, referencedLocalLabels) { + PsiTreeUtil.getPrevSiblingOfType(it, M68kStatement::class.java) + } + referencedLocalLabels.removeAll(definedLocalLabels) + resultSet.addAllElements( + if (parameters.originalPosition?.text == ".") { + referencedLocalLabels.map { LookupElementBuilder.create(it.removePrefix(".")) } + } else { + referencedLocalLabels.map(LookupElementBuilder::create) + } + ) + } + + private fun findUndefinedLocalLabels( + topLevelElement: PsiElement, + affectedStatements: MutableList, + definedLocalLabels: MutableSet, + referencedLocalLabels: MutableSet, + direction: (topLevelElement: PsiElement) -> M68kStatement? + ) { + var currStatement = topLevelElement + while (true) { + currStatement = direction.invoke(currStatement) ?: break + val globalLabel = PsiTreeUtil.findChildOfType(currStatement, M68kGlobalLabel::class.java) + if (globalLabel != null) break + affectedStatements.add(currStatement) + val localLabel = PsiTreeUtil.findChildOfType(currStatement, M68kLocalLabel::class.java) + if (localLabel != null) definedLocalLabels.add(localLabel.name!!) + val symbolReferences = PsiTreeUtil.findChildrenOfAnyType(currStatement, M68kSymbolReference::class.java) + if (symbolReferences.isNotEmpty()) { + referencedLocalLabels.addAll( + symbolReferences.filter(M68kSymbolReference::isLocalLabelRef).map(M68kSymbolReference::getSymbolName) + ) + } + } + } + }) + } +} \ No newline at end of file diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 50eae19..057cfff 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -24,6 +24,7 @@ + diff --git a/src/test/java/de/platon42/intellij/plugins/m68k/refs/M68kLocalLabelDefCompletionContributorTest.kt b/src/test/java/de/platon42/intellij/plugins/m68k/refs/M68kLocalLabelDefCompletionContributorTest.kt new file mode 100644 index 0000000..889f510 --- /dev/null +++ b/src/test/java/de/platon42/intellij/plugins/m68k/refs/M68kLocalLabelDefCompletionContributorTest.kt @@ -0,0 +1,57 @@ +package de.platon42.intellij.plugins.m68k.refs + +import com.intellij.testFramework.fixtures.CodeInsightTestFixture +import de.platon42.intellij.jupiter.LightCodeInsightExtension +import de.platon42.intellij.jupiter.MyFixture +import de.platon42.intellij.jupiter.TestDataPath +import de.platon42.intellij.jupiter.TestDataSubPath +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@TestDataPath("src/test/resources/references") +@TestDataSubPath("completion") +@ExtendWith(LightCodeInsightExtension::class) +internal class M68kLocalLabelDefCompletionContributorTest { + + @Test + internal fun completion_shows_undefined_local_labels_without_dot_prefix(@MyFixture myFixture: CodeInsightTestFixture) { + myFixture.configureByText( + "completeme.asm", """ +main +.loop + dbra d0,.loop + dbra d1,.loop2 +. + bra.s .foo + rts + +coolsubroutine + bra.s .narf + """ + ) + myFixture.completeBasic() + assertThat(myFixture.lookupElementStrings).containsExactlyInAnyOrder("loop2", "foo") + } + + @Test + internal fun completion_shows_undefined_local_labels_even_without_dot(@MyFixture myFixture: CodeInsightTestFixture) { + myFixture.configureByText( + "completeme.asm", """ +main +.loop + dbra d0,.loop + dbra d1,.loop2 + + bra.s .foo + bra.s bar$ + rts + +coolsubroutine + bra.s .narf + """ + ) + myFixture.completeBasic() + assertThat(myFixture.lookupElementStrings).containsExactlyInAnyOrder(".loop2", ".foo", "bar$") + } +} \ No newline at end of file