Tweaking for M68kRegisterFlowDocumentationProvider regarding colors, formatting and abbreviated documentation on hover. Prepared next release.

This commit is contained in:
Chris Hodges 2021-08-03 07:59:47 +02:00
parent 94001c8b87
commit 954f42bf63
4 changed files with 136 additions and 106 deletions

View File

@ -30,6 +30,7 @@ it's "good enough" to get started, and I can return to demo coding with its curr
- Mnemonics code completion - Mnemonics code completion
- Symbols / Labels / Macros code completion - Symbols / Labels / Macros code completion
- References / Refactoring support for local and global labels, symbol assignments, and macros. - References / Refactoring support for local and global labels, symbol assignments, and macros.
- Simple register usage flow (hover over register or press F1 for full flow)
- Brace matching - Brace matching
- Quote handler - Quote handler
- Goto Symbol support - Goto Symbol support
@ -70,7 +71,7 @@ make it work with JUnit 5. Feel free to use the code (in package ```de.platon42.
## Changelog ## Changelog
### V0.4 (unreleased) ### V0.4 (03-Aug-21)
- Notice: Due to major new API use, this plugin no longer works on IDEs >=2019.3.1, but rather requires >=2020.3. - Notice: Due to major new API use, this plugin no longer works on IDEs >=2019.3.1, but rather requires >=2020.3.
- Enhancement: Added Structure View filters. - Enhancement: Added Structure View filters.

View File

@ -57,7 +57,7 @@ runPluginVerifier {
patchPluginXml { patchPluginXml {
setChangeNotes(""" setChangeNotes("""
<h4>V0.4 (unreleased)</h4> <h4>V0.4 (03-Aug-21)</h4>
<ul> <ul>
<li>Notice: Due to major new API use, this plugin no longer works on IDEs >=2019.3.1, but rather requires >=2020.3. <li>Notice: Due to major new API use, this plugin no longer works on IDEs >=2019.3.1, but rather requires >=2020.3.
<li>Enhancement: Added Structure View filters. <li>Enhancement: Added Structure View filters.

View File

@ -8,6 +8,8 @@ import com.intellij.openapi.util.text.HtmlChunk
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile import com.intellij.psi.PsiFile
import com.intellij.psi.util.PsiTreeUtil import com.intellij.psi.util.PsiTreeUtil
import com.intellij.ui.ColorUtil
import com.intellij.ui.JBColor
import de.platon42.intellij.plugins.m68k.asm.* import de.platon42.intellij.plugins.m68k.asm.*
import de.platon42.intellij.plugins.m68k.asm.Register.Companion.getRegFromName import de.platon42.intellij.plugins.m68k.asm.Register.Companion.getRegFromName
import de.platon42.intellij.plugins.m68k.psi.* import de.platon42.intellij.plugins.m68k.psi.*
@ -21,83 +23,104 @@ class M68kRegisterFlowDocumentationProvider : AbstractDocumentationProvider() {
override fun generateDoc(element: PsiElement, originalElement: PsiElement?): String? { override fun generateDoc(element: PsiElement, originalElement: PsiElement?): String? {
if (element is M68kDataRegister || element is M68kAddressRegister) { if (element is M68kDataRegister || element is M68kAddressRegister) {
val register = getRegFromName(element.text) return createDoc(element as M68kRegister, 100) // TODO make this configurable
val addressingMode = PsiTreeUtil.getParentOfType(element, M68kAddressingMode::class.java) ?: return null
val asmInstruction = PsiTreeUtil.getParentOfType(addressingMode, M68kAsmInstruction::class.java) ?: return null
val (isaData, adrMode) = findExactIsaDataAndAllowedAdrModeForInstruction(asmInstruction) ?: return "unknown instruction"
val cursorInstRwmRegs = evaluateRegisterUse(asmInstruction, adrMode, register)
val opSize = getOpSizeOrDefault(asmInstruction.asmOp.opSize, adrMode)
val rn = register.regname
val thisInfo = cursorInstRwmRegs
.joinToString(separator = ", ", prefix = "${isaData.mnemonic} instruction ") {
rwmToDisplayText(it, rn)
}
val totalRwm = cursorInstRwmRegs.reduce(Int::or)
val firstOp = asmInstruction.addressingModeList[0] == addressingMode
val cursorRwm = modifyRwmWithOpsize((adrMode.modInfo ushr if (firstOp) RWM_OP1_SHIFT else RWM_OP2_SHIFT) and RWM_OP_MASK, opSize)
val analysisBuilder = HtmlBuilder()
val missingBits = if (cursorRwm and RWM_SET_L != 0) {
if (totalRwm and RWM_SET_L == RWM_SET_L) {
analysisBuilder.append(HtmlChunk.text("Register result is fully defined by this instruction."))
0
} else {
(RWM_SET_L and RWM_SIZE_MASK) and totalRwm.inv()
}
} else {
(RWM_SET_L and RWM_SIZE_MASK) and ((cursorRwm and RWM_MODIFY_L) ushr 8)
}
val backtrace: MutableList<HtmlChunk> = ArrayList()
val initialStatement: M68kStatement = asmInstruction.parent as M68kStatement
if (missingBits > 0) {
val localLabelName = PsiTreeUtil.findChildOfType(initialStatement, M68kLocalLabel::class.java)?.name ?: ""
backtrace.add(
HtmlChunk.tag("tr")
.children(DocumentationMarkup.SECTION_CONTENT_CELL.child(HtmlChunk.text(localLabelName)))
.children(highlightRegister(asmInstruction, register).bold().wrapWith(DocumentationMarkup.SECTION_CONTENT_CELL))
.children(DocumentationMarkup.SECTION_CONTENT_CELL.child(HtmlChunk.text(" ; <--")))
)
}
backtrace.addAll(analyseFlow(register, missingBits, true, initialStatement) { PsiTreeUtil.getPrevSiblingOfType(it, M68kStatement::class.java) })
backtrace.reverse()
val traceBits = (cursorRwm or (cursorRwm ushr 8)) and RWM_SIZE_MASK
backtrace.addAll(analyseFlow(register, traceBits, false, initialStatement) { PsiTreeUtil.getNextSiblingOfType(it, M68kStatement::class.java) })
val statementRows = HtmlBuilder()
backtrace.forEach(statementRows::append)
val builder = HtmlBuilder()
builder.append(HtmlChunk.text(thisInfo).wrapWith(DocumentationMarkup.DEFINITION_ELEMENT))
builder.append(statementRows.wrapWith(DocumentationMarkup.SECTIONS_TABLE.style("padding-left: 8pt; padding-right: 8pt")))
builder.append(analysisBuilder.wrapWith(DocumentationMarkup.CONTENT_ELEMENT))
return builder.toString()
} }
return null return null
} }
override fun generateHoverDoc(element: PsiElement, originalElement: PsiElement?): String? {
if (element is M68kDataRegister || element is M68kAddressRegister) {
return createDoc(element as M68kRegister, 4) // TODO make this configurable
}
return null
}
private fun createDoc(element: M68kRegister, linesLimit: Int): String? {
val register = getRegFromName(element.text)
val addressingMode = PsiTreeUtil.getParentOfType(element, M68kAddressingMode::class.java) ?: return null
val asmInstruction = PsiTreeUtil.getParentOfType(addressingMode, M68kAsmInstruction::class.java) ?: return null
val (isaData, adrMode) = findExactIsaDataAndAllowedAdrModeForInstruction(asmInstruction) ?: return "unknown instruction"
val cursorInstRwmRegs = evaluateRegisterUse(asmInstruction, adrMode, register)
val opSize = getOpSizeOrDefault(asmInstruction.asmOp.opSize, adrMode)
val rn = register.regname
val thisInfo = cursorInstRwmRegs
.joinToString(separator = ", ", prefix = "${isaData.mnemonic} instruction ") { rwmToDisplayText(it, rn) }
val totalRwm = cursorInstRwmRegs.reduce(Int::or)
val firstOp = asmInstruction.addressingModeList[0] == addressingMode
val cursorRwm = modifyRwmWithOpsize((adrMode.modInfo ushr if (firstOp) RWM_OP1_SHIFT else RWM_OP2_SHIFT) and RWM_OP_MASK, opSize)
val backtrace: MutableList<HtmlChunk> = ArrayList()
val missingBits = if (cursorRwm and RWM_SET_L != 0) {
if (totalRwm and RWM_SET_L == RWM_SET_L) {
backtrace.add(
HtmlChunk.tag("tr")
.children(
DocumentationMarkup.SECTION_CONTENT_CELL.attr("colspan", "3")
.child(HtmlChunk.text("Register result is fully defined by this instruction."))
)
)
0
} else {
(RWM_SET_L and RWM_SIZE_MASK) and totalRwm.inv()
}
} else {
(RWM_SET_L and RWM_SIZE_MASK) and ((cursorRwm and RWM_MODIFY_L) ushr 8)
}
val initialStatement: M68kStatement = asmInstruction.parent as M68kStatement
val localLabelName = PsiTreeUtil.findChildOfType(initialStatement, M68kLocalLabel::class.java)?.name ?: "-->"
backtrace.add(
HtmlChunk.tag("tr")
.children(DocumentationMarkup.SECTION_CONTENT_CELL.child(HtmlChunk.text(localLabelName)))
.children(highlightRegister(asmInstruction, register).bold().wrapWith(DocumentationMarkup.SECTION_CONTENT_CELL))
.children(DocumentationMarkup.SECTION_CONTENT_CELL.child(HtmlChunk.text(" ; <--")))
)
backtrace.addAll(analyseFlow(register, missingBits, true, initialStatement, linesLimit) {
PsiTreeUtil.getPrevSiblingOfType(
it,
M68kStatement::class.java
)
})
backtrace.reverse()
val traceBits = (cursorRwm or (cursorRwm ushr 8)) and RWM_SIZE_MASK
backtrace.addAll(analyseFlow(register, traceBits, false, initialStatement, linesLimit) {
PsiTreeUtil.getNextSiblingOfType(
it,
M68kStatement::class.java
)
})
val statementRows = HtmlBuilder()
backtrace.forEach(statementRows::append)
val builder = HtmlBuilder()
builder.append(HtmlChunk.text(thisInfo).wrapWith(DocumentationMarkup.DEFINITION_ELEMENT))
builder.append(statementRows.wrapWith(DocumentationMarkup.SECTIONS_TABLE.style("padding-left: 8pt; padding-right: 8pt")))
return builder.toString()
}
private fun analyseFlow( private fun analyseFlow(
register: Register, register: Register,
rwmBits: Int, rwmBits: Int,
globalLabelBreaksOnInitialStatement: Boolean, globalLabelBreaksOnInitialStatement: Boolean,
startingStatement: M68kStatement, startingStatement: M68kStatement,
linesLimit: Int,
direction: (statement: M68kStatement) -> M68kStatement? direction: (statement: M68kStatement) -> M68kStatement?
): MutableList<HtmlChunk> { ): MutableList<HtmlChunk> {
var missingBits = rwmBits var missingBits = rwmBits
var currStatement = startingStatement var currStatement = startingStatement
val backtrace: MutableList<HtmlChunk> = ArrayList() val statementLines: MutableList<HtmlChunk> = ArrayList()
val rn = register.regname val rn = register.regname
var addAbrevDots = false var addAbrevDots = false
var lines = 0
while (missingBits > 0) { while (missingBits > 0) {
val globalLabel = PsiTreeUtil.findChildOfType(currStatement, M68kGlobalLabel::class.java) val globalLabel = PsiTreeUtil.findChildOfType(currStatement, M68kGlobalLabel::class.java)
if ((globalLabel != null) && (globalLabelBreaksOnInitialStatement || (currStatement !== startingStatement))) { if ((globalLabel != null) && (globalLabelBreaksOnInitialStatement || (currStatement !== startingStatement))) {
backtrace.add( statementLines.add(
HtmlChunk.tag("tr") HtmlChunk.tag("tr")
.children(DocumentationMarkup.SECTION_CONTENT_CELL.child(HtmlChunk.text(globalLabel.name!!).bold())) .children(DocumentationMarkup.SECTION_CONTENT_CELL.attr("colspan", "3").child(HtmlChunk.text(globalLabel.name!!).bold()))
.children(DocumentationMarkup.SECTION_CONTENT_CELL.child(HtmlChunk.nbsp()))
.children(DocumentationMarkup.SECTION_CONTENT_CELL.child(HtmlChunk.nbsp()))
) )
break break
} }
@ -105,37 +128,35 @@ class M68kRegisterFlowDocumentationProvider : AbstractDocumentationProvider() {
val currAsmInstruction = PsiTreeUtil.getChildOfType(currStatement, M68kAsmInstruction::class.java) ?: continue val currAsmInstruction = PsiTreeUtil.getChildOfType(currStatement, M68kAsmInstruction::class.java) ?: continue
if (checkIfInstructionUsesRegister(currAsmInstruction, register)) { if (checkIfInstructionUsesRegister(currAsmInstruction, register)) {
if (addAbrevDots) { if (addAbrevDots) {
backtrace.add( ++lines
HtmlChunk.tag("tr") statementLines.add(createAbbreviationDots())
.children(DocumentationMarkup.SECTION_CONTENT_CELL.child(HtmlChunk.nbsp())) }
.children( if (++lines > linesLimit) {
DocumentationMarkup.SECTION_CONTENT_CELL.child( if (!addAbrevDots) {
HtmlChunk.text("[...]").wrapWith(HtmlChunk.div().attr("class", "grayed")) statementLines.add(createAbbreviationDots())
) }
) break
.children(DocumentationMarkup.SECTION_CONTENT_CELL.child(HtmlChunk.nbsp()))
)
} }
addAbrevDots = false addAbrevDots = false
val (_, currAdrMode) = findExactIsaDataAndAllowedAdrModeForInstruction(currAsmInstruction) ?: continue val (_, currAdrMode) = findExactIsaDataAndAllowedAdrModeForInstruction(currAsmInstruction) ?: continue
val localLabelName = PsiTreeUtil.findChildOfType(currStatement, M68kLocalLabel::class.java)?.name ?: "" val localLabelName = PsiTreeUtil.findChildOfType(currStatement, M68kLocalLabel::class.java)?.name ?: "        "
val currRwms = evaluateRegisterUse(currAsmInstruction, currAdrMode, register) val currRwms = evaluateRegisterUse(currAsmInstruction, currAdrMode, register)
val currTotalRwm = currRwms.reduce(Int::or) val currTotalRwm = currRwms.reduce(Int::or)
if ((currTotalRwm and RWM_SET_L) > 0) {
missingBits = missingBits and currTotalRwm.inv()
}
val lineInfo = currRwms val lineInfo = currRwms
.map { .map {
val text = HtmlChunk.text(rwmToDisplayText(it, rn)) val text = HtmlChunk.text(rwmToDisplayText(it, rn))
if ((missingBits and it) > 0) text.bold() else text.italic() if ((missingBits and it) > 0) text.wrapWith(HtmlChunk.font("#" + ColorUtil.toHex(JBColor.GREEN))) else text
} }
if ((currTotalRwm and RWM_SET_L) > 0) {
missingBits = missingBits and currTotalRwm.inv()
}
val lineBuilder = HtmlBuilder() val lineBuilder = HtmlBuilder()
lineBuilder.append(" ; ") lineBuilder.append(" ; ")
lineBuilder.appendWithSeparators(HtmlChunk.text(", "), lineInfo) lineBuilder.appendWithSeparators(HtmlChunk.text(", "), lineInfo)
.wrapWith(HtmlChunk.div()) .wrapWith(HtmlChunk.div())
backtrace.add( statementLines.add(
HtmlChunk.tag("tr") HtmlChunk.tag("tr")
.children(DocumentationMarkup.SECTION_CONTENT_CELL.child(HtmlChunk.text(localLabelName))) .children(DocumentationMarkup.SECTION_CONTENT_CELL.child(HtmlChunk.text(localLabelName)))
.children(highlightRegister(currAsmInstruction, register).wrapWith(DocumentationMarkup.SECTION_CONTENT_CELL)) .children(highlightRegister(currAsmInstruction, register).wrapWith(DocumentationMarkup.SECTION_CONTENT_CELL))
@ -145,7 +166,7 @@ class M68kRegisterFlowDocumentationProvider : AbstractDocumentationProvider() {
addAbrevDots = true addAbrevDots = true
} }
} }
return backtrace return statementLines
} }
private fun highlightRegister(currAsmInstruction: M68kAsmInstruction, register: Register): HtmlChunk { private fun highlightRegister(currAsmInstruction: M68kAsmInstruction, register: Register): HtmlChunk {
@ -158,7 +179,7 @@ class M68kRegisterFlowDocumentationProvider : AbstractDocumentationProvider() {
if (indexPos >= 0) { if (indexPos >= 0) {
builder.append(HtmlChunk.text(plainText.substring(startPos until indexPos))) builder.append(HtmlChunk.text(plainText.substring(startPos until indexPos)))
startPos = indexPos + register.regname.length startPos = indexPos + register.regname.length
builder.append(HtmlChunk.text(plainText.substring(indexPos until startPos)).wrapWith(HtmlChunk.font("red"))) builder.append(HtmlChunk.text(plainText.substring(indexPos until startPos)).wrapWith(HtmlChunk.font("#" + ColorUtil.toHex(JBColor.ORANGE))))
} else { } else {
builder.append(HtmlChunk.text(plainText.substring(startPos))) builder.append(HtmlChunk.text(plainText.substring(startPos)))
} }
@ -180,6 +201,15 @@ class M68kRegisterFlowDocumentationProvider : AbstractDocumentationProvider() {
else -> "uhm?" else -> "uhm?"
} }
private fun createAbbreviationDots() = HtmlChunk.tag("tr")
.children(DocumentationMarkup.SECTION_CONTENT_CELL.child(HtmlChunk.nbsp()))
.children(
DocumentationMarkup.SECTION_CONTENT_CELL.child(
HtmlChunk.text("[...]").wrapWith(HtmlChunk.div().attr("class", "grayed"))
)
)
.children(DocumentationMarkup.SECTION_CONTENT_CELL.child(HtmlChunk.nbsp()))
private fun evaluateRegisterUse( private fun evaluateRegisterUse(
asmInstruction: M68kAsmInstruction, asmInstruction: M68kAsmInstruction,
adrMode: AllowedAdrMode, adrMode: AllowedAdrMode,
@ -211,4 +241,4 @@ class M68kRegisterFlowDocumentationProvider : AbstractDocumentationProvider() {
if (parent is M68kDataRegister || parent is M68kAddressRegister) return parent if (parent is M68kDataRegister || parent is M68kAddressRegister) return parent
return null return null
} }
} }

View File

@ -37,14 +37,14 @@ nextlabel
<div class="definition">move instruction reads d0.w</div> <div class="definition">move instruction reads d0.w</div>
<table class="sections" style="padding-left: 8pt; padding-right: 8pt"> <table class="sections" style="padding-left: 8pt; padding-right: 8pt">
<tr> <tr>
<td valign="top"></td> <td valign="top">        </td>
<td valign="top"><code>moveq.l #0,<font color="red">d0</font></code></td> <td valign="top"><code>moveq.l #0,<font color="#ffc800">d0</font></code></td>
<td valign="top"> ; <i>sets d0.l</i></td> <td valign="top"> ; <font color="#00ff00">sets d0.l</font></td>
</tr> </tr>
<tr> <tr>
<td valign="top"></td> <td valign="top">        </td>
<td valign="top"><code>add.w #1,<font color="red">d0</font></code></td> <td valign="top"><code>add.w #1,<font color="#ffc800">d0</font></code></td>
<td valign="top"> ; <i>modifies d0.w</i></td> <td valign="top"> ; modifies d0.w</td>
</tr> </tr>
<tr> <tr>
<td valign="top">&nbsp;</td> <td valign="top">&nbsp;</td>
@ -54,29 +54,29 @@ nextlabel
<td valign="top">&nbsp;</td> <td valign="top">&nbsp;</td>
</tr> </tr>
<tr> <tr>
<td valign="top"></td> <td valign="top">        </td>
<td valign="top"><code>move.w <font color="red">d0</font>,d1</code></td> <td valign="top"><code>move.w <font color="#ffc800">d0</font>,d1</code></td>
<td valign="top"> ; <i>reads d0.w</i></td> <td valign="top"> ; reads d0.w</td>
</tr> </tr>
<tr> <tr>
<td valign="top"></td> <td valign="top">        </td>
<td valign="top"><code>move.b d2,<font color="red">d0</font></code></td> <td valign="top"><code>move.b d2,<font color="#ffc800">d0</font></code></td>
<td valign="top"> ; <i>sets d0.b</i></td> <td valign="top"> ; <font color="#00ff00">sets d0.b</font></td>
</tr> </tr>
<tr> <tr>
<td valign="top"></td> <td valign="top">        </td>
<td valign="top"><code>addq.b #1,<font color="red">d0</font></code></td> <td valign="top"><code>addq.b #1,<font color="#ffc800">d0</font></code></td>
<td valign="top"> ; <i>modifies d0.b</i></td> <td valign="top"> ; modifies d0.b</td>
</tr> </tr>
<tr> <tr>
<td valign="top"></td> <td valign="top">--&gt;</td>
<td valign="top"><b><code>move.w <font color="red">d0</font>,d1</code></b></td> <td valign="top"><b><code>move.w <font color="#ffc800">d0</font>,d1</code></b></td>
<td valign="top"> ; &lt;--</td> <td valign="top"> ; &lt;--</td>
</tr> </tr>
<tr> <tr>
<td valign="top"></td> <td valign="top">        </td>
<td valign="top"><code>move.l <font color="red">d0</font>,d2</code></td> <td valign="top"><code>move.l <font color="#ffc800">d0</font>,d2</code></td>
<td valign="top"> ; <i>reads d0.l</i></td> <td valign="top"> ; reads d0.l</td>
</tr> </tr>
<tr> <tr>
<td valign="top">&nbsp;</td> <td valign="top">&nbsp;</td>
@ -86,17 +86,16 @@ nextlabel
<td valign="top">&nbsp;</td> <td valign="top">&nbsp;</td>
</tr> </tr>
<tr> <tr>
<td valign="top"></td> <td valign="top">        </td>
<td valign="top"><code>clr.b <font color="red">d0</font></code></td> <td valign="top"><code>clr.b <font color="#ffc800">d0</font></code></td>
<td valign="top"> ; <i>sets d0.b</i></td> <td valign="top"> ; <font color="#00ff00">sets d0.b</font></td>
</tr> </tr>
<tr> <tr>
<td valign="top"></td> <td valign="top">        </td>
<td valign="top"><code>moveq.l #0,<font color="red">d0</font></code></td> <td valign="top"><code>moveq.l #0,<font color="#ffc800">d0</font></code></td>
<td valign="top"> ; <i>sets d0.l</i></td> <td valign="top"> ; <font color="#00ff00">sets d0.l</font></td>
</tr> </tr>
</table> </table>
<div class="content"/>
""".trimIndent() """.trimIndent()
) )
} }