| Index: lib/src/dependency_graph.dart
 | 
| diff --git a/lib/src/dependency_graph.dart b/lib/src/dependency_graph.dart
 | 
| index dcded66942caf4a90baf757f77e1483f85cf5e30..dcf876d41257013d6f2d8589f5abb24f691d682e 100644
 | 
| --- a/lib/src/dependency_graph.dart
 | 
| +++ b/lib/src/dependency_graph.dart
 | 
| @@ -25,12 +25,10 @@ import 'package:html/dom.dart' show Document, Node;
 | 
|  import 'package:html/parser.dart' as html;
 | 
|  import 'package:logging/logging.dart' show Logger, Level;
 | 
|  import 'package:path/path.dart' as path;
 | 
| -import 'package:source_span/source_span.dart' show SourceSpan;
 | 
|  
 | 
|  import 'info.dart';
 | 
|  import 'options.dart';
 | 
|  import 'report.dart';
 | 
| -import 'utils.dart';
 | 
|  
 | 
|  /// Holds references to all source nodes in the import graph. This is mainly
 | 
|  /// used as a level of indirection to ensure that each source has a canonical
 | 
| @@ -70,11 +68,11 @@ class SourceGraph {
 | 
|        var source = _context.sourceFactory.forUri(uriString);
 | 
|        var extension = path.extension(uriString);
 | 
|        if (extension == '.html') {
 | 
| -        return new HtmlSourceNode(uri, source, this);
 | 
| +        return new HtmlSourceNode(this, uri, source);
 | 
|        } else if (extension == '.dart' || uriString.startsWith('dart:')) {
 | 
| -        return new DartSourceNode(uri, source);
 | 
| +        return new DartSourceNode(this, uri, source);
 | 
|        } else {
 | 
| -        return new ResourceSourceNode(uri, source);
 | 
| +        return new ResourceSourceNode(this, uri, source);
 | 
|        }
 | 
|      });
 | 
|    }
 | 
