Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
579ab46
add ParameterConnections test input
supersaiyansubtlety Nov 9, 2025
b6e99e6
disable user renaming of enum constants
supersaiyansubtlety Nov 9, 2025
87543ca
extract EnigmaProjectImpl
supersaiyansubtlety Nov 9, 2025
581fc9e
move new classes to jspecify annotations
supersaiyansubtlety Nov 18, 2025
c5e057b
remove z_ prefix from z_parameters_connection test input package
supersaiyansubtlety Dec 12, 2025
fb21a56
disallow manual naming of definite record getters
supersaiyansubtlety Jan 31, 2026
5a36dd4
update TestRecordStats:testMethods
supersaiyansubtlety Jan 31, 2026
317b567
make EntryReference::getNameableEntry return definite getters' fields…
supersaiyansubtlety Feb 1, 2026
0424c3f
start work on ParamSyntheticFieldIndexingVisitor
supersaiyansubtlety Feb 3, 2026
b50e21b
Merge branch 'develop/2.8' into internal-proposers
supersaiyansubtlety Feb 5, 2026
fc784a4
more param synthetic field work, move some functionality to indexing …
supersaiyansubtlety Feb 6, 2026
261fe92
initial (very broken) ParamSyntheticFieldProposalService implementation
supersaiyansubtlety Feb 8, 2026
1385596
minor refactors
supersaiyansubtlety Feb 9, 2026
4071b93
remove some param linking invalidation that isn't relevant to local c…
supersaiyansubtlety Feb 9, 2026
e312a5d
get param synthetic field proposals working... for bytecode only
supersaiyansubtlety Feb 10, 2026
f4e83bb
get *some* param fake local proposals working for vineflower
supersaiyansubtlety Feb 10, 2026
87f2b89
offset fake local indices when there are multiple
supersaiyansubtlety Feb 12, 2026
ddaf22d
fix accidentally checking innerName instead of outerName when finding…
supersaiyansubtlety Feb 12, 2026
aa2b040
offset fake locals by synthetic field index instead of counting acces…
supersaiyansubtlety Feb 12, 2026
49ca2f7
rename new services and visitor
supersaiyansubtlety Feb 12, 2026
68ccbfe
simplify ParamLocalClassLinkingVisitor method collection
supersaiyansubtlety Feb 12, 2026
4d6bc0c
extract EnigmaProject::getNameTarget from EntryReference::getNameable…
supersaiyansubtlety Feb 12, 2026
deca432
rename EntryReference::getNameableEntry -> getNameTarget to match the…
supersaiyansubtlety Feb 12, 2026
08be404
add ParamLocalClassLinksTest with one initial test
supersaiyansubtlety Feb 12, 2026
4183d14
fix limiting localSyntheticFieldsByGetterByOwner to one field per getter
supersaiyansubtlety Feb 14, 2026
1a28c25
use only consider linked synthetic fields when calculating fake local…
supersaiyansubtlety Feb 15, 2026
26630c3
show linked param info in IdentifierPanel for fake locals
supersaiyansubtlety Feb 15, 2026
8d3ed8c
more fake local spaghetti
supersaiyansubtlety Feb 16, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions enigma-swing/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ dependencies {
implementation project(':enigma')
implementation project(':enigma-server')

implementation libs.asm
implementation libs.syntaxpain
implementation libs.jopt
implementation libs.flatlaf
Expand Down
4 changes: 2 additions & 2 deletions enigma-swing/src/main/java/org/quiltmc/enigma/gui/Gui.java
Original file line number Diff line number Diff line change
Expand Up @@ -435,7 +435,7 @@ public void showTokens(AbstractEditorPanel<?> editor, List<Token> tokens) {
}

public void showCursorReference(EntryReference<Entry<?>, Entry<?>> reference) {
this.infoPanel.setReference(reference == null ? null : reference.entry);
this.infoPanel.setReference(reference);
}

public void startDocChange(EditorPanel editor) {
Expand Down Expand Up @@ -506,7 +506,7 @@ public void toggleMapping(EditorPanel editor) {
return;
}

Entry<?> obfEntry = cursorReference.getNameableEntry();
Entry<?> obfEntry = cursorReference.getNameTarget(this.controller.getProject());
this.toggleMappingFromEntry(obfEntry);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -437,7 +437,7 @@ public void navigateTo(Entry<?> entry) {
}

public void navigateTo(EntryReference<Entry<?>, Entry<?>> reference) {
if (!this.project.isNavigable(reference.getNameableEntry())) {
if (!this.project.isNavigable(reference.getNameTarget(this.project))) {
return;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,7 @@ protected Entry<?> resolveReference(EntryReference<Entry<?>, Entry<?>> reference
navigationEntry = reference.entry;
}

return navigationEntry;
return this.gui.getController().getProject().getRepresentative(navigationEntry);
}

protected void setCursorReference(EntryReference<Entry<?>, Entry<?>> ref) {
Expand Down Expand Up @@ -543,7 +543,6 @@ private void showReferenceImpl(EntryReference<Entry<?>, Entry<?>> reference) {
final List<Token> tokens = this.getReferences(reference);

if (tokens.isEmpty()) {
// DEBUG
Logger.debug("No tokens found for {} in {}", reference, this.classHandler.getHandle().getRef());
} else {
this.gui.showTokens(this, tokens);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@
import java.util.function.Function;
import java.util.function.Predicate;

import static org.quiltmc.enigma.gui.util.GuiUtil.getRecordIndexingService;
import static java.util.Comparator.comparingInt;

public class DeclarationSnippetPanel extends AbstractEditorPanel<JScrollPane> {
Expand Down Expand Up @@ -486,24 +485,10 @@ private static Snippet toSnippet(LineIndexer lineIndexer, Position startPos, Pos
}

private Optional<Target> resolveTarget(DecompiledClassSource source, Entry<?> targetEntry) {
return Optional.ofNullable(source.getIndex().getDeclarationToken(targetEntry))
.map(token -> new Target(token, targetEntry))
.or(() -> {
if (targetEntry instanceof MethodEntry targetMethod) {
// try to find record component getter's corresponding field instead
return getRecordIndexingService(this.gui)
.map(service -> service.getComponentField(targetMethod))
.flatMap(componentField -> Optional
.ofNullable(source.getIndex().getDeclarationToken(componentField))
.map(fieldToken -> new Target(fieldToken, componentField))
);
} else {
// This can happen as a result of #252: Issue with lost parameter connection.
// This can also happen when the token is from a library.
final Entry<?> namingTargetEntry = this.gui.getController().getProject().getRepresentative(targetEntry);

return Optional.empty();
}
});
return Optional.ofNullable(source.getIndex().getDeclarationToken(namingTargetEntry))
.map(token -> new Target(token, namingTargetEntry));
}

private record Target(Token token, Entry<?> entry) { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ public void keyTyped(KeyEvent event) {
if (!event.isControlDown() && !event.isAltDown() && Character.isJavaIdentifierStart(event.getKeyChar())) {
EnigmaProject project = gui.getController().getProject();
EntryReference<Entry<?>, Entry<?>> reference = project.getRemapper().deobfuscate(EditorPanel.this.cursorReference);
Entry<?> entry = reference.getNameableEntry();
Entry<?> entry = reference.getNameTarget(EditorPanel.this.gui.getController().getProject());

String name = String.valueOf(event.getKeyChar());
if (entry instanceof ClassEntry classEntry && classEntry.getParent() == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import org.jspecify.annotations.Nullable;
import org.quiltmc.enigma.api.EnigmaProject;
import org.quiltmc.enigma.api.analysis.EntryReference;
import org.quiltmc.enigma.api.analysis.index.jar.EntryIndex;
import org.quiltmc.enigma.api.translation.mapping.EntryChange;
import org.quiltmc.enigma.api.translation.representation.TypeDescriptor;
Expand All @@ -19,6 +20,7 @@
import org.quiltmc.enigma.gui.util.GridBagConstraintsBuilder;
import org.quiltmc.enigma.gui.util.GuiUtil;
import org.quiltmc.enigma.gui.util.ScaleUtil;
import org.quiltmc.enigma.impl.EnigmaProjectImpl;
import org.quiltmc.enigma.util.I18n;
import org.quiltmc.enigma.util.validation.ValidationContext;

Expand All @@ -30,15 +32,17 @@
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.MouseEvent;
import java.util.Objects;

public class IdentifierPanel {
private final Gui gui;

private final JPanel ui = new JPanel();

private Entry<?> lastEntry;
private Entry<?> entry;
private Entry<?> deobfEntry;
@Nullable
private EntryReference<Entry<?>, Entry<?>> lastReference;
@Nullable
private EntryReference<Entry<?>, Entry<?>> reference;

private ConvertingTextField nameField;

Expand All @@ -54,13 +58,15 @@ public IdentifierPanel(Gui gui) {
this.ui.setEnabled(false);
}

public void setReference(Entry<?> entry) {
this.entry = entry;
public void setReference(EntryReference<Entry<?>, Entry<?>> reference) {
this.reference = reference;
this.refreshReference();
}

public boolean startRenaming() {
if (this.nameField == null) return false;
if (this.nameField == null) {
return false;
}

this.nameField.startEditing();

Expand All @@ -78,15 +84,15 @@ public boolean startRenaming(String text) {

public void refreshReference() {
final EnigmaProject project = this.gui.getController().getProject();
this.deobfEntry = this.entry == null ? null : project.getRemapper().deobfuscate(this.entry);
final Entry<?> deobfEntry = this.reference == null ? null : project.getRemapper().deobfuscate(this.reference.entry);

// Prevent IdentifierPanel from being rebuilt if you didn't click off.
if (this.lastEntry == this.entry && this.nameField != null) {
if (this.lastReference == this.reference && this.nameField != null) {
if (!this.nameField.hasChanges()) {
final String name;

// Find what to set the name to.
if (this.deobfEntry instanceof MethodEntry methodEntry && methodEntry.isConstructor()) {
if (deobfEntry instanceof MethodEntry methodEntry && methodEntry.isConstructor()) {
// Get the parent of the method if it is a constructor.
final ClassEntry parent = methodEntry.getParent();

Expand All @@ -95,10 +101,10 @@ public void refreshReference() {
}

name = parent.isInnerClass() ? parent.getName() : parent.getFullName();
} else if (this.deobfEntry instanceof ClassEntry classEntry && !classEntry.isInnerClass()) {
} else if (deobfEntry instanceof ClassEntry classEntry && !classEntry.isInnerClass()) {
name = classEntry.getFullName();
} else {
name = this.deobfEntry.getName();
name = deobfEntry.getName();
}

this.nameField.setReferenceText(name);
Expand All @@ -107,31 +113,31 @@ public void refreshReference() {
return;
}

this.lastEntry = this.entry;
this.lastReference = this.reference;

this.nameField = null;

TableHelper th = new TableHelper(this.ui, this.entry, this.gui);
TableHelper th = new TableHelper(this.ui, this.reference, this.gui);
th.begin();
if (this.entry == null) {
if (this.reference == null) {
this.ui.setEnabled(false);
} else {
this.ui.setEnabled(true);

if (this.deobfEntry instanceof ClassEntry ce) {
if (deobfEntry instanceof ClassEntry ce) {
String name = ce.isInnerClass() ? ce.getName() : ce.getFullName();
this.nameField = th.addRenameTextField(EditableType.CLASS, name);
th.addCopiableStringRow(I18n.translate("info_panel.identifier.obfuscated"), this.entry.getName());
th.addCopiableStringRow(I18n.translate("info_panel.identifier.obfuscated"), this.reference.entry.getName());

if (ce.getParent() != null) {
th.addCopiableStringRow(I18n.translate("info_panel.identifier.outer_class"), ce.getParent().getFullName());
}
} else if (this.deobfEntry instanceof FieldEntry fe) {
} else if (deobfEntry instanceof FieldEntry fe) {
this.nameField = th.addRenameTextField(EditableType.FIELD, fe.getName());
th.addStringRow(I18n.translate("info_panel.identifier.class"), fe.getParent().getFullName());
th.addCopiableStringRow(I18n.translate("info_panel.identifier.obfuscated"), this.entry.getName());
th.addCopiableStringRow(I18n.translate("info_panel.identifier.obfuscated"), this.reference.entry.getName());
th.addCopiableStringRow(I18n.translate("info_panel.identifier.type"), toReadableType(fe.getDesc()));
} else if (this.deobfEntry instanceof MethodEntry me) {
} else if (deobfEntry instanceof MethodEntry me) {
if (me.isConstructor()) {
ClassEntry ce = me.getParent();
if (ce != null) {
Expand All @@ -143,37 +149,44 @@ public void refreshReference() {
th.addStringRow(I18n.translate("info_panel.identifier.class"), me.getParent().getFullName());
}

th.addCopiableStringRow(I18n.translate("info_panel.identifier.obfuscated"), this.entry.getName());
th.addCopiableStringRow(I18n.translate("info_panel.identifier.obfuscated"), this.reference.entry.getName());
th.addCopiableStringRow(I18n.translate("info_panel.identifier.method_descriptor"), me.getDesc().toString());
} else if (this.deobfEntry instanceof LocalVariableEntry local) {
EditableType type;

if (local.isArgument()) {
} else if (this.reference.entry instanceof LocalVariableEntry obfLocal) {
// DEBUG
// final LocalVariableEntry effectiveObfLocal = ((EnigmaProjectImpl) this.gui.getController().getProject())
// .getParamLocalClassLinkIndexingService()
// .map(service -> service.getLinkedParam(obfLocal))
// .orElse(obfLocal);
final LocalVariableEntry effectiveObfLocal = obfLocal;

final LocalVariableEntry effectiveDeobfLocal = project.getRemapper().deobfuscate(effectiveObfLocal);

final EditableType type;
if (effectiveObfLocal.isArgument()) {
type = EditableType.PARAMETER;
} else {
type = EditableType.LOCAL_VARIABLE;
}

this.nameField = th.addRenameTextField(type, local.getName());
th.addStringRow(I18n.translate("info_panel.identifier.class"), local.getContainingClass().getFullName());
th.addCopiableStringRow(I18n.translate("info_panel.identifier.method"), local.getParent().getName());
th.addStringRow(I18n.translate("info_panel.identifier.index"), Integer.toString(local.getIndex()));
this.nameField = th.addRenameTextField(type, effectiveDeobfLocal.getName());
th.addStringRow(I18n.translate("info_panel.identifier.class"), effectiveDeobfLocal.getContainingClass().getFullName());
th.addCopiableStringRow(I18n.translate("info_panel.identifier.method"), effectiveDeobfLocal.getParent().getName());
th.addStringRow(I18n.translate("info_panel.identifier.index"), Integer.toString(effectiveDeobfLocal.getIndex()));

// type
EntryIndex index = project.getJarIndex().getIndex(EntryIndex.class);
// EntryIndex only contains obf entries, so use the obf entry to look up the local's descriptor
@Nullable
final LocalVariableDefEntry obfLocal = index.getDefinition((LocalVariableEntry) this.entry);
final String localDesc = obfLocal == null
final LocalVariableDefEntry definition = index.getDefinition(effectiveObfLocal);
final String localDesc = definition == null
? I18n.translate("info_panel.identifier.type.unknown")
: toReadableType(project.getRemapper().deobfuscate(obfLocal.getDesc()));
: toReadableType(project.getRemapper().deobfuscate(definition.getDesc()));

th.addCopiableStringRow(I18n.translate("info_panel.identifier.type"), localDesc);
} else {
throw new IllegalStateException("unreachable");
throw new IllegalStateException("Unrecognized entry type: " + deobfEntry);
}

var mapping = project.getRemapper().getMapping(this.entry);
var mapping = project.getRemapper().getMapping(this.reference.entry);
if (Config.main().development.showMappingSourcePlugin.value() && mapping.tokenType().isProposed()) {
th.addStringRow(I18n.translate("dev.source_plugin"), mapping.sourcePluginId());
}
Expand Down Expand Up @@ -245,12 +258,12 @@ private void doRename(String newName) {
}

private EntryChange<? extends Entry<?>> getRename(String newName) {
Entry<?> entry = this.entry;
if (entry instanceof MethodEntry method && method.isConstructor()) {
entry = method.getContainingClass();
}

return EntryChange.modify(entry).withDeobfName(newName);
return EntryChange
.modify(Objects
.requireNonNull(this.reference, "Cannot rename without a reference!")
.getNameTarget(this.gui.getController().getProject())
)
.withDeobfName(newName);
}

public void retranslateUi() {
Expand All @@ -264,13 +277,13 @@ public JPanel getUi() {

private static final class TableHelper {
private final Container c;
private final Entry<?> e;
private final EntryReference<Entry<?>, Entry<?>> r;
private final Gui gui;
private int row;

TableHelper(Container c, Entry<?> e, Gui gui) {
TableHelper(Container c, EntryReference<Entry<?>, Entry<?>> r, Gui gui) {
this.c = c;
this.e = e;
this.r = r;
this.gui = gui;
}

Expand Down Expand Up @@ -314,7 +327,7 @@ public ConvertingTextField addRenameTextField(EditableType type, String c2) {
default -> throw new IllegalStateException("Unexpected value: " + type);
};

if (this.gui.getController().getProject().isRenamable(this.e)) {
if (this.gui.getController().getProject().isRenamable(this.r)) {
ConvertingTextField field = this.addConvertingTextField(description, c2);
field.setEditable(this.gui.isEditable(type));
return field;
Expand Down
2 changes: 1 addition & 1 deletion enigma/src/main/java/org/quiltmc/enigma/api/Enigma.java
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ public EnigmaProject openJar(Path path, ClassProvider libraryClassProvider, Prog
MappingsIndex mappingsIndex = MappingsIndex.empty();
mappingsIndex.indexMappings(proposedNames, progress);

return new EnigmaProject(this, path, mainProjectProvider, jarIndex, libIndex, comboIndex, mappingsIndex, proposedNames, Utils.zipSha1(path));
return EnigmaProject.of(this, path, mainProjectProvider, jarIndex, libIndex, comboIndex, mappingsIndex, proposedNames, Utils.zipSha1(path));
}

private void index(
Expand Down
Loading