Skip to content

Commit

Permalink
add additional intentions for converting literals to binary and decim…
Browse files Browse the repository at this point in the history
…al conversions
  • Loading branch information
mike42 committed Jan 30, 2022
1 parent 4562721 commit c682953
Show file tree
Hide file tree
Showing 14 changed files with 228 additions and 27 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ runPluginVerifier {
}

patchPluginXml {
changeNotes = """This change adds an intention action for converting numeric literals to hexadecimal."""
changeNotes = """This change adds intention actions for converting numeric literals between hexadecimal, decimal and binary representations."""
sinceBuild = '211.6693'
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package org.ca65.action;

import com.intellij.codeInsight.intention.impl.BaseIntentionAction;
import com.intellij.codeInspection.util.IntentionFamilyName;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.util.IncorrectOperationException;
import org.apache.commons.lang.StringUtils;
import org.ca65.Asm6502Bundle;
import org.ca65.psi.AsmElementFactory;
import org.ca65.psi.AsmFile;
import org.ca65.psi.AsmNumericLiteral;
import org.jetbrains.annotations.NotNull;

import static org.ca65.action.IntentionActionUtil.*;

public class ConvertNumberToBinaryIntentionAction extends BaseIntentionAction {
@Override
public @NotNull @IntentionFamilyName String getFamilyName() {
return Asm6502Bundle.message("INTN.NAME.convert.to.bin");
}

@Override
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
if (!(file instanceof AsmFile)) {
return false;
}
AsmNumericLiteral literal = getAsmNumericLiteral(editor, file);
if (literal == null) { // Caret is not over a numeric literal
return false;
}
String text = literal.getText();
if (!canConvertToBinary(text)) {
return false;
}
setText(Asm6502Bundle.message("INTN.convert.to.bin", literal.getText()));
return true;
}

private static boolean canConvertToBinary(String text) {
return isConvertibleDec(text) || isConvertibleHex(text);
}

private static String doConvertToBinary(String str) {
// Parse
final int intValue;
if(str.startsWith("$")) {
intValue = Integer.parseInt(str.substring(1), 16); // From hex eg. "$ff"
} else {
intValue = Integer.parseInt(str, 10); // From dec eg. "42"
}
String binString = Integer.toBinaryString(intValue);
// Pad to 8 bit, 16 bit, 24-bit values.
int currentLen = binString.length();
int remainder = currentLen % 8;
if(remainder != 0) {
// Pad to multiple of 8 bits
binString = StringUtils.leftPad(binString, (currentLen + 8) - remainder, "0");
}
return "%" + binString; // Prefixed with %
}

@Override
public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
AsmNumericLiteral literal = getAsmNumericLiteral(editor, file);
if(literal == null || !canConvertToBinary(literal.getText())) {
// Some weirdness if this happens
return;
}
String replacement = doConvertToBinary(literal.getText());
PsiElement newLiteral = AsmElementFactory.createNumericLiteral(project, replacement);
literal.replace(newLiteral);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package org.ca65.action;

import com.intellij.codeInsight.intention.impl.BaseIntentionAction;
import com.intellij.codeInspection.util.IntentionFamilyName;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.util.IncorrectOperationException;
import org.ca65.Asm6502Bundle;
import org.ca65.psi.AsmElementFactory;
import org.ca65.psi.AsmFile;
import org.ca65.psi.AsmNumericLiteral;
import org.jetbrains.annotations.NotNull;

import static org.ca65.action.IntentionActionUtil.*;

public class ConvertNumberToDecimalIntentionAction extends BaseIntentionAction {
@Override
public @NotNull @IntentionFamilyName String getFamilyName() {
return Asm6502Bundle.message("INTN.NAME.convert.to.dec");
}

@Override
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
if (!(file instanceof AsmFile)) {
return false;
}
AsmNumericLiteral literal = getAsmNumericLiteral(editor, file);
if (literal == null) { // Caret is not over a numeric literal
return false;
}
String text = literal.getText();
if (!canConvertToDecimal(text)) {
return false;
}
setText(Asm6502Bundle.message("INTN.convert.to.dec", literal.getText()));
return true;
}

private static boolean canConvertToDecimal(String text) {
return isConvertibleBin(text) || isConvertibleHex(text);
}

private static String doConvertToDecimal(String str) {
// Parse
final int intValue;
if(str.startsWith("%")) {
intValue = Integer.parseInt(str.substring(1), 2); // From bin eg. "%01010"
} else {
intValue = Integer.parseInt(str.substring(1), 16); // From hex eg. "$ff"
}
return Integer.toString(intValue);
}

@Override
public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
AsmNumericLiteral literal = getAsmNumericLiteral(editor, file);
if(literal == null || !canConvertToDecimal(literal.getText())) {
// Some weirdness if this happens
return;
}
String replacement = doConvertToDecimal(literal.getText());
PsiElement newLiteral = AsmElementFactory.createNumericLiteral(project, replacement);
literal.replace(newLiteral);
}
}
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
package org.ca65.action;

import com.intellij.codeInsight.TargetElementUtilBase;
import com.intellij.codeInsight.intention.impl.BaseIntentionAction;
import com.intellij.codeInspection.util.IntentionFamilyName;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.IncorrectOperationException;
import org.ca65.Asm6502Bundle;
import org.ca65.psi.AsmElementFactory;
import org.ca65.psi.AsmFile;
import org.ca65.psi.AsmNumericLiteral;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import static org.ca65.action.IntentionActionUtil.*;

