|
1 | 1 | /******************************************************************************* |
2 | | - * Copyright (c) 2000, 2016 IBM Corporation and others. |
| 2 | + * Copyright (c) 2000, 2026 IBM Corporation and others. |
3 | 3 | * |
4 | 4 | * This program and the accompanying materials |
5 | 5 | * are made available under the terms of the Eclipse Public License 2.0 |
|
16 | 16 |
|
17 | 17 | import java.lang.reflect.InvocationTargetException; |
18 | 18 | import java.util.ArrayList; |
| 19 | +import java.util.HashMap; |
19 | 20 | import java.util.List; |
| 21 | +import java.util.Map; |
| 22 | +import java.util.Objects; |
| 23 | +import java.util.regex.Matcher; |
| 24 | +import java.util.regex.Pattern; |
20 | 25 |
|
21 | 26 | import org.eclipse.swt.SWT; |
22 | 27 | import org.eclipse.swt.custom.StackLayout; |
| 28 | +import org.eclipse.swt.custom.StyleRange; |
23 | 29 | import org.eclipse.swt.custom.StyledText; |
| 30 | +import org.eclipse.swt.custom.StyledTextContent; |
24 | 31 | import org.eclipse.swt.events.SelectionEvent; |
25 | 32 | import org.eclipse.swt.events.SelectionListener; |
26 | 33 | import org.eclipse.swt.graphics.Color; |
|
54 | 61 | import org.eclipse.jface.util.PropertyChangeEvent; |
55 | 62 |
|
56 | 63 | import org.eclipse.jface.text.IWidgetTokenKeeper; |
| 64 | +import org.eclipse.jface.text.Region; |
57 | 65 | import org.eclipse.jface.text.source.IOverviewRuler; |
58 | 66 | import org.eclipse.jface.text.source.ISourceViewer; |
59 | 67 | import org.eclipse.jface.text.source.IVerticalRuler; |
@@ -557,6 +565,9 @@ private boolean isEqualInput(IEditorInput input1, IEditorInput input2) { |
557 | 565 | } |
558 | 566 | } |
559 | 567 |
|
| 568 | + private record MethodIdentifier(String name, String signature) {} |
| 569 | + private record LineRegion(int startLine, int endLine) {} |
| 570 | + |
560 | 571 | private StackLayout fStackLayout; |
561 | 572 | private Composite fParent; |
562 | 573 |
|
@@ -585,7 +596,9 @@ private boolean isEqualInput(IEditorInput input1, IEditorInput input2) { |
585 | 596 | * @since 3.3 |
586 | 597 | */ |
587 | 598 | private StyledText fNoSourceTextWidget; |
| 599 | + private StyleRange currentSelection; |
588 | 600 | private volatile boolean disposed; |
| 601 | + private Color instructionPointerColor = new Color(0, 255, 0); |
589 | 602 |
|
590 | 603 | /** |
591 | 604 | * Default constructor. |
@@ -736,6 +749,7 @@ protected IEditorInput transformEditorInput(IEditorInput input) throws CoreExcep |
736 | 749 | @Override |
737 | 750 | public void init(IEditorSite site, IEditorInput input) throws PartInitException { |
738 | 751 | JavaCore.runReadOnly(() -> super.init(site, input)); |
| 752 | + initInstructionPointerColor(); |
739 | 753 | } |
740 | 754 | /* |
741 | 755 | * @see AbstractTextEditor#doSetInput(IEditorInput) |
@@ -964,6 +978,104 @@ private static boolean hasSource(IClassFile file) { |
964 | 978 | } |
965 | 979 | } |
966 | 980 |
|
| 981 | + public void unhighlight() { |
| 982 | + if (disposed) { |
| 983 | + return; |
| 984 | + } |
| 985 | + if (currentSelection != null) { |
| 986 | + fNoSourceTextWidget.replaceStyleRanges(currentSelection.start, currentSelection.length, new StyleRange[0]); |
| 987 | + currentSelection= null; |
| 988 | + } |
| 989 | + } |
| 990 | + |
| 991 | + public void highlightInstruction(String methodName, String signature, long codeIndex) { |
| 992 | + if (disposed) { |
| 993 | + return; |
| 994 | + } |
| 995 | + unhighlight(); |
| 996 | + StyledTextContent content= fNoSourceTextWidget.getContent(); |
| 997 | + |
| 998 | + LineRegion methodBounds= getMethodsToInstructionRegionCache().get(new MethodIdentifier(methodName, signature)); |
| 999 | + if (methodBounds == null) { |
| 1000 | + return; |
| 1001 | + } |
| 1002 | + Region instructionPosition= findInstruction(methodBounds, content, codeIndex); |
| 1003 | + if (instructionPosition == null) { |
| 1004 | + return; |
| 1005 | + } |
| 1006 | + currentSelection= new StyleRange(instructionPosition.getOffset(), instructionPosition.getLength(), fNoSourceTextWidget.getForeground(), instructionPointerColor); |
| 1007 | + fNoSourceTextWidget.setStyleRange(currentSelection); |
| 1008 | + |
| 1009 | + // move cursor to ensure scrolling |
| 1010 | + fNoSourceTextWidget.setSelection(instructionPosition.getOffset()); |
| 1011 | + } |
| 1012 | + |
| 1013 | + private Map<MethodIdentifier, LineRegion> methodsToInstructionRegions; |
| 1014 | + private Map<MethodIdentifier, LineRegion> getMethodsToInstructionRegionCache() { |
| 1015 | + return methodsToInstructionRegions = Objects.requireNonNullElseGet(methodsToInstructionRegions, this::createCache); |
| 1016 | + } |
| 1017 | + |
| 1018 | + private Map<MethodIdentifier, LineRegion> createCache() { |
| 1019 | + Pattern methodDescriptorPattern = Pattern.compile("^\\s+// Method descriptor #\\d+ (.+)$"); //$NON-NLS-1$ |
| 1020 | + StyledTextContent content= fNoSourceTextWidget.getContent(); |
| 1021 | + Map<MethodIdentifier, LineRegion> cache = new HashMap<>(); |
| 1022 | + for(int currentLine = 0; currentLine < content.getLineCount(); currentLine++) { |
| 1023 | + String line= content.getLine(currentLine); |
| 1024 | + Matcher descriptorMatcher= methodDescriptorPattern.matcher(line); |
| 1025 | + if (descriptorMatcher.matches()) { |
| 1026 | + String methodSignature = descriptorMatcher.group(1); |
| 1027 | + for(; currentLine < content.getLineCount(); currentLine++) { |
| 1028 | + // skip lines with leading slashes |
| 1029 | + line = content.getLine(currentLine); |
| 1030 | + if (!line.trim().startsWith("//")) { //$NON-NLS-1$ |
| 1031 | + break; |
| 1032 | + } |
| 1033 | + } |
| 1034 | + |
| 1035 | + int methodNameEnd = line.indexOf('('); |
| 1036 | + int methodNameStart = line.lastIndexOf(' ', methodNameEnd); |
| 1037 | + if (methodNameStart != -1) { |
| 1038 | + String methodName= line.substring(methodNameStart + 1, methodNameEnd); |
| 1039 | + int indentation = getIndentation(line); |
| 1040 | + int startLine = ++currentLine; |
| 1041 | + for(; currentLine < content.getLineCount() && getIndentation(content.getLine(currentLine)) > indentation; currentLine++) { |
| 1042 | + // find end of method |
| 1043 | + } |
| 1044 | + int endLine = currentLine; |
| 1045 | + cache.put(new MethodIdentifier(methodName, methodSignature), new LineRegion(startLine, endLine)); |
| 1046 | + } |
| 1047 | + } |
| 1048 | + } |
| 1049 | + return cache; |
| 1050 | + } |
| 1051 | + |
| 1052 | + private int getIndentation(String line) { |
| 1053 | + return line.length() - line.stripLeading().length(); |
| 1054 | + } |
| 1055 | + |
| 1056 | + private Region findInstruction(LineRegion methodBounds, StyledTextContent content, long codeIndex) { |
| 1057 | + for(int currentLine = methodBounds.startLine(); currentLine < methodBounds.endLine(); currentLine++) { |
| 1058 | + String line= content.getLine(currentLine); |
| 1059 | + if (line.trim().startsWith(codeIndex + " ")) { //$NON-NLS-1$ |
| 1060 | + return new Region(content.getOffsetAtLine(currentLine), line.length()); |
| 1061 | + } |
| 1062 | + } |
| 1063 | + return null; |
| 1064 | + } |
| 1065 | + |
| 1066 | + private void initInstructionPointerColor() { |
| 1067 | + String color= getPreferenceStore().getString("currentIPColor"); //$NON-NLS-1$ |
| 1068 | + String[] splitColor = color.split(","); //$NON-NLS-1$ |
| 1069 | + if (splitColor.length != 3) { |
| 1070 | + return; |
| 1071 | + } |
| 1072 | + try { |
| 1073 | + instructionPointerColor = new Color(Integer.parseInt(splitColor[0]), Integer.parseInt(splitColor[1]), Integer.parseInt(splitColor[2])); |
| 1074 | + } catch (NumberFormatException e) { |
| 1075 | + // use default color |
| 1076 | + } |
| 1077 | + } |
| 1078 | + |
967 | 1079 | /* |
968 | 1080 | * @see ClassFileDocumentProvider.InputChangeListener#inputChanged(IClassFileEditorInput) |
969 | 1081 | */ |
@@ -1032,4 +1144,11 @@ public void setFocus() { |
1032 | 1144 | fSourceAttachmentForm.setFocus(); |
1033 | 1145 | } |
1034 | 1146 | } |
| 1147 | + |
| 1148 | + /* |
| 1149 | + * Used for testing. |
| 1150 | + */ |
| 1151 | + public StyledText getNoSourceTextWidget() { |
| 1152 | + return fNoSourceTextWidget; |
| 1153 | + } |
1035 | 1154 | } |
0 commit comments