Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(78)

Side by Side Diff: runtime/observatory/lib/src/elements/script_inset.dart

Issue 959043003: Build script views programmatically. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | runtime/observatory/lib/src/elements/script_inset.html » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 library script_inset_element; 5 library script_inset_element;
6 6
7 import 'dart:async';
7 import 'dart:html'; 8 import 'dart:html';
8 import 'observatory_element.dart'; 9 import 'observatory_element.dart';
9 import 'package:observatory/service.dart'; 10 import 'package:observatory/service.dart';
10 import 'package:polymer/polymer.dart'; 11 import 'package:polymer/polymer.dart';
11 12
13 const nbsp = "\u00A0";
14
15 class Annotation {
16 int line;
17 int columnStart;
18 int columnStop;
19 String title;
20
21 void applyStyleTo(element) {
22 element.style.color = "blue";
23 element.style.textDecoration = "underline";
24 element.title = title;
25 }
26 }
27
12 /// Box with script source code in it. 28 /// Box with script source code in it.
13 @CustomTag('script-inset') 29 @CustomTag('script-inset')
14 class ScriptInsetElement extends ObservatoryElement { 30 class ScriptInsetElement extends ObservatoryElement {
15 @published Script script; 31 @published Script script;
16 32
17 /// Set the height to make the script inset scroll. Otherwise it 33 /// Set the height to make the script inset scroll. Otherwise it
18 /// will show from startPos to endPos. 34 /// will show from startPos to endPos.
19 @published String height = null; 35 @published String height = null;
20 36
21 @published int currentPos; 37 @published int currentPos;
22 @published int startPos; 38 @published int startPos;
23 @published int endPos; 39 @published int endPos;
24 40
25 @observable int currentLine; 41 @observable int currentLine;
26 @observable int currentCol; 42 @observable int currentCol;
27 @observable int startLine; 43 @observable int startLine;
28 @observable int endLine; 44 @observable int endLine;
29 @observable bool linesReady = false;
30 45
31 // Contents are either ScriptLine or ScriptElipsis. 46 var annotations = [];
32 @observable List lines = toObservable([]); 47 var annotationsCursor;
48
49 StreamSubscription scriptChangeSubscription;
33 50
34 String makeLineId(int line) { 51 String makeLineId(int line) {
35 return 'line-$line'; 52 return 'line-$line';
36 } 53 }
37 54
38 String clip(String line, int start, [int limit]) {
39 try {
40 return line.substring(start, limit);
41 } catch (_) {
42 // NOTE(turnidge): Sometimes polymer updates give us garbage
43 // starts and limits during page updates.
44 return "OOB";
45 }
46 }
47
48 MutationObserver _observer;
49
50 void _scrollToCurrentPos() { 55 void _scrollToCurrentPos() {
51 var line = shadowRoot.querySelector('#line-$currentLine'); 56 var line = querySelector('#${makeLineId(currentLine)}');
52 if (line != null) { 57 if (line != null) {
53 line.scrollIntoView(); 58 line.scrollIntoView();
54 } 59 }
55 } 60 }
56 61
57 void _onMutation(mutations, observer) {
58 _scrollToCurrentPos();
59 }
60
61 void attached() {
62 super.attached();
63 var table = shadowRoot.querySelector('.sourceTable');
64 if (table != null) {
65 _observer = new MutationObserver(_onMutation);
66 _observer.observe(table, childList:true);
67 }
68 }
69
70 void detached() { 62 void detached() {
71 if (_observer != null) { 63 if (scriptChangeSubscription != null) {
72 _observer.disconnect(); 64 // Don't leak. If only Dart and Javascript exposed weak references...
73 _observer = null; 65 scriptChangeSubscription.cancel();
66 scriptChangeSubscription = null;
74 } 67 }
75 super.detached(); 68 super.detached();
76 } 69 }
77 70
78 void currentPosChanged(oldValue) { 71 void currentPosChanged(oldValue) {
79 _updateLines(); 72 update();
80 _scrollToCurrentPos(); 73 _scrollToCurrentPos();
81 } 74 }
82 75
83 void startPosChanged(oldValue) { 76 void startPosChanged(oldValue) {
84 _updateLines(); 77 update();
85 } 78 }
86 79
87 void endPosChanged(oldValue) { 80 void endPosChanged(oldValue) {
88 _updateLines(); 81 update();
89 } 82 }
90 83
91 void scriptChanged(oldValue) { 84 void scriptChanged(oldValue) {
92 _updateLines(); 85 update();
93 } 86 }
94 87
95 var _updateFuture; 88 Element a(String text) => new AnchorElement()..text = text;
89 Element span(String text) => new SpanElement()..text = text;
96 90
97 void _updateLines() { 91 Element hitsUnknown(Element element) {
98 linesReady = false; 92 element.classes.add('hitsNone');
99 if (_updateFuture != null) { 93 element.title = "";
100 // Already scheduled. 94 return element;
101 return; 95 }
102 } 96 Element hitsNotExecuted(Element element) {
97 element.classes.add('hitsNotExecuted');
98 element.title = "Line did not execute";
99 return element;
100 }
101 Element hitsExecuted(Element element) {
102 element.classes.add('hitsExecuted');
103 element.title = "Line did execute";
104 return element;
105 }
106
107 Element container;
108
109 void update() {
103 if (script == null) { 110 if (script == null) {
104 // Wait for script to be assigned. 111 // We may have previously had a script.
112 if (container != null) {
113 container.children.clear();
114 }
105 return; 115 return;
106 } 116 }
107 if (!script.loaded) { 117 if (!script.loaded) {
108 _updateFuture = script.load().then((_) { 118 script.load().then((_) => update());
109 if (script.loaded) {
110 _updateFuture = null;
111 _updateLines();
112 }
113 });
114 return; 119 return;
115 } 120 }
121
122 if (scriptChangeSubscription == null) {
123 scriptChangeSubscription = script.changes.listen((_) => update());
124 }
125
126 computeAnnotations();
127
128 var table = linesTable();
129 if (container == null) {
130 // Indirect to avoid deleting the style element.
131 container = new DivElement();
132 shadowRoot.append(container);
133 }
134 container.children.clear();
135 container.children.add(table);
136 }
137
138 void computeAnnotations() {
116 startLine = (startPos != null 139 startLine = (startPos != null
117 ? script.tokenToLine(startPos) 140 ? script.tokenToLine(startPos)
118 : 1); 141 : 1);
119 currentLine = (currentPos != null 142 currentLine = (currentPos != null
120 ? script.tokenToLine(currentPos) 143 ? script.tokenToLine(currentPos)
121 : null); 144 : null);
122 currentCol = (currentPos != null 145 currentCol = (currentPos != null
123 ? (script.tokenToCol(currentPos) - 1) // make this 0-based. 146 ? (script.tokenToCol(currentPos) - 1) // make this 0-based.
124 : null); 147 : null);
125 endLine = (endPos != null 148 endLine = (endPos != null
126 ? script.tokenToLine(endPos) 149 ? script.tokenToLine(endPos)
127 : script.lines.length); 150 : script.lines.length);
128 151
129 lines.clear(); 152 annotations.clear();
153 if (currentLine != null) {
154 var a = new Annotation();
155 a.line = currentLine;
156 a.columnStart = currentCol;
157 a.columnStop = currentCol + 1;
158 a.title = "Point of interest";
159 annotations.add(a);
160 }
161
162 // TODO(rmacnak): Call site data.
163 }
164
165 Element linesTable() {
166 var table = new DivElement();
167 table.classes.add("sourceTable");
168
169 annotationsCursor = 0;
170
130 int blankLineCount = 0; 171 int blankLineCount = 0;
131 for (int i = (startLine - 1); i <= (endLine - 1); i++) { 172 for (int i = (startLine - 1); i <= (endLine - 1); i++) {
132 if (script.lines[i].isBlank) { 173 if (script.lines[i].isBlank) {
133 // Try to introduce elipses if there are 4 or more contiguous blank line s. 174 // Try to introduce elipses if there are 4 or more contiguous
175 // blank lines.
134 blankLineCount++; 176 blankLineCount++;
135 } else { 177 } else {
136 if (blankLineCount > 0) { 178 if (blankLineCount > 0) {
137 int firstBlank = i - blankLineCount; 179 int firstBlank = i - blankLineCount;
138 int lastBlank = i - 1; 180 int lastBlank = i - 1;
139 if (blankLineCount < 4) { 181 if (blankLineCount < 4) {
140 // Too few blank lines for an elipsis. 182 // Too few blank lines for an elipsis.
141 for (int j = firstBlank; j <= lastBlank; j++) { 183 for (int j = firstBlank; j <= lastBlank; j++) {
142 lines.add(script.lines[j]); 184 table.append(lineElement(script.lines[j]));
143 } 185 }
144 } else { 186 } else {
145 // Add an elipsis for the skipped region. 187 // Add an elipsis for the skipped region.
146 lines.add(script.lines[firstBlank]); 188 table.append(lineElement(script.lines[firstBlank]));
147 lines.add(null); 189 table.append(lineElement(null));
148 lines.add(script.lines[lastBlank]); 190 table.append(lineElement(script.lines[lastBlank]));
149 } 191 }
150 blankLineCount = 0; 192 blankLineCount = 0;
151 } 193 }
152 lines.add(script.lines[i]); 194 table.append(lineElement(script.lines[i]));
153 } 195 }
154 } 196 }
155 linesReady = true; 197
198 return table;
199 }
200
201 // Assumes annotations are sorted.
202 Annotation nextAnnotationOnLine(int line) {
203 if (annotationsCursor >= annotations.length) return null;
204 var annotation = annotations[annotationsCursor];
205 if (annotation.line != line) return null;
206 annotationsCursor++;
207 return annotation;
208 }
209
210 Element lineElement(ScriptLine line) {
211 var e = new DivElement();
212 e.classes.add("sourceRow");
213 e.append(lineBreakpointElement(line));
214 e.append(lineNumberElement(line));
215 e.append(lineSourceElement(line));
216 return e;
217 }
218
219 Element lineBreakpointElement(ScriptLine line) {
220 BreakpointToggleElement e = new Element.tag("breakpoint-toggle");
221 e.line = line;
222 return e;
223 }
224
225 Element lineNumberElement(ScriptLine line) {
226 var lineNumber = line == null ? "..." : line.line;
227 var e = span("$nbsp$lineNumber$nbsp");
228
229 if ((line == null) || (line.hits == null)) {
230 hitsUnknown(e);
231 } else if (line.hits == 0) {
232 hitsNotExecuted(e);
233 } else {
234 hitsExecuted(e);
235 }
236
237 return e;
238 }
239
240 Element lineSourceElement(ScriptLine line) {
241 var e = new DivElement();
242 e.classes.add("sourceItem");
243
244 if (line != null) {
245 if (line.line == currentLine) {
246 e.classes.add("sourceItemCurrent");
247 }
248
249 e.id = makeLineId(line.line);
250
251 var position = 0;
252 consumeUntil(var stop) {
253 if (stop <= position) return; // Empty gap between annotations/boundrie s.
Cutch 2015/03/02 19:11:44 column limit
254 var chunk = line.text.substring(position, stop);
255 var chunkNode = span(chunk);
256 e.append(chunkNode);
257 position = stop;
258 return chunkNode;
259 }
260
261 // TODO(rmacnak): Tolerate overlapping annotations.
262 var annotation;
263 while ((annotation = nextAnnotationOnLine(line.line)) != null) {
264 consumeUntil(annotation.columnStart);
265 annotation.applyStyleTo(consumeUntil(annotation.columnStop));
266 }
267 consumeUntil(line.text.length);
268 }
269
270 return e;
156 } 271 }
157 272
158 ScriptInsetElement.created() : super.created(); 273 ScriptInsetElement.created() : super.created();
159 } 274 }
160 275
161 @CustomTag('breakpoint-toggle') 276 @CustomTag('breakpoint-toggle')
162 class BreakpointToggleElement extends ObservatoryElement { 277 class BreakpointToggleElement extends ObservatoryElement {
163 @published ScriptLine line; 278 @published ScriptLine line;
164 @observable bool busy = false; 279 @observable bool busy = false;
165 280
(...skipping 10 matching lines...) Expand all
176 } else { 291 } else {
177 // Existing breakpoint. Remove it. 292 // Existing breakpoint. Remove it.
178 line.script.isolate.removeBreakpoint(line.bpt).then((_) { 293 line.script.isolate.removeBreakpoint(line.bpt).then((_) {
179 busy = false; 294 busy = false;
180 }); 295 });
181 } 296 }
182 } 297 }
183 298
184 BreakpointToggleElement.created() : super.created(); 299 BreakpointToggleElement.created() : super.created();
185 } 300 }
OLDNEW
« no previous file with comments | « no previous file | runtime/observatory/lib/src/elements/script_inset.html » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698