23
23
24
24
package org .graalvm .visualizer .source .impl .ui ;
25
25
26
+ import java .util .ArrayList ;
27
+ import java .util .Collections ;
28
+ import java .util .List ;
26
29
import org .graalvm .visualizer .source .Location ;
27
30
import org .graalvm .visualizer .source .ui .Trackable ;
28
31
import org .netbeans .api .actions .Openable ;
34
37
import org .openide .text .Line ;
35
38
import org .openide .util .NbBundle ;
36
39
37
- import javax .swing .SwingUtilities ;
38
40
import javax .swing .text .Document ;
39
41
import javax .swing .text .JTextComponent ;
42
+ import javax .swing .text .StyledDocument ;
43
+ import org .openide .text .Annotatable ;
44
+ import org .openide .text .Annotation ;
45
+ import org .openide .text .NbDocument ;
40
46
import org .openide .util .Mutex ;
47
+ import org .openide .util .Task ;
48
+ import org .openide .util .TaskListener ;
41
49
42
50
/**
43
51
*
44
52
*/
45
- public class LocationOpener implements Openable , Trackable {
53
+ public final class LocationOpener implements Openable , Trackable {
46
54
private final Location location ;
47
55
48
56
public LocationOpener (Location location ) {
@@ -58,33 +66,48 @@ public void open() {
58
66
}
59
67
60
68
private void openOrView (boolean focus ) {
61
- FileObject toOpen ;
62
- toOpen = location .getOriginFile ();
69
+ FileObject toOpen = location .getOriginFile ();
63
70
if (toOpen == null ) {
64
71
return ;
65
72
}
66
- int line = location .getLine ();
67
73
EditorCookie cake = toOpen .getLookup ().lookup (EditorCookie .class );
68
-
69
- Line l = findLine (cake , line );
70
- if (l == null ) {
71
- cake .open ();
72
- StatusDisplayer .getDefault ().setStatusText (Bundle .ERR_LineNotFound ());
74
+ if (cake == null ) {
73
75
return ;
74
76
}
75
- Mutex .EVENT .readAccess (() -> {
76
- l .show (Line .ShowOpenType .REUSE , focus ? Line .ShowVisibilityType .FRONT : Line .ShowVisibilityType .FRONT );
77
- });
78
- }
79
77
80
- private Line findLine (EditorCookie cake , int line ) {
81
- try {
82
- return cake .getLineSet ().getOriginal (line - 1 );
83
- } catch (IndexOutOfBoundsException ex ) {
84
- // expected, the source has changed
85
- // just open the file
86
- return null ;
78
+ Task task = cake .prepareDocument ();
79
+ class WhenShowing implements TaskListener , Runnable {
80
+ @ Override
81
+ public void taskFinished (Task task ) {
82
+ task .removeTaskListener (this );
83
+ Mutex .EVENT .postReadRequest (this );
84
+ }
85
+
86
+ @ Override
87
+ public void run () {
88
+ final StyledDocument doc = cake .getDocument ();
89
+ if (doc == null ) {
90
+ return ;
91
+ }
92
+ List <Annotatable > select = findLinesOrParts (cake .getLineSet (), doc , location .getLine () - 1 , location .getOffsetsOrNull ());
93
+ if (select .size () == 1 && select .get (0 ) instanceof Line ) {
94
+ Line line = (Line ) select .get (0 );
95
+ line .show (Line .ShowOpenType .REUSE , focus ? Line .ShowVisibilityType .FRONT : Line .ShowVisibilityType .FRONT );
96
+ CurrentNodeAnnotation .highlight (select );
97
+ } else if (select .size () >= 1 && select .get (0 ) instanceof Line .Part ) {
98
+ Line .Part part = (Line .Part ) select .get (0 );
99
+ part .getLine ().show (Line .ShowOpenType .REUSE , focus ? Line .ShowVisibilityType .FRONT : Line .ShowVisibilityType .FRONT );
100
+ CurrentNodeAnnotation .highlight (select );
101
+ } else {
102
+ // neither line nor offsets
103
+ cake .open ();
104
+ StatusDisplayer .getDefault ().setStatusText (Bundle .ERR_LineNotFound ());
105
+ }
106
+ }
107
+
87
108
}
109
+ WhenShowing select = new WhenShowing ();
110
+ task .addTaskListener (select );
88
111
}
89
112
90
113
@ Override
@@ -107,4 +130,103 @@ public void viewIfOpened() {
107
130
public void view () {
108
131
openOrView (false );
109
132
}
133
+
134
+ /**
135
+ * Find {@link Line} or {@link Line.Part} to select.
136
+ *
137
+ * @param lines lines of the document to search in
138
+ * @param doc document to search in
139
+ * @param lineNumber line number (counting from zero} or value less then zero when there is no line info
140
+ * @param offsetsOrNull {@code int[] { startOffset, endOffset }} or {@code null}
141
+ * @return found {@link Line} or {@link Line.Part} or {@code null}
142
+ */
143
+ static List <Annotatable > findLinesOrParts (Line .Set lines , StyledDocument doc , int lineNumber , int [] offsetsOrNull ) {
144
+ assert doc != null ;
145
+
146
+ Line exactLine = findLine (lines , lineNumber );
147
+ Line startLine = null ;
148
+ Line endLine = null ;
149
+ if (offsetsOrNull != null ) {
150
+ int startOffsetLineNumber = NbDocument .findLineNumber (doc , offsetsOrNull [0 ]);
151
+ startLine = findLine (lines , startOffsetLineNumber );
152
+ int endOffsetLineNumber = NbDocument .findLineNumber (doc , offsetsOrNull [1 ]);
153
+ endLine = findLine (lines , endOffsetLineNumber );
154
+ }
155
+
156
+ if (startLine == null || (exactLine != null && startLine != exactLine )) {
157
+ // prefer exact line
158
+ return Collections .singletonList (exactLine );
159
+ } else {
160
+ // use offset line
161
+ int startLineOffset = NbDocument .findLineOffset (doc , startLine .getLineNumber ());
162
+ int startColumn = offsetsOrNull [0 ] - startLineOffset ;
163
+ if (startLine == endLine || endLine == null ) {
164
+ int len = offsetsOrNull [1 ] - offsetsOrNull [0 ];
165
+ Line .Part linePart = startLine .createPart (startColumn , len );
166
+ return Collections .singletonList (linePart );
167
+ } else {
168
+ var multiple = new ArrayList <Annotatable >();
169
+ Line .Part firstLinePart = startLine .createPart (startColumn , startLine .getText ().length () - startColumn );
170
+ multiple .add (firstLinePart );
171
+ for (var between = startLine .getLineNumber () + 1 ; between < endLine .getLineNumber (); between ++) {
172
+ var lineBetween = findLine (lines , between );
173
+ if (lineBetween != null ) {
174
+ multiple .add (lineBetween );
175
+ }
176
+ }
177
+ int endLineOffset = NbDocument .findLineOffset (doc , endLine .getLineNumber ());
178
+ int endColumn = offsetsOrNull [1 ] - endLineOffset ;
179
+ Line .Part lastLinePart = endLine .createPart (0 , endColumn );
180
+ multiple .add (lastLinePart );
181
+ return multiple ;
182
+ }
183
+ }
184
+ }
185
+
186
+ private static Line findLine (Line .Set lines , int line ) {
187
+ try {
188
+ return lines .getOriginal (line );
189
+ } catch (IndexOutOfBoundsException ex ) {
190
+ // expected, the source has changed
191
+ // just open the file
192
+ return null ;
193
+ }
194
+ }
195
+
196
+ @ NbBundle .Messages ({
197
+ "CTL_CurrentNode=Current node"
198
+ })
199
+ private static final class CurrentNodeAnnotation extends Annotation {
200
+ private static List <CurrentNodeAnnotation > previous = Collections .emptyList ();
201
+
202
+ private CurrentNodeAnnotation () {
203
+ }
204
+
205
+ private static void highlight (List <Annotatable > select ) {
206
+ List <CurrentNodeAnnotation > newOnes = new ArrayList <>();
207
+ for (var l : select ) {
208
+ var a = new CurrentNodeAnnotation ();
209
+ newOnes .add (a );
210
+ a .attach (l );
211
+ }
212
+ List <CurrentNodeAnnotation > toClear ;
213
+ synchronized (CurrentNodeAnnotation .class ) {
214
+ toClear = previous ;
215
+ previous = newOnes ;
216
+ }
217
+ for (var a : toClear ) {
218
+ a .detach ();
219
+ }
220
+ }
221
+
222
+ @ Override
223
+ public String getAnnotationType () {
224
+ return "NodePositionCurrent" ;
225
+ }
226
+
227
+ @ Override
228
+ public String getShortDescription () {
229
+ return Bundle .CTL_CurrentNode ();
230
+ }
231
+ }
110
232
}
0 commit comments