| @@ -82,6 +80,8 @@ class SourceGraph {
 | 
|  
 | 
|  /// A node in the import graph representing a source file.
 | 
|  abstract class SourceNode {
 | 
| +  final SourceGraph graph;
 | 
| +
 | 
|    /// Resolved URI for this node.
 | 
|    final Uri uri;
 | 
|  
 | 
| @@ -90,6 +90,8 @@ abstract class SourceNode {
 | 
|    Source _source;
 | 
|    Source get source => _source;
 | 
|  
 | 
| +  String get contents => graph._context.getContents(source).data;
 | 
| +
 | 
|    /// Last stamp read from `source.modificationStamp`.
 | 
|    int _lastStamp = 0;
 | 
|  
 | 
| @@ -113,22 +115,27 @@ abstract class SourceNode {
 | 
|    /// parts are excluded from this list.
 | 
|    Iterable<SourceNode> get depsWithoutParts => const [];
 | 
|  
 | 
| -  SourceNode(this.uri, this._source);
 | 
| +  SourceNode(this.graph, this.uri, this._source);
 | 
|  
 | 
|    /// Check for whether the file has changed and, if so, mark [needsRebuild] and
 | 
|    /// [structureChanged] as necessary.
 | 
| -  void update(SourceGraph graph) {
 | 
| +  void update() {
 | 
|      if (_source == null) {
 | 
|        _source = graph._context.sourceFactory.forUri(Uri.encodeFull('$uri'));
 | 
|        if (_source == null) return;
 | 
|      }
 | 
|      int newStamp = _source.modificationStamp;
 | 
|      if (newStamp > _lastStamp) {
 | 
| +      // If the timestamp changed, read the file from disk and cache it.
 | 
| +      // We don't want the source text to change during compilation.
 | 
| +      saveUpdatedContents();
 | 
|        _lastStamp = newStamp;
 | 
|        needsRebuild = true;
 | 
|      }
 | 
|    }
 | 
|  
 | 
| +  void saveUpdatedContents() {}
 | 
| +
 | 
|    String toString() {
 | 
|      var simpleUri = uri.scheme == 'file' ? path.relative(uri.path) : "$uri";
 | 
|      return '[$runtimeType: $simpleUri]';
 | 
| @@ -156,15 +163,16 @@ class HtmlSourceNode extends SourceNode {
 | 
|    /// Parsed document, updated whenever [update] is invoked.
 | 
|    Document document;
 | 
|  
 | 
| -  HtmlSourceNode(Uri uri, Source source, SourceGraph graph)
 | 
| +  HtmlSourceNode(SourceGraph graph, Uri uri, Source source)
 | 
|        : runtimeDeps = graph.runtimeDeps,
 | 
| -        super(uri, source);
 | 
| +        super(graph, uri, source);
 | 
|  
 | 
| -  void update(SourceGraph graph) {
 | 
| -    super.update(graph);
 | 
| +  @override
 | 
| +  void update() {
 | 
| +    super.update();
 | 
|      if (needsRebuild) {
 | 
|        graph._reporter.clearHtml(uri);
 | 
| -      document = html.parse(source.contents.data, generateSpans: true);
 | 
| +      document = html.parse(contents, generateSpans: true);
 | 
|        var newScripts = new Set<DartSourceNode>();
 | 
|        var tags = document.querySelectorAll('script[type="application/dart"]');
 | 
|        for (var script in tags) {
 | 
| @@ -204,7 +212,9 @@ class HtmlSourceNode extends SourceNode {
 | 
|  
 | 
|    void _reportError(SourceGraph graph, String message, Node node) {
 | 
|      graph._reporter.enterHtml(source.uri);
 | 
| -    graph._reporter.log(new DependencyGraphError(message, node.sourceSpan));
 | 
| +    var span = node.sourceSpan;
 | 
| +    graph._reporter.log(
 | 
| +        new Message(message, Level.SEVERE, span.start.offset, span.end.offset));
 | 
|      graph._reporter.leaveHtml();
 | 
|    }
 | 
|  }
 | 
| @@ -223,7 +233,7 @@ class DartSourceNode extends SourceNode {
 | 
|    /// How many times this file is included as a part.
 | 
|    int includedAsPart = 0;
 | 
|  
 | 
| -  DartSourceNode(uri, source) : super(uri, source);
 | 
| +  DartSourceNode(graph, uri, source) : super(graph, uri, source);
 | 
|  
 | 
|    @override
 | 
|    Iterable<SourceNode> get allDeps =>
 | 
| @@ -235,14 +245,25 @@ class DartSourceNode extends SourceNode {
 | 
|  
 | 
|    LibraryInfo info;
 | 
|  
 | 
| -  void update(SourceGraph graph) {
 | 
| -    super.update(graph);
 | 
| +  // TODO(jmesserly): it would be nice to not keep all sources in memory at
 | 
| +  // once, but how else can we ensure a consistent view across a given
 | 
| +  // compile? One different from dev_compiler vs analyzer is that our
 | 
| +  // messages later in the compiler need the original source text to print
 | 
| +  // spans. We also read source text ourselves to parse directives.
 | 
| +  // But we could discard it after that point.
 | 
| +  void saveUpdatedContents() {
 | 
| +    graph._context.setContents(source, source.contents.data);
 | 
| +  }
 | 
|  
 | 
| -    if (needsRebuild && source.contents.data != null) {
 | 
| +  @override
 | 
| +  void update() {
 | 
| +    super.update();
 | 
| +
 | 
| +    if (needsRebuild && contents != null) {
 | 
|        graph._reporter.clearLibrary(uri);
 | 
|        // If the defining compilation-unit changed, the structure might have
 | 
|        // changed.
 | 
| -      var unit = parseDirectives(source.contents.data, name: source.fullName);
 | 
| +      var unit = parseDirectives(contents, name: source.fullName);
 | 
|        var newImports = new Set<DartSourceNode>();
 | 
|        var newExports = new Set<DartSourceNode>();
 | 
|        var newParts = new Set<DartSourceNode>();
 | 
| @@ -262,7 +283,7 @@ class DartSourceNode extends SourceNode {
 | 
|            if (targetUri != target.uri) print(">> ${target.uri} $targetUri");
 | 
|          }
 | 
|          var node = graph.nodes.putIfAbsent(
 | 
| -            targetUri, () => new DartSourceNode(targetUri, target));
 | 
| +            targetUri, () => new DartSourceNode(graph, targetUri, target));
 | 
|          //var node = graph.nodeFromUri(targetUri);
 | 
|          if (node.source == null || !node.source.exists()) {
 | 
|            _reportError(graph, 'File $targetUri not found', unit, d);
 | 
| @@ -314,24 +335,26 @@ class DartSourceNode extends SourceNode {
 | 
|        // contain imports, exports, or parts, we'll ignore them in our crawling.
 | 
|        // However we do a full update to make it easier to adjust when users
 | 
|        // switch a file from a part to a library.
 | 
| -      p.update(graph);
 | 
| +      p.update();
 | 
|        if (p.needsRebuild) needsRebuild = true;
 | 
|      }
 | 
|    }
 | 
|  
 | 
|    void _reportError(
 | 
|        SourceGraph graph, String message, CompilationUnit unit, AstNode node) {
 | 
| -    graph._reporter.enterLibrary(source.uri);
 | 
| -    graph._reporter.log(
 | 
| -        new DependencyGraphError(message, spanForNode(unit, source, node)));
 | 
| -    graph._reporter.leaveLibrary();
 | 
| +    graph._reporter
 | 
| +      ..enterLibrary(source.uri)
 | 
| +      ..enterCompilationUnit(unit, source)
 | 
| +      ..log(new Message(message, Level.SEVERE, node.offset, node.end))
 | 
| +      ..leaveCompilationUnit()
 | 
| +      ..leaveLibrary();
 | 
|    }
 | 
|  }
 | 
|  
 | 
|  /// Represents a runtime resource from our compiler that is needed to run an
 | 
|  /// application.
 | 
|  class ResourceSourceNode extends SourceNode {
 | 
| -  ResourceSourceNode(uri, source) : super(uri, source);
 | 
| +  ResourceSourceNode(graph, uri, source) : super(graph, uri, source);
 | 
|  }
 | 
|  
 | 
|  /// Updates the structure and `needsRebuild` marks in nodes of [graph] reachable
 | 
| @@ -345,8 +368,8 @@ class ResourceSourceNode extends SourceNode {
 | 
|  /// contained local changes. Rebuild decisions that derive from transitive
 | 
|  /// changes (e.g. when the API of a dependency changed) are handled later in
 | 
|  /// [rebuild].
 | 
| -void refreshStructureAndMarks(SourceNode start, SourceGraph graph) {
 | 
| -  visitInPreOrder(start, (n) => n.update(graph), includeParts: false);
 | 
| +void refreshStructureAndMarks(SourceNode start) {
 | 
| +  visitInPreOrder(start, (n) => n.update(), includeParts: false);
 | 
|  }
 | 
|  
 | 
|  /// Clears all the `needsRebuild` and `structureChanged` marks in nodes
 | 
| @@ -382,8 +405,8 @@ void clearMarks(SourceNode start) {
 | 
|  ///     whether other nodes need to be rebuilt. The function [build] is expected
 | 
|  ///     to return `true` on a node `n` if it detemines other nodes that import
 | 
|  ///     `n` may need to be rebuilt as well.
 | 
| -rebuild(SourceNode start, SourceGraph graph, bool build(SourceNode node)) {
 | 
| -  refreshStructureAndMarks(start, graph);
 | 
| +rebuild(SourceNode start, bool build(SourceNode node)) {
 | 
| +  refreshStructureAndMarks(start);
 | 
|    // Hold which source nodes may have changed their public API, this includes
 | 
|    // libraries that were modified or libraries that export other modified APIs.
 | 
|    // TODO(sigmund): consider removing this special support for exports? Many
 | 
| @@ -454,12 +477,6 @@ visitInPostOrder(SourceNode start, void action(SourceNode node),
 | 
|  
 | 
|  bool _same(Set a, Set b) => a.length == b.length && a.containsAll(b);
 | 
|  
 | 
| -/// An error message discovered while parsing the dependencies between files.
 | 
| -class DependencyGraphError extends MessageWithSpan {
 | 
| -  const DependencyGraphError(String message, SourceSpan span)
 | 
| -      : super(message, Level.SEVERE, span);
 | 
| -}
 | 
| -
 | 
|  /// Runtime files added to all applications when running the compiler in the
 | 
|  /// command line.
 | 
|  final defaultRuntimeFiles = () {
 | 
| 
 |