Skip to content

Commit 8a8c2b6

Browse files
committed
Allow highlighting instructions in ClassFileEditor
1 parent 8adc178 commit 8a8c2b6

3 files changed

Lines changed: 183 additions & 2 deletions

File tree

org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/AutomatedSuite.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2000, 2025 IBM Corporation and others.
2+
* Copyright (c) 2000, 2026 IBM Corporation and others.
33
*
44
* This program and the accompanying materials
55
* are made available under the terms of the Eclipse Public License 2.0
@@ -34,6 +34,7 @@
3434
import org.eclipse.jdt.ui.tests.core.CoreTestSuite;
3535
import org.eclipse.jdt.ui.tests.core.CoreTests;
3636
import org.eclipse.jdt.ui.tests.dialogs.FilteredTypesSelectionDialogTests;
37+
import org.eclipse.jdt.ui.tests.editor.ClassFileEditorTests;
3738
import org.eclipse.jdt.ui.tests.editor.ClassFileInputTests;
3839
import org.eclipse.jdt.ui.tests.hover.JavadocHoverTests;
3940
import org.eclipse.jdt.ui.tests.hover.MarkdownCommentTests;
@@ -91,6 +92,7 @@
9192
MarkdownCommentTests.class,
9293
SmokeViewsTest.class,
9394
ClassFileInputTests.class,
95+
ClassFileEditorTests.class,
9496
FilteredTypesSelectionDialogTests.class
9597
})
9698
public class AutomatedSuite {
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2026, Daniel Schmid and others.
3+
*
4+
* This program and the accompanying materials
5+
* are made available under the terms of the Eclipse Public License 2.0
6+
* which accompanies this distribution, and is available at
7+
* https://www.eclipse.org/legal/epl-2.0/
8+
*
9+
* SPDX-License-Identifier: EPL-2.0
10+
*
11+
* Contributors:
12+
* Daniel Schmid - initial API and implementation
13+
*******************************************************************************/
14+
package org.eclipse.jdt.ui.tests.editor;
15+
16+
import static org.junit.jupiter.api.Assertions.assertEquals;
17+
import static org.junit.jupiter.api.Assertions.assertFalse;
18+
19+
import org.junit.jupiter.api.AfterEach;
20+
import org.junit.jupiter.api.BeforeEach;
21+
import org.junit.jupiter.api.Test;
22+
23+
import org.eclipse.jdt.testplugin.JavaProjectHelper;
24+
25+
import org.eclipse.swt.custom.StyleRange;
26+
import org.eclipse.swt.custom.StyledText;
27+
import org.eclipse.swt.custom.StyledTextContent;
28+
import org.eclipse.swt.graphics.Color;
29+
30+
import org.eclipse.core.runtime.CoreException;
31+
import org.eclipse.core.runtime.NullProgressMonitor;
32+
33+
import org.eclipse.core.resources.IncrementalProjectBuilder;
34+
35+
import org.eclipse.jdt.core.IJavaProject;
36+
import org.eclipse.jdt.core.IPackageFragment;
37+
38+
import org.eclipse.jdt.internal.ui.javaeditor.ClassFileEditor;
39+
import org.eclipse.jdt.internal.ui.javaeditor.EditorUtility;
40+
41+
public class ClassFileEditorTests {
42+
private static final String TYPE_NAME= "HelloWorld";
43+
44+
private String fileName;
45+
private String className;
46+
private IJavaProject javaProject;
47+
48+
@BeforeEach
49+
void setUp() throws Exception {
50+
fileName= TYPE_NAME + ".java";
51+
className= TYPE_NAME + ".class";
52+
javaProject = setUpProject();
53+
}
54+
55+
@AfterEach
56+
void tearDown() throws Exception {
57+
if (javaProject != null) {
58+
JavaProjectHelper.delete(javaProject);
59+
}
60+
}
61+
62+
private IJavaProject setUpProject() throws Exception {
63+
javaProject= JavaProjectHelper.createJavaProject(ClassFileInputTests.class.getSimpleName(), "bin");
64+
JavaProjectHelper.addSourceContainer(javaProject, "src");
65+
IPackageFragment fragment= javaProject.findPackageFragment(javaProject.getProject().getFullPath().append("src"));
66+
String content= """
67+
public class HelloWorld {
68+
void main() {
69+
IO.println("Hello World");
70+
}
71+
}
72+
""";
73+
fragment.createCompilationUnit(fileName, content, true, new NullProgressMonitor());
74+
javaProject.getProject().build(IncrementalProjectBuilder.FULL_BUILD, new NullProgressMonitor());
75+
return javaProject;
76+
}
77+
78+
@Test
79+
void testHighlightRange() throws CoreException {
80+
javaProject.getProject().getFile("src/" + fileName).delete(true,new NullProgressMonitor());
81+
ClassFileEditor classEditor= (ClassFileEditor) EditorUtility.openInEditor(javaProject.getProject().getFile("bin/" + className));
82+
assertFalse(classEditor.isEditable());
83+
classEditor.highlightInstruction("main", "()V", 3, "doesnotexist");
84+
StyledText noSourceTextWidget= classEditor.getNoSourceTextWidget();
85+
StyledTextContent content= noSourceTextWidget.getContent();
86+
StyleRange[] ranges= noSourceTextWidget.getStyleRanges();
87+
assertEquals(1, ranges.length);
88+
StyleRange range= ranges[0];
89+
String highlighted= content.getTextRange(range.start, range.length);
90+
assertEquals(" 3 dup", highlighted);
91+
assertEquals(new Color(0,255,0), range.background);
92+
}
93+
}

org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/javaeditor/ClassFileEditor.java

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2000, 2016 IBM Corporation and others.
2+
* Copyright (c) 2000, 2026 IBM Corporation and others.
33
*
44
* This program and the accompanying materials
55
* are made available under the terms of the Eclipse Public License 2.0
@@ -17,10 +17,13 @@
1717
import java.lang.reflect.InvocationTargetException;
1818
import java.util.ArrayList;
1919
import java.util.List;
20+
import java.util.regex.Pattern;
2021

2122
import org.eclipse.swt.SWT;
2223
import org.eclipse.swt.custom.StackLayout;
24+
import org.eclipse.swt.custom.StyleRange;
2325
import org.eclipse.swt.custom.StyledText;
26+
import org.eclipse.swt.custom.StyledTextContent;
2427
import org.eclipse.swt.events.SelectionEvent;
2528
import org.eclipse.swt.events.SelectionListener;
2629
import org.eclipse.swt.graphics.Color;
@@ -54,6 +57,7 @@
5457
import org.eclipse.jface.util.PropertyChangeEvent;
5558

5659
import org.eclipse.jface.text.IWidgetTokenKeeper;
60+
import org.eclipse.jface.text.Region;
5761
import org.eclipse.jface.text.source.IOverviewRuler;
5862
import org.eclipse.jface.text.source.ISourceViewer;
5963
import org.eclipse.jface.text.source.IVerticalRuler;
@@ -585,6 +589,7 @@ private boolean isEqualInput(IEditorInput input1, IEditorInput input2) {
585589
* @since 3.3
586590
*/
587591
private StyledText fNoSourceTextWidget;
592+
private StyleRange currentSelection;
588593
private volatile boolean disposed;
589594

590595
/**
@@ -964,6 +969,80 @@ private static boolean hasSource(IClassFile file) {
964969
}
965970
}
966971

972+
public void unhighlight() {
973+
if (disposed) {
974+
return;
975+
}
976+
if (currentSelection != null) {
977+
fNoSourceTextWidget.replaceStyleRanges(currentSelection.start, currentSelection.length, new StyleRange[0]);
978+
currentSelection= null;
979+
}
980+
}
981+
982+
public void highlightInstruction(String methodName, String signature, long codeIndex, String colorPreferenceKey) {
983+
unhighlight();
984+
StyledTextContent content= fNoSourceTextWidget.getContent();
985+
986+
int currentLine= findMethodStart(methodName, signature, content);
987+
Region instructionPosition= findInstruction(currentLine, content, codeIndex);
988+
if (instructionPosition == null) {
989+
return;
990+
}
991+
currentSelection= new StyleRange(instructionPosition.getOffset(), instructionPosition.getLength(), fNoSourceTextWidget.getForeground(), getColorPreference(colorPreferenceKey));
992+
fNoSourceTextWidget.setStyleRange(currentSelection);
993+
994+
// move cursor to ensure scrolling
995+
fNoSourceTextWidget.setSelection(instructionPosition.getOffset());
996+
}
997+
998+
private int findMethodStart(String methodName, String signature, StyledTextContent content) {
999+
int currentLine;
1000+
Pattern methodDescriptorPattern = Pattern.compile("\\s+// Method descriptor #\\d+ " + Pattern.quote(signature)); //$NON-NLS-1$
1001+
for(currentLine = 0; currentLine < content.getLineCount() - 2; currentLine++) {
1002+
String line= content.getLine(currentLine);
1003+
if (methodDescriptorPattern.matcher(line).matches()) {
1004+
for(; currentLine < content.getLineCount(); currentLine++) {
1005+
// skip lines with leading slashes
1006+
line = content.getLine(currentLine);
1007+
if (!line.trim().startsWith("//")) { //$NON-NLS-1$
1008+
break;
1009+
}
1010+
}
1011+
if (line.contains(" " + methodName + "(")) {//$NON-NLS-1$//$NON-NLS-2$
1012+
break;
1013+
}
1014+
}
1015+
}
1016+
return currentLine;
1017+
}
1018+
1019+
private Region findInstruction(int currentLine, StyledTextContent content, long codeIndex) {
1020+
for(currentLine++; currentLine < content.getLineCount(); currentLine++) {
1021+
String line= content.getLine(currentLine);
1022+
if (line.trim().startsWith("// Method descriptor")) { //$NON-NLS-1$
1023+
// stop when reaching another method
1024+
return null;
1025+
}
1026+
if (line.trim().startsWith(String.valueOf(codeIndex))) {
1027+
return new Region(content.getOffsetAtLine(currentLine), line.length());
1028+
}
1029+
}
1030+
return null;
1031+
}
1032+
1033+
private Color getColorPreference(String key) {
1034+
String color= getPreferenceStore().getString(key);
1035+
String[] splitColor = color.split(","); //$NON-NLS-1$
1036+
if (splitColor.length != 3) {
1037+
return new Color(0, 255, 0);
1038+
}
1039+
try {
1040+
return new Color(Integer.parseInt(splitColor[0]), Integer.parseInt(splitColor[1]), Integer.parseInt(splitColor[2]));
1041+
} catch (NumberFormatException e) {
1042+
return new Color(0, 255, 0);
1043+
}
1044+
}
1045+
9671046
/*
9681047
* @see ClassFileDocumentProvider.InputChangeListener#inputChanged(IClassFileEditorInput)
9691048
*/
@@ -1032,4 +1111,11 @@ public void setFocus() {
10321111
fSourceAttachmentForm.setFocus();
10331112
}
10341113
}
1114+
1115+
/*
1116+
* Used for testing.
1117+
*/
1118+
public StyledText getNoSourceTextWidget() {
1119+
return fNoSourceTextWidget;
1120+
}
10351121
}

0 commit comments

Comments
 (0)