Skip to content

Commit

Permalink
Add 'copy path' to most UI elements representing workspace items
Browse files Browse the repository at this point in the history
  • Loading branch information
Col-E committed Aug 13, 2023
1 parent 64626c8 commit 5256863
Show file tree
Hide file tree
Showing 14 changed files with 278 additions and 75 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@
import jakarta.annotation.Nullable;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.tree.AnnotationNode;
import software.coley.recaf.RecafConstants;
import software.coley.recaf.info.member.FieldMember;
import software.coley.recaf.info.member.MethodMember;

/**
* Simple visitor for inserting an annotation.
Expand All @@ -27,6 +30,29 @@ public FieldAnnotationInsertingVisitor(@Nullable FieldVisitor fv,
this.inserted = inserted;
}

/**
* @param cv
* Visitor of a class.
* @param inserted
* Annotation to insert on a field.
* @param field
* Field to target, or {@code null} for any field.
*
* @return Visitor that adds a field annotation in the requested circumstances.
*/
@Nonnull
public static ClassVisitor forClass(@Nonnull ClassVisitor cv, @Nonnull AnnotationNode inserted, @Nullable FieldMember field) {
return new ClassVisitor(RecafConstants.getAsmVersion(), cv) {
@Override
public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {
FieldVisitor fv = super.visitField(access, name, descriptor, signature, value);
if (field == null || (field.getName().equals(name) && field.getDescriptor().equals(descriptor)))
return new FieldAnnotationInsertingVisitor(fv, inserted);
return fv;
}
};
}

