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

Unified 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: column limit Created 5 years, 10 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | runtime/observatory/lib/src/elements/script_inset.html » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: runtime/observatory/lib/src/elements/script_inset.dart
diff --git a/runtime/observatory/lib/src/elements/script_inset.dart b/runtime/observatory/lib/src/elements/script_inset.dart
index aef3fe3dde57d321ee2f8a107482a897802cf8a6..124697464f8cf859ccc140afdeadbdadb1f896b6 100644
--- a/runtime/observatory/lib/src/elements/script_inset.dart
+++ b/runtime/observatory/lib/src/elements/script_inset.dart
@@ -4,11 +4,27 @@
library script_inset_element;
+import 'dart:async';
import 'dart:html';
import 'observatory_element.dart';
import 'package:observatory/service.dart';
import 'package:polymer/polymer.dart';
+const nbsp = "\u00A0";
+
+class Annotation {
+ int line;
+ int columnStart;
+ int columnStop;
+ String title;
+
+ void applyStyleTo(element) {
+ element.style.color = "blue";
+ element.style.textDecoration = "underline";
+ element.title = title;
+ }
+}
+
/// Box with script source code in it.
@CustomTag('script-inset')
class ScriptInsetElement extends ObservatoryElement {
@@ -26,93 +42,100 @@ class ScriptInsetElement extends ObservatoryElement {
@observable int currentCol;
@observable int startLine;
@observable int endLine;
- @observable bool linesReady = false;
- // Contents are either ScriptLine or ScriptElipsis.
- @observable List lines = toObservable([]);
+ var annotations = [];
+ var annotationsCursor;
+
+ StreamSubscription scriptChangeSubscription;
String makeLineId(int line) {
return 'line-$line';
}
- String clip(String line, int start, [int limit]) {
- try {
- return line.substring(start, limit);
- } catch (_) {
- // NOTE(turnidge): Sometimes polymer updates give us garbage
- // starts and limits during page updates.
- return "OOB";
- }
- }
-
- MutationObserver _observer;
-
void _scrollToCurrentPos() {
- var line = shadowRoot.querySelector('#line-$currentLine');
+ var line = querySelector('#${makeLineId(currentLine)}');
if (line != null) {
line.scrollIntoView();
}
}
- void _onMutation(mutations, observer) {
- _scrollToCurrentPos();
- }
-
- void attached() {
- super.attached();
- var table = shadowRoot.querySelector('.sourceTable');
- if (table != null) {
- _observer = new MutationObserver(_onMutation);
- _observer.observe(table, childList:true);
- }
- }
-
void detached() {
- if (_observer != null) {
- _observer.disconnect();
- _observer = null;
+ if (scriptChangeSubscription != null) {
+ // Don't leak. If only Dart and Javascript exposed weak references...
+ scriptChangeSubscription.cancel();
+ scriptChangeSubscription = null;
}
super.detached();
}
void currentPosChanged(oldValue) {
- _updateLines();
+ update();
_scrollToCurrentPos();
}
void startPosChanged(oldValue) {
- _updateLines();
+ update();
}
void endPosChanged(oldValue) {
- _updateLines();
+ update();
}
void scriptChanged(oldValue) {
- _updateLines();
+ update();
}
- var _updateFuture;
+ Element a(String text) => new AnchorElement()..text = text;
+ Element span(String text) => new SpanElement()..text = text;
- void _updateLines() {
- linesReady = false;
- if (_updateFuture != null) {
- // Already scheduled.
- return;
- }
+ Element hitsUnknown(Element element) {
+ element.classes.add('hitsNone');
+ element.title = "";
+ return element;
+ }
+ Element hitsNotExecuted(Element element) {
+ element.classes.add('hitsNotExecuted');
+ element.title = "Line did not execute";
+ return element;
+ }
+ Element hitsExecuted(Element element) {
+ element.classes.add('hitsExecuted');
+ element.title = "Line did execute";
+ return element;
+ }
+
+ Element container;
+
+ void update() {
if (script == null) {
- // Wait for script to be assigned.
+ // We may have previously had a script.
+ if (container != null) {
+ container.children.clear();
+ }
return;
}
if (!script.loaded) {
- _updateFuture = script.load().then((_) {
- if (script.loaded) {
- _updateFuture = null;
- _updateLines();
- }
- });
+ script.load().then((_) => update());
return;
}
+
+ if (scriptChangeSubscription == null) {
+ scriptChangeSubscription = script.changes.listen((_) => update());
+ }
+
+ computeAnnotations();
+
+ var table = linesTable();
+ if (container == null) {
+ // Indirect to avoid deleting the style element.
+ container = new DivElement();
+ shadowRoot.append(container);
+ }
+ container.children.clear();
+ container.children.add(table);
+ }
+
+ void computeAnnotations() {
startLine = (startPos != null
? script.tokenToLine(startPos)
: 1);
@@ -126,11 +149,30 @@ class ScriptInsetElement extends ObservatoryElement {
? script.tokenToLine(endPos)
: script.lines.length);
- lines.clear();
+ annotations.clear();
+ if (currentLine != null) {
+ var a = new Annotation();
+ a.line = currentLine;
+ a.columnStart = currentCol;
+ a.columnStop = currentCol + 1;
+ a.title = "Point of interest";
+ annotations.add(a);
+ }
+
+ // TODO(rmacnak): Call site data.
+ }
+
+ Element linesTable() {
+ var table = new DivElement();
+ table.classes.add("sourceTable");
+
+ annotationsCursor = 0;
+
int blankLineCount = 0;
for (int i = (startLine - 1); i <= (endLine - 1); i++) {
if (script.lines[i].isBlank) {
- // Try to introduce elipses if there are 4 or more contiguous blank lines.
+ // Try to introduce elipses if there are 4 or more contiguous
+ // blank lines.
blankLineCount++;
} else {
if (blankLineCount > 0) {
@@ -139,20 +181,95 @@ class ScriptInsetElement extends ObservatoryElement {
if (blankLineCount < 4) {
// Too few blank lines for an elipsis.
for (int j = firstBlank; j <= lastBlank; j++) {
- lines.add(script.lines[j]);
+ table.append(lineElement(script.lines[j]));
}
} else {
// Add an elipsis for the skipped region.
- lines.add(script.lines[firstBlank]);
- lines.add(null);
- lines.add(script.lines[lastBlank]);
+ table.append(lineElement(script.lines[firstBlank]));
+ table.append(lineElement(null));
+ table.append(lineElement(script.lines[lastBlank]));
}
blankLineCount = 0;
}
- lines.add(script.lines[i]);
+ table.append(lineElement(script.lines[i]));
}
}
- linesReady = true;
+
+ return table;
+ }
+
+ // Assumes annotations are sorted.
+ Annotation nextAnnotationOnLine(int line) {
+ if (annotationsCursor >= annotations.length) return null;
+ var annotation = annotations[annotationsCursor];
+ if (annotation.line != line) return null;
+ annotationsCursor++;
+ return annotation;
+ }
+
+ Element lineElement(ScriptLine line) {
+ var e = new DivElement();
+ e.classes.add("sourceRow");
+ e.append(lineBreakpointElement(line));
+ e.append(lineNumberElement(line));
+ e.append(lineSourceElement(line));
+ return e;
+ }
+
+ Element lineBreakpointElement(ScriptLine line) {
+ BreakpointToggleElement e = new Element.tag("breakpoint-toggle");
+ e.line = line;
+ return e;
+ }
+
+ Element lineNumberElement(ScriptLine line) {
+ var lineNumber = line == null ? "..." : line.line;
+ var e = span("$nbsp$lineNumber$nbsp");
+
+ if ((line == null) || (line.hits == null)) {
+ hitsUnknown(e);
+ } else if (line.hits == 0) {
+ hitsNotExecuted(e);
+ } else {
+ hitsExecuted(e);
+ }
+
+ return e;
+ }
+
+ Element lineSourceElement(ScriptLine line) {
+ var e = new DivElement();
+ e.classes.add("sourceItem");
+
+ if (line != null) {
+ if (line.line == currentLine) {
+ e.classes.add("sourceItemCurrent");
+ }
+
+ e.id = makeLineId(line.line);
+
+ var position = 0;
+ consumeUntil(var stop) {
+ if (stop <= position) {
+ return; // Empty gap between annotations/boundries.
+ }
+ var chunk = line.text.substring(position, stop);
+ var chunkNode = span(chunk);
+ e.append(chunkNode);
+ position = stop;
+ return chunkNode;
+ }
+
+ // TODO(rmacnak): Tolerate overlapping annotations.
+ var annotation;
+ while ((annotation = nextAnnotationOnLine(line.line)) != null) {
+ consumeUntil(annotation.columnStart);
+ annotation.applyStyleTo(consumeUntil(annotation.columnStop));
+ }
+ consumeUntil(line.text.length);
+ }
+
+ return e;
}
ScriptInsetElement.created() : super.created();
« 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