public class ConvertNumberToHexadecimalIntentionAction extends BaseIntentionAction {
@Override
Expand All @@ -39,36 +38,17 @@ public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file
return true;
}

@Nullable
private AsmNumericLiteral getAsmNumericLiteral(Editor editor, PsiFile file) {
final int offset = TargetElementUtilBase.adjustOffset(file, editor.getDocument(), editor.getCaretModel().getOffset());
return PsiTreeUtil.getParentOfType(file.findElementAt(offset), AsmNumericLiteral.class);
}

private static boolean canConvertToHex(String str) {
if(str.startsWith("$")) { // Already in hex
return false;
}
if(str.startsWith("%")) {
// In binary, just do length check for 24 bits max.
return str.length() > 1 && str.length() <= 25;
}
// Decimal literal. Check length first to avoid overflow weirdness
int maxValue = 16777215;
if(str.length() > Integer.toString(maxValue).length()) {
return false;
}
int parsedValue = Integer.parseInt(str);
return parsedValue >= 0 && parsedValue <= maxValue;
return isConvertibleDec(str) || isConvertibleBin(str);
}

private static String doConvertToHex(String str) {
// Parse
final int intValue;
if(str.startsWith("%")) {
intValue = Integer.parseInt(str.substring(1), 2);
intValue = Integer.parseInt(str.substring(1), 2);; // From bin eg. "%01010"
} else {
intValue = Integer.parseInt(str, 10);
intValue = Integer.parseInt(str, 10); // From dec eg. "42"
}
String rawHexString = Integer.toHexString(intValue);
if(rawHexString.length() % 2 == 1) { // Even number of digits. Expect 8 bit, 16 bit, 24-bit values.
Expand Down
44 changes: 44 additions & 0 deletions src/main/java/org/ca65/action/IntentionActionUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package org.ca65.action;

import com.intellij.codeInsight.TargetElementUtilBase;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.PsiFile;
import com.intellij.psi.util.PsiTreeUtil;
import org.ca65.psi.AsmNumericLiteral;
import org.jetbrains.annotations.Nullable;

public class IntentionActionUtil {
@Nullable
public static AsmNumericLiteral getAsmNumericLiteral(Editor editor, PsiFile file) {
final int offset = TargetElementUtilBase.adjustOffset(file, editor.getDocument(), editor.getCaretModel().getOffset());
return PsiTreeUtil.getParentOfType(file.findElementAt(offset), AsmNumericLiteral.class);
}

public static boolean isConvertibleHex(String text) {
if(!text.startsWith("$")) { // Already in hex
return false;
}
return text.length() > 1 && text.length() <= 7;
}

public static boolean isConvertibleBin(String text) {
if(!text.startsWith("%")) {
return false;
}
// In binary, just do length check for 24 bits max.
return text.length() > 1 && text.length() <= 25;
}

public static boolean isConvertibleDec(String text) {
if(text.startsWith("%") || text.startsWith("$")) {
return false;
}
// Decimal literal. Check length first to avoid overflow weirdness
int maxValue = 16777215;
if(text.length() > Integer.toString(maxValue).length()) {
return false;
}
int parsedValue = Integer.parseInt(text);
return parsedValue >= 0 && parsedValue <= maxValue;
}
}
10 changes: 10 additions & 0 deletions src/main/resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,16 @@
<bundleName>messages.Asm6502Bundle</bundleName>
<categoryKey>INTN.category.asm6502</categoryKey>
</intentionAction>
<intentionAction>
<className>org.ca65.action.ConvertNumberToDecimalIntentionAction</className>
<bundleName>messages.Asm6502Bundle</bundleName>
<categoryKey>INTN.category.asm6502</categoryKey>
</intentionAction>
<intentionAction>
<className>org.ca65.action.ConvertNumberToBinaryIntentionAction</className>
<bundleName>messages.Asm6502Bundle</bundleName>
<categoryKey>INTN.category.asm6502</categoryKey>
</intentionAction>
</extensions>

<actions>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
lda #%00101010
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
lda #42
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<html>
<body>
Replaces a numeric literal (decimal or hexadecimal) with an equivalent binary value.

This intention is available for decimal or hexadecimal literals of up to 24 bits. The result will be represented as an 8, 16, or 24 digit binary number, padded with '0' if necessary.
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
sta $5fff
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
sta 24575
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<html>
<body>
Replaces a numeric literal (hexadecimal or binary) with an equivalent decimal value.

This intention is available for hexadecimal or binary literals of up to 24 bits.
</body>
</html>
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@
<body>
Replaces a numeric literal (decimal or binary) with an equivalent hexadecimal value.

This intention is available for decimal or binary literals of up to 24 bits. The result will be represented as a two, four, or six digit hexadecimal number, prefixed with '0' if necessary.
This intention is available for decimal or binary literals of up to 24 bits. The result will be represented as a 2, 4, or 6 digit hexadecimal number, padded with '0' if necessary.
</body>
</html>
6 changes: 6 additions & 0 deletions src/main/resources/messages/Asm6502Bundle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,9 @@ INTN.category.asm6502=6502 assembly

INTN.NAME.convert.to.hex=Convert to hexadecimal
INTN.convert.to.hex=Convert ''{0}'' to hexadecimal

INTN.NAME.convert.to.dec=Convert to decimal
INTN.convert.to.dec=Convert ''{0}'' to decimal

INTN.NAME.convert.to.bin=Convert to binary
INTN.convert.to.bin=Convert ''{0}'' to binary

0 comments on commit c682953

Please sign in to comment.