@Override
public void visitEnd() {
visitAnnotation(inserted.desc, true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@
import jakarta.annotation.Nonnull;
import jakarta.annotation.Nullable;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.TypePath;
import software.coley.recaf.RecafConstants;
import software.coley.recaf.info.member.FieldMember;

/**
* Simple visitor for inserting a annotation.
* Simple visitor for removing an annotation.
*
* @author Matt Coley
*/
Expand All @@ -27,6 +29,29 @@ public FieldAnnotationRemovingVisitor(@Nullable FieldVisitor fv,
this.annotationType = annotationType;
}

/**
* @param cv
* Visitor of a class.
* @param annotationType
* Annotation type to remove on a field.
* @param field
* Field to target, or {@code null} for any field.
*
* @return Visitor that removes field annotations in the requested circumstances.
*/
@Nonnull
public static ClassVisitor forClass(@Nonnull ClassVisitor cv, @Nonnull String annotationType, @Nullable FieldMember field) {
return new ClassVisitor(RecafConstants.getAsmVersion(), cv) {
@Override
public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {
FieldVisitor fv = super.visitField(access, name, descriptor, signature, value);
if (field == null || (field.getName().equals(name) && field.getDescriptor().equals(descriptor)))
return new FieldAnnotationRemovingVisitor(fv, annotationType);
return fv;
}
};
}

@Override
public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
if (annotationType.equals(descriptor.substring(1, descriptor.length() - 1)))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@

import jakarta.annotation.Nonnull;
import jakarta.annotation.Nullable;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.tree.AnnotationNode;
import software.coley.recaf.RecafConstants;
import software.coley.recaf.info.member.MethodMember;

/**
* Simple visitor for inserting an annotation.
Expand All @@ -27,6 +29,29 @@ public MethodAnnotationInsertingVisitor(@Nullable MethodVisitor mv,
this.inserted = inserted;
}

/**
* @param cv
* Visitor of a class.
* @param inserted
* Annotation to insert on a method.
* @param method
* Method to target, or {@code null} for any method.
*
* @return Visitor that adds a method annotation in the requested circumstances.
*/
@Nonnull
public static ClassVisitor forClass(@Nonnull ClassVisitor cv, @Nonnull AnnotationNode inserted, @Nullable MethodMember method) {
return new ClassVisitor(RecafConstants.getAsmVersion(), cv) {
@Override
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
if (method == null || (method.getName().equals(name) && method.getDescriptor().equals(descriptor)))
return new MethodAnnotationInsertingVisitor(mv, inserted);
return mv;
}
};
}

@Override
public void visitEnd() {
visitAnnotation(inserted.desc, true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,13 @@

import jakarta.annotation.Nonnull;
import jakarta.annotation.Nullable;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.TypePath;
import org.objectweb.asm.*;
import software.coley.recaf.RecafConstants;
import software.coley.recaf.info.member.FieldMember;
import software.coley.recaf.info.member.MethodMember;

/**
* Simple visitor for inserting a annotation.
* Simple visitor for removing an annotation.
*
* @author Matt Coley
*/
Expand All @@ -28,6 +27,29 @@ public MethodAnnotationRemovingVisitor(@Nullable MethodVisitor mv,
this.annotationType = annotationType;
}

/**
* @param cv
* Visitor of a class.
* @param annotationType
* Annotation type to remove on a method.
* @param method
* Method to target, or {@code null} for any method.
*
* @return Visitor that removes method annotations in the requested circumstances.
*/
@Nonnull
public static ClassVisitor forClass(@Nonnull ClassVisitor cv, @Nonnull String annotationType, @Nullable MethodMember method) {
return new ClassVisitor(RecafConstants.getAsmVersion(), cv) {
@Override
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
if (method == null || (method.getName().equals(name) && method.getDescriptor().equals(descriptor)))
return new MethodAnnotationRemovingVisitor(mv, annotationType);
return mv;
}
};
}

@Override
public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
if (annotationType.equals(descriptor.substring(1, descriptor.length() - 1)))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@
import software.coley.recaf.services.cell.TextProviderService;
import software.coley.recaf.services.navigation.Actions;

/**
* Common base to context menu provider factories.
*
* @author Matt Coley
*/
public abstract class AbstractContextMenuProviderFactory implements ContextMenuProviderFactory {
protected final TextProviderService textService;
protected final IconProviderService iconService;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,37 @@
import jakarta.annotation.Nonnull;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import javafx.collections.ObservableList;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.MenuItem;
import org.kordamp.ikonli.carbonicons.CarbonIcons;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Type;
import org.slf4j.Logger;
import software.coley.recaf.analytics.logging.Logging;
import software.coley.recaf.info.ClassInfo;
import software.coley.recaf.info.JvmClassInfo;
import software.coley.recaf.info.annotation.Annotated;
import software.coley.recaf.info.annotation.AnnotationInfo;
import software.coley.recaf.info.builder.JvmClassInfoBuilder;
import software.coley.recaf.info.member.ClassMember;
import software.coley.recaf.info.member.FieldMember;
import software.coley.recaf.info.member.MethodMember;
import software.coley.recaf.path.ClassPathNode;
import software.coley.recaf.services.cell.*;
import software.coley.recaf.services.navigation.Actions;
import software.coley.recaf.util.visitors.ClassAnnotationRemovingVisitor;
import software.coley.recaf.util.visitors.FieldAnnotationRemovingVisitor;
import software.coley.recaf.util.visitors.MethodAnnotationRemovingVisitor;
import software.coley.recaf.workspace.model.Workspace;
import software.coley.recaf.workspace.model.bundle.ClassBundle;
import software.coley.recaf.workspace.model.resource.WorkspaceResource;

import static software.coley.recaf.util.Menus.action;
import static software.coley.recaf.util.Unchecked.cast;
import static software.coley.recaf.util.Unchecked.runnable;

/**
* Basic implementation for {@link AnnotationContextMenuProviderFactory}.
*
Expand All @@ -21,6 +42,8 @@
@ApplicationScoped
public class BasicAnnotationContextMenuProviderFactory extends AbstractContextMenuProviderFactory
implements AnnotationContextMenuProviderFactory {
private static final Logger logger = Logging.get(BasicAnnotationContextMenuProviderFactory.class);

@Inject
public BasicAnnotationContextMenuProviderFactory(@Nonnull TextProviderService textService,
@Nonnull IconProviderService iconService,
Expand All @@ -41,9 +64,38 @@ public ContextMenuProvider getAnnotationContextMenuProvider(@Nonnull ContextSour
IconProvider iconProvider = iconService.getAnnotationIconProvider(workspace, resource, bundle, annotated, annotation);
ContextMenu menu = new ContextMenu();
addHeader(menu, nameProvider.makeText(), iconProvider.makeIcon());
// TODO: Actions
// - Goto annotation type declaration
// - Goto annotated class/member

String annotationType = Type.getType(annotation.getDescriptor()).getInternalName();

ObservableList<MenuItem> items = menu.getItems();
ClassPathNode annotationDecPath = workspace.findClass(annotationType);
if (annotationDecPath != null)
items.add(action("menu.goto.class", CarbonIcons.ARROW_RIGHT, runnable(() -> actions.gotoDeclaration(annotationDecPath))));

items.add(action("menu.edit.remove.annotation", CarbonIcons.TRASH_CAN, () -> {
try {
if (annotated instanceof JvmClassInfo target) {
ClassWriter writer = new ClassWriter(0);
target.getClassReader().accept(new ClassAnnotationRemovingVisitor(writer, annotationType), 0);
JvmClassInfo updatedClass = new JvmClassInfoBuilder(new ClassReader(writer.toByteArray())).build();
bundle.put(cast(updatedClass));
} else if (annotated instanceof ClassMember member && member.getDeclaringClass() instanceof JvmClassInfo target) {
ClassWriter writer = new ClassWriter(0);
if (member.isField()) {
FieldMember field = (FieldMember) member;
target.getClassReader().accept(FieldAnnotationRemovingVisitor.forClass(writer, annotationType, field), 0);
} else {
MethodMember method = (MethodMember) member;
target.getClassReader().accept(MethodAnnotationRemovingVisitor.forClass(writer, annotationType, method), 0);
}
JvmClassInfo updatedClass = new JvmClassInfoBuilder(new ClassReader(writer.toByteArray())).build();
bundle.put(cast(updatedClass));
}
} catch (Throwable t) {
logger.error("Failed removing annotation", t);
}
}));

return menu;
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import software.coley.recaf.services.cell.*;
import software.coley.recaf.services.navigation.Actions;
import software.coley.recaf.ui.control.ActionMenuItem;
import software.coley.recaf.util.ClipboardUtil;
import software.coley.recaf.util.Menus;
import software.coley.recaf.workspace.model.Workspace;
import software.coley.recaf.workspace.model.bundle.AndroidClassBundle;
Expand Down Expand Up @@ -136,6 +137,7 @@ private void populateJvmMenu(@Nonnull ContextMenu menu,
items.add(action("menu.goto.class", CarbonIcons.ARROW_RIGHT,
() -> actions.gotoDeclaration(workspace, resource, bundle, info)));
} else if (source.isDeclaration()) {
items.add(action("menu.tab.copypath", CarbonIcons.COPY_LINK, () -> ClipboardUtil.copyString(info)));
ActionMenuItem copy = action("menu.edit.copy", CarbonIcons.COPY_FILE, () -> actions.copyClass(workspace, resource, bundle, info));
ActionMenuItem delete = action("menu.edit.delete", CarbonIcons.DELETE, () -> actions.deleteClass(workspace, resource, bundle, info));
Menu edit = Menus.menu("menu.edit", CarbonIcons.EDIT);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,19 @@
import jakarta.annotation.Nonnull;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import javafx.collections.ObservableList;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.MenuItem;
import org.kordamp.ikonli.carbonicons.CarbonIcons;
import software.coley.recaf.services.cell.*;
import software.coley.recaf.services.navigation.Actions;
import software.coley.recaf.util.ClipboardUtil;
import software.coley.recaf.workspace.model.Workspace;
import software.coley.recaf.workspace.model.bundle.FileBundle;
import software.coley.recaf.workspace.model.resource.WorkspaceResource;

import static software.coley.recaf.util.Menus.action;

/**
* Basic implementation for {@link DirectoryContextMenuProviderFactory}.
*
Expand Down Expand Up @@ -38,13 +44,18 @@ public ContextMenuProvider getDirectoryContextMenuProvider(@Nonnull ContextSourc
IconProvider iconProvider = iconService.getDirectoryIconProvider(workspace, resource, bundle, directoryName);
ContextMenu menu = new ContextMenu();
addHeader(menu, nameProvider.makeText(), iconProvider.makeIcon());
// TODO: implement operations
// - Copy
// - Delete
// - Refactor
// - Rename
// - Move
// - Search references
ObservableList<MenuItem> items = menu.getItems();
if (source.isDeclaration()) {
items.add(action("menu.tab.copypath", CarbonIcons.COPY_LINK, () -> ClipboardUtil.copyString(directoryName)));

// TODO: implement operations
// - Copy
// - Delete
// - Refactor
// - Rename
// - Move
// - Search references
}
return menu;
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
import software.coley.recaf.services.cell.*;
import software.coley.recaf.services.navigation.Actions;
import software.coley.recaf.ui.control.ActionMenuItem;
import software.coley.recaf.util.Menus;
import software.coley.recaf.util.ClipboardUtil;
import software.coley.recaf.util.Lang;
import software.coley.recaf.workspace.model.Workspace;
import software.coley.recaf.workspace.model.bundle.ClassBundle;
import software.coley.recaf.workspace.model.resource.WorkspaceResource;
Expand Down Expand Up @@ -69,6 +70,8 @@ public ContextMenuProvider getFieldContextMenuProvider(@Nonnull ContextSource so
}
}));
} else {
items.add(action("menu.tab.copypath", CarbonIcons.COPY_LINK, () -> ClipboardUtil.copyString(declaringClass, field)));

// TODO: implement operations
// - Edit
// - (field / method assembler)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import software.coley.recaf.path.PathNodes;
import software.coley.recaf.services.cell.*;
import software.coley.recaf.services.navigation.Actions;
import software.coley.recaf.util.ClipboardUtil;
import software.coley.recaf.workspace.model.Workspace;
import software.coley.recaf.workspace.model.bundle.FileBundle;
import software.coley.recaf.workspace.model.resource.WorkspaceResource;
Expand Down Expand Up @@ -49,16 +50,19 @@ public ContextMenuProvider getFileInfoContextMenuProvider(@Nonnull ContextSource
ObservableList<MenuItem> items = menu.getItems();

FilePathNode filePath = PathNodes.filePath(workspace, resource, bundle, info);
items.add(action("menu.goto.file", CarbonIcons.ARROW_RIGHT, runnable(() -> actions.gotoDeclaration(filePath))));

// TODO: implement operations
// - Copy
// - Delete
// - Refactor
// - Rename
// - Move
// - Search references
// - Override text-view language
if (source.isReference()) {
items.add(action("menu.goto.file", CarbonIcons.ARROW_RIGHT, runnable(() -> actions.gotoDeclaration(filePath))));
} else if (source.isDeclaration()) {
items.add(action("menu.tab.copypath", CarbonIcons.COPY_LINK, () -> ClipboardUtil.copyString(info)));
// TODO: implement operations
// - Copy
// - Delete
// - Refactor
// - Rename
// - Move
// - Search references
// - Override text-view language (FileTypeAssociationService)
}
return menu;
};
}
Expand Down
Loading

0 comments on commit 5256863

Please sign in to comment.