Remove support for the old Span classes from source_maps.

This releases source_maps 0.10.0, code_transformers 0.2.0+2, observe 0.11.0+3,
and polymer 0.12.0+4.

BUG=19930
R=sigmund@google.com

Review URL: https://codereview.chromium.org//421723004

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/source_maps@38803 260f80e4-7a28-3924-810f-c04153c831b5
diff --git a/lib/builder.dart b/lib/builder.dart
index 494a6b6..091e220 100644
--- a/lib/builder.dart
+++ b/lib/builder.dart
@@ -9,9 +9,10 @@
 
 import 'dart:convert';
 
+import 'package:source_span/source_span.dart';
+
 import 'parser.dart';
-import 'span.dart';
-import 'src/span_wrapper.dart';
+import 'src/source_map_span.dart';
 
 /// Builds a source map given a set of mappings.
 class SourceMapBuilder {
@@ -19,39 +20,33 @@
   final List<Entry> _entries = <Entry>[];
 
   /// Adds an entry mapping the [targetOffset] to [source].
-  ///
-  /// [source] can be either a [Location] or a [SourceLocation]. Using a
-  /// [Location] is deprecated and will be unsupported in version 0.10.0.
-  void addFromOffset(source, targetFile, int targetOffset, String identifier) {
+  void addFromOffset(SourceLocation source, SourceFile targetFile,
+      int targetOffset, String identifier) {
     if (targetFile == null) {
       throw new ArgumentError('targetFile cannot be null');
     }
-    _entries.add(new Entry(source,
-          new FileLocation(targetFile, targetOffset), identifier));
+    _entries.add(
+        new Entry(source, targetFile.location(targetOffset), identifier));
   }
 
   /// Adds an entry mapping [target] to [source].
   ///
-  /// [source] and [target] can be either a [Span] or a [SourceSpan]. Using a
-  /// [Span] is deprecated and will be unsupported in version 0.10.0.
-  ///
-  /// If [isIdentifier] is true, this entry is considered to represent an
-  /// identifier whose value will be stored in the source map.
-  void addSpan(source, target, {bool isIdentifier}) {
-    source = SpanWrapper.wrap(source);
-    target = SpanWrapper.wrap(target);
-    if (isIdentifier == null) isIdentifier = source.isIdentifier;
+  /// If [isIdentifier] is true or if [target] is a [SourceMapSpan] with
+  /// `isIdentifier` set to true, this entry is considered to represent an
+  /// identifier whose value will be stored in the source map. [isIdenfier]
+  /// takes precedence over [target]'s `isIdentifier` value.
+  void addSpan(SourceSpan source, SourceSpan target, {bool isIdentifier}) {
+    if (isIdentifier == null) {
+      isIdentifier = source is SourceMapSpan ? source.isIdentifier : false;
+    }
 
     var name = isIdentifier ? source.text : null;
     _entries.add(new Entry(source.start, target.start, name));
   }
 
   /// Adds an entry mapping [target] to [source].
-  ///
-  /// [source] and [target] can be either a [Location] or a [SourceLocation].
-  /// Using a [Location] is deprecated and will be unsupported in version
-  /// 0.10.0.
-  void addLocation(source, target, String identifier) {
+  void addLocation(SourceLocation source, SourceLocation target,
+      String identifier) {
     _entries.add(new Entry(source, target, identifier));
   }
 
@@ -67,22 +62,16 @@
 /// An entry in the source map builder.
 class Entry implements Comparable {
   /// Span denoting the original location in the input source file
-  final Location source;
+  final SourceLocation source;
 
   /// Span indicating the corresponding location in the target file.
-  final Location target;
+  final SourceLocation target;
 
   /// An identifier name, when this location is the start of an identifier.
   final String identifierName;
 
   /// Creates a new [Entry] mapping [target] to [source].
-  ///
-  /// [source] and [target] can be either a [Location] or a [SourceLocation].
-  /// Using a [Location] is deprecated and will be unsupported in version
-  /// 0.10.0.
-  Entry(source, target, this.identifierName)
-      : source = LocationWrapper.wrap(source),
-        target = LocationWrapper.wrap(target);
+  Entry(this.source, this.target, this.identifierName);
 
   /// Implements [Comparable] to ensure that entries are ordered by their
   /// location in the target file. We sort primarily by the target offset
@@ -91,7 +80,8 @@
   int compareTo(Entry other) {
     int res = target.compareTo(other.target);
     if (res != 0) return res;
-    res = source.sourceUrl.compareTo(other.source.sourceUrl);
+    res = source.sourceUrl.toString().compareTo(
+        other.source.sourceUrl.toString());
     if (res != 0) return res;
     return source.compareTo(other.source);
   }
diff --git a/lib/parser.dart b/lib/parser.dart
index c18a1d6..d67a65e 100644
--- a/lib/parser.dart
+++ b/lib/parser.dart
@@ -8,9 +8,10 @@
 import 'dart:collection';
 import 'dart:convert';
 
+import 'package:source_span/source_span.dart';
+
 import 'builder.dart' as builder;
-import 'span.dart';
-import 'src/span_wrapper.dart';
+import 'src/source_map_span.dart';
 import 'src/utils.dart';
 import 'src/vlq.dart';
 
@@ -49,19 +50,11 @@
 /// A mapping parsed out of a source map.
 abstract class Mapping {
   /// Returns the span associated with [line] and [column].
-  ///
-  /// The values of [files] can be either `source_map` [SourceFile]s or
-  /// `source_span` `SourceFile`s. Using `source_map` [SourceFile]s is
-  /// deprecated and will be unsupported in version 0.10.0.
-  Span spanFor(int line, int column, {Map<String, dynamic> files});
+  SourceMapSpan spanFor(int line, int column, {Map<String, SourceFile> files});
 
   /// Returns the span associated with [location].
-  ///
-  /// The values of [files] may be either `source_map` [SourceFile]s or
-  /// `source_span` `SourceFile`s. Using `source_map` [SourceFile]s is
-  /// deprecated and will be unsupported in version 0.10.0.
-  Span spanForLocation(location, {Map<String, dynamic> files}) {
-    location = LocationWrapper.wrap(location);
+  SourceMapSpan spanForLocation(SourceLocation location,
+      {Map<String, SourceFile> files}) {
     return spanFor(location.line, location.column, files: files);
   }
 }
@@ -124,7 +117,7 @@
     return _lineStart.length - 1;
   }
 
-  Span spanFor(int line, int column, {Map<String, dynamic> files}) {
+  SourceMapSpan spanFor(int line, int column, {Map<String, SourceFile> files}) {
     int index = _indexFor(line, column);
     return _maps[index].spanFor(
         line - _lineStart[index], column - _columnStart[index], files: files);
@@ -191,8 +184,9 @@
       if (sourceEntry.source == null) {
         targetEntries.add(new TargetEntry(sourceEntry.target.column));
       } else {
+        var sourceUrl = sourceEntry.source.sourceUrl;
         var urlId = urls.putIfAbsent(
-            sourceEntry.source.sourceUrl, () => urls.length);
+            sourceUrl == null ? '' : sourceUrl.toString(), () => urls.length);
         var srcNameId = sourceEntry.identifierName == null ? null :
             names.putIfAbsent(sourceEntry.identifierName, () => names.length);
         targetEntries.add(new TargetEntry(
@@ -363,7 +357,7 @@
     return (index <= 0) ? null : entries[index - 1];
   }
 
-  Span spanFor(int line, int column, {Map<String, dynamic> files}) {
+  SourceMapSpan spanFor(int line, int column, {Map<String, SourceFile> files}) {
     var entry = _findColumn(line, column, _findLine(line));
     if (entry == null || entry.sourceUrlId == null) return null;
     var url = urls[entry.sourceUrlId];
@@ -371,21 +365,24 @@
       url = '${sourceRoot}${url}';
     }
     if (files != null && files[url] != null) {
-      var file = SourceFileWrapper.wrap(files[url]);
+      var file = files[url];
       var start = file.getOffset(entry.sourceLine, entry.sourceColumn);
       if (entry.sourceNameId != null) {
         var text = names[entry.sourceNameId];
-        return new FileSpan(files[url], start, start + text.length, true);
+        return new SourceMapFileSpan(
+            files[url].span(start, start + text.length),
+            isIdentifier: true);
       } else {
-        return new FileSpan(files[url], start);
+        return new SourceMapFileSpan(files[url].location(start).pointSpan());
       }
     } else {
+      var start = new SourceLocation(0,
+          sourceUrl: url, line: entry.sourceLine, column: entry.sourceColumn);
       // Offset and other context is not available.
       if (entry.sourceNameId != null) {
-        return new FixedSpan(url, 0, entry.sourceLine, entry.sourceColumn,
-            text: names[entry.sourceNameId], isIdentifier: true);
+        return new SourceMapSpan.identifier(start, names[entry.sourceNameId]);
       } else {
-        return new FixedSpan(url, 0, entry.sourceLine, entry.sourceColumn);
+        return new SourceMapSpan(start, start, '');
       }
     }
   }
diff --git a/lib/printer.dart b/lib/printer.dart
index aa18bd7..906e260 100644
--- a/lib/printer.dart
+++ b/lib/printer.dart
@@ -5,11 +5,10 @@
 /// Contains a code printer that generates code by recording the source maps.
 library source_maps.printer;
 
-import 'package:source_span/source_span.dart' as source_span;
+import 'package:source_span/source_span.dart';
 
 import 'builder.dart';
-import 'span.dart';
-import 'src/span_wrapper.dart';
+import 'src/source_map_span.dart';
 
 const int _LF = 10;
 const int _CR = 13;
@@ -24,7 +23,7 @@
   String get map => _maps.toJson(filename);
 
   /// Current source location mapping.
-  Location _loc;
+  SourceLocation _loc;
 
   /// Current line in the buffer;
   int _line = 0;
@@ -49,11 +48,12 @@
         _line++;
         _column = 0;
         if (projectMarks && _loc != null) {
-          if (_loc is FixedLocation) {
-            mark(new FixedLocation(0, _loc.sourceUrl, _loc.line + 1, 0));
-          } else if (_loc is FileLocation) {
+          if (_loc is FileLocation) {
             var file = (_loc as FileLocation).file;
-            mark(new FileLocation(file, file.getOffset(_loc.line + 1, 0)));
+            mark(file.location(file.getOffset(_loc.line + 1)));
+          } else {
+            mark(new SourceLocation(0,
+                sourceUrl: _loc.sourceUrl, line: _loc.line + 1, column: 0));
           }
         }
       } else {
@@ -72,21 +72,23 @@
   }
 
   /// Marks that the current point in the target file corresponds to the [mark]
-  /// in the source file, which can be either a [Location] or a [Span]. When the
-  /// mark is an identifier's Span, this also records the name of the identifier
-  /// in the source map information.
+  /// in the source file, which can be either a [SourceLocation] or a
+  /// [SourceSpan]. When the mark is a [SourceMapSpan] with `isIdentifier` set,
+  /// this also records the name of the identifier in the source map
+  /// information.
   void mark(mark) {
     var loc;
     var identifier = null;
-    if (mark is Location || mark is source_span.SourceLocation) {
-      loc = LocationWrapper.wrap(mark);
-    } else if (mark is Span || mark is source_span.SourceSpan) {
-      mark = SpanWrapper.wrap(mark);
+    if (mark is SourceLocation) {
+      loc = mark;
+    } else if (mark is SourceSpan) {
       loc = mark.start;
-      if (mark.isIdentifier) identifier = mark.text;
+      if (mark is SourceMapSpan && mark.isIdentifier) identifier = mark.text;
     }
-    _maps.addLocation(loc,
-        new FixedLocation(_buff.length, null, _line, _column), identifier);
+    _maps.addLocation(
+        loc,
+        new SourceLocation(_buff.length, line: _line, column: _column),
+        identifier);
     _loc = loc;
   }
 }
@@ -101,7 +103,8 @@
 class NestedPrinter implements NestedItem {
 
   /// Items recoded by this printer, which can be [String] literals,
-  /// [NestedItem]s, and source map information like [Location] and [Span].
+  /// [NestedItem]s, and source map information like [SourceLocation] and
+  /// [SourceSpan].
   List _items = [];
 
   /// Internal buffer to merge consecutive strings added to this printer.
@@ -128,19 +131,16 @@
   /// location of [object] in the original input. Only one, [location] or
   /// [span], should be provided at a time.
   ///
-  /// [location] can be either a [Location] or a [SourceLocation]. [span] can be
-  /// either a [Span] or a [SourceSpan]. Using a [Location] or a [Span] is
-  /// deprecated and will be unsupported in version 0.10.0.
-  ///
   /// Indicate [isOriginal] when [object] is copied directly from the user code.
   /// Setting [isOriginal] will make this printer propagate source map locations
   /// on every line-break.
-  void add(object, {location, span, bool isOriginal: false}) {
+  void add(object, {SourceLocation location, SourceSpan span,
+      bool isOriginal: false}) {
     if (object is! String || location != null || span != null || isOriginal) {
       _flush();
       assert(location == null || span == null);
-      if (location != null) _items.add(LocationWrapper.wrap(location));
-      if (span != null) _items.add(SpanWrapper.wrap(span));
+      if (location != null) _items.add(location);
+      if (span != null) _items.add(span);
       if (isOriginal) _items.add(_ORIGINAL);
     }
 
@@ -164,16 +164,12 @@
   /// The [location] and [span] parameters indicate the corresponding source map
   /// location of [object] in the original input. Only one, [location] or
   /// [span], should be provided at a time.
-  ///
-  /// [location] can be either a [Location] or a [SourceLocation]. [span] can be
-  /// either a [Span] or a [SourceSpan]. Using a [Location] or a [Span] is
-  /// deprecated and will be unsupported in version 0.10.0.
-  void addLine(String line, {location, span}) {
+  void addLine(String line, {SourceLocation location, SourceSpan span}) {
     if (location != null || span != null) {
       _flush();
       assert(location == null || span == null);
-      if (location != null) _items.add(LocationWrapper.wrap(location));
-      if (span != null) _items.add(SpanWrapper.wrap(span));
+      if (location != null) _items.add(location);
+      if (span != null) _items.add(span);
     }
     if (line == null) return;
     if (line != '') {
@@ -235,7 +231,7 @@
       } else if (item is String) {
         printer.add(item, projectMarks: propagate);
         propagate = false;
-      } else if (item is Location || item is Span) {
+      } else if (item is SourceLocation || item is SourceSpan) {
         printer.mark(item);
       } else if (item == _ORIGINAL) {
         // we insert booleans when we are about to quote text that was copied
diff --git a/lib/refactor.dart b/lib/refactor.dart
index 47ce2ed..a33b86b 100644
--- a/lib/refactor.dart
+++ b/lib/refactor.dart
@@ -8,9 +8,9 @@
 /// [guessIndent] helps to guess the appropriate indentiation for the new code.
 library source_maps.refactor;
 
-import 'span.dart';
+import 'package:source_span/source_span.dart';
+
 import 'printer.dart';
-import 'src/span_wrapper.dart';
 
 /// Editable text transaction.
 ///
@@ -22,12 +22,7 @@
   final _edits = <_TextEdit>[];
 
   /// Creates a new transaction.
-  ///
-  /// [file] can be either a `source_map` [SourceFile] or a `source_span`
-  /// `SourceFile`. Using a `source_map` [SourceFile] is deprecated and will be
-  /// unsupported in version 0.10.0.
-  TextEditTransaction(this.original, file)
-      : file = SourceFileWrapper.wrap(file);
+  TextEditTransaction(this.original, this.file);
 
   bool get hasEdits => _edits.length > 0;
 
@@ -38,8 +33,8 @@
     _edits.add(new _TextEdit(begin, end, replacement));
   }
 
-  /// Create a source map [Location] for [offset].
-  Location _loc(int offset) =>
+  /// Create a source map [SourceLocation] for [offset].
+  SourceLocation _loc(int offset) =>
       file != null ? file.location(offset) : null;
 
   /// Applies all pending [edit]s and returns a [NestedPrinter] containing the
@@ -62,7 +57,7 @@
     for (var edit in _edits) {
       if (consumed > edit.begin) {
         var sb = new StringBuffer();
-        sb..write(file.location(edit.begin).formatString)
+        sb..write(file.location(edit.begin).toolString)
             ..write(': overlapping edits. Insert at offset ')
             ..write(edit.begin)
             ..write(' but have consumed ')
diff --git a/lib/source_maps.dart b/lib/source_maps.dart
index 2d4a4cc..9531903 100644
--- a/lib/source_maps.dart
+++ b/lib/source_maps.dart
@@ -11,7 +11,8 @@
 ///         ..add(inputSpan3, outputSpan3)
 ///         .toJson(outputFile);
 ///
-/// Use the [Span] and [SourceFile] classes to specify span locations.
+/// Use the source_span package's [SourceSpan] and [SourceFile] classes to
+/// specify span locations.
 ///
 /// Parse a source map using [parse], and call `spanFor` on the returned mapping
 /// object. For example:
@@ -40,4 +41,4 @@
 export "parser.dart";
 export "printer.dart";
 export "refactor.dart";
-export "span.dart";
+export 'src/source_map_span.dart';
diff --git a/lib/span.dart b/lib/span.dart
deleted file mode 100644
index a21e893..0000000
--- a/lib/span.dart
+++ /dev/null
@@ -1,404 +0,0 @@
-// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-/// Dart classes representing the souce spans and source files.
-library source_maps.span;
-
-import 'dart:math' show min, max;
-
-import 'package:path/path.dart' as p;
-
-import 'src/utils.dart';
-
-/// A simple class that describe a segment of source text.
-@Deprecated("Use the source_span package instead.")
-abstract class Span implements Comparable {
-  /// The start location of this span.
-  final Location start;
-
-  /// The end location of this span, exclusive.
-  final Location end;
-
-  /// Url of the source (typically a file) containing this span.
-  String get sourceUrl => start.sourceUrl;
-
-  /// The length of this span, in characters.
-  int get length => end.offset - start.offset;
-
-  /// The source text for this span, if available.
-  String get text;
-
-  /// Whether [text] corresponds to an identifier symbol.
-  final bool isIdentifier;
-
-  Span(this.start, this.end, bool isIdentifier)
-      : isIdentifier = isIdentifier != null ? isIdentifier : false {
-    _checkRange();
-  }
-
-  /// Creates a new span that is the union of two existing spans [start] and
-  /// [end]. Note that the resulting span might contain some positions that were
-  /// not in either of the original spans if [start] and [end] are disjoint.
-  Span.union(Span start, Span end)
-      : start = start.start, end = end.end, isIdentifier = false {
-    _checkRange();
-  }
-
-  void _checkRange() {
-    if (start.offset < 0) throw new ArgumentError('start $start must be >= 0');
-    if (end.offset < start.offset) {
-      throw new ArgumentError('end $end must be >= start $start');
-    }
-  }
-
-  /// Compares two spans. If the spans are not in the same source, this method
-  /// generates an error.
-  int compareTo(Span other) {
-    int d = start.compareTo(other.start);
-    return d == 0 ? end.compareTo(other.end) : d;
-  }
-
-  /// Gets the location in standard printed form `filename:line:column`, where
-  /// line and column are adjusted by 1 to match the convention in editors.
-  String get formatLocation => start.formatString;
-
-  String getLocationMessage(String message,
-      {bool useColors: false, String color}) {
-    var source = start.sourceUrl == null ? '' :
-        ' of ${p.prettyUri(start.sourceUrl)}';
-    return 'line ${start.line + 1}, column ${start.column + 1}$source: ' +
-        message;
-  }
-
-  bool operator ==(Span other) =>
-    sourceUrl == other.sourceUrl && start == other.start && end == other.end;
-
-  int get hashCode => sourceUrl.hashCode + start.offset + (31 * length);
-
-  String toString() => '<$runtimeType: $start $end $formatLocation $text>';
-}
-
-/// A location in the source text
-@Deprecated("Use the source_span package instead.")
-abstract class Location implements Comparable {
-  /// Url of the source containing this span.
-  String get sourceUrl;
-
-  /// The offset of this location, 0-based.
-  final int offset;
-
-  /// The 0-based line in the source of this location.
-  int get line;
-
-  /// The 0-based column in the source of this location.
-  int get column;
-
-  Location(this.offset);
-
-  /// Compares two locations. If the locations are not in the same source, this
-  /// method generates an error.
-  int compareTo(Location other) {
-    if (sourceUrl != other.sourceUrl) {
-      throw new ArgumentError('can only compare locations of the same source');
-    }
-    return offset - other.offset;
-  }
-
-  bool operator ==(Location other) =>
-      sourceUrl == other.sourceUrl && offset == other.offset;
-
-  int get hashCode => sourceUrl.hashCode + offset;
-
-  String toString() => '(Location $offset)';
-  String get formatString => '$sourceUrl:${line + 1}:${column + 1}';
-}
-
-/// Implementation of [Location] with fixed values given at allocation time.
-@Deprecated("Use the source_span package instead.")
-class FixedLocation extends Location {
-  final String sourceUrl;
-  final int line;
-  final int column;
-
-  FixedLocation(int offset, this.sourceUrl, this.line, this.column)
-      : super(offset);
-}
-
-/// Implementation of [Span] where all the values are given at allocation time.
-@Deprecated("Use the source_span package instead.")
-class FixedSpan extends Span {
-  /// The source text for this span, if available.
-  final String text;
-
-  /// Creates a span which starts and end in the same line.
-  FixedSpan(String sourceUrl, int start, int line, int column,
-            {String text: '', bool isIdentifier: false})
-      : text = text, super(new FixedLocation(start, sourceUrl, line, column),
-            new FixedLocation(start + text.length, sourceUrl, line,
-                column + text.length),
-            isIdentifier);
-}
-
-/// [Location] with values computed from an underling [SourceFile].
-@Deprecated("Use the source_span package instead.")
-class FileLocation extends Location {
-  /// The source file containing this location.
-  final SourceFile file;
-
-  String get sourceUrl => file.url;
-  int get line => file.getLine(offset);
-  int get column => file.getColumn(line, offset);
-
-  FileLocation(this.file, int offset): super(offset);
-}
-
-/// [Span] where values are computed from an underling [SourceFile].
-@Deprecated("Use the source_span package instead.")
-class FileSpan extends Span {
-  /// The source file containing this span.
-  final SourceFile file;
-
-  /// The source text for this span, if available.
-  String get text => file.getText(start.offset, end.offset);
-
-  factory FileSpan(SourceFile file, int start,
-      [int end, bool isIdentifier = false]) {
-    var startLoc = new FileLocation(file, start);
-    var endLoc = end == null ? startLoc : new FileLocation(file, end);
-    return new FileSpan.locations(startLoc, endLoc, isIdentifier);
-  }
-
-  FileSpan.locations(FileLocation start, FileLocation end,
-      bool isIdentifier)
-      : file = start.file, super(start, end, isIdentifier);
-
-  /// Creates a new span that is the union of two existing spans [start] and
-  /// [end]. Note that the resulting span might contain some positions that were
-  /// not in either of the original spans if [start] and [end] are disjoint.
-  FileSpan.union(FileSpan start, FileSpan end)
-      : file = start.file, super.union(start, end) {
-    if (start.file != end.file) {
-      throw new ArgumentError('start and end must be from the same file');
-    }
-  }
-
-  String getLocationMessage(String message,
-      {bool useColors: false, String color}) {
-    return file.getLocationMessage(message, start.offset, end.offset,
-        useColors: useColors, color: color);
-  }
-}
-
-// Constants to determine end-of-lines.
-const int _LF = 10;
-const int _CR = 13;
-
-// Color constants used for generating messages.
-const String _RED_COLOR = '\u001b[31m';
-const String _NO_COLOR = '\u001b[0m';
-
-/// Stores information about a source file, to permit computation of the line
-/// and column. Also contains a nice default error message highlighting the code
-/// location.
-@Deprecated("Use the source_span package instead.")
-class SourceFile {
-  /// Url where the source file is located.
-  final String url;
-  final List<int> _lineStarts;
-  final List<int> _decodedChars;
-
-  SourceFile(this.url, this._lineStarts, this._decodedChars);
-
-  SourceFile.text(this.url, String text)
-      : _lineStarts = <int>[0],
-        _decodedChars = text.runes.toList() {
-    for (int i = 0; i < _decodedChars.length; i++) {
-      var c = _decodedChars[i];
-      if (c == _CR) {
-        // Return not followed by newline is treated as a newline
-        int j = i + 1;
-        if (j >= _decodedChars.length || _decodedChars[j] != _LF) {
-          c = _LF;
-        }
-      }
-      if (c == _LF) _lineStarts.add(i + 1);
-    }
-  }
-
-  /// Returns a span in this [SourceFile] with the given offsets.
-  Span span(int start, [int end, bool isIdentifier = false]) =>
-      new FileSpan(this, start, end, isIdentifier);
-
-  /// Returns a location in this [SourceFile] with the given offset.
-  Location location(int offset) => new FileLocation(this, offset);
-
-  /// Gets the 0-based line corresponding to an offset.
-  int getLine(int offset) => binarySearch(_lineStarts, (o) => o > offset) - 1;
-
-  /// Gets the 0-based column corresponding to an offset.
-  int getColumn(int line, int offset) {
-    if (line < 0 || line >= _lineStarts.length) return 0;
-    return offset - _lineStarts[line];
-  }
-
-  /// Get the offset for a given line and column
-  int getOffset(int line, int column) {
-    if (line < 0) return getOffset(0, 0);
-    if (line < _lineStarts.length) {
-      return _lineStarts[line] + column;
-    } else {
-      return _decodedChars.length;
-    }
-  }
-
-  /// Gets the text at the given offsets.
-  String getText(int start, [int end]) =>
-      new String.fromCharCodes(_decodedChars.sublist(max(start, 0), end));
-
-  /// Create a pretty string representation from a span.
-  String getLocationMessage(String message, int start, int end,
-      {bool useColors: false, String color}) {
-    // TODO(jmesserly): it would be more useful to pass in an object that
-    // controls how the errors are printed. This method is a bit too smart.
-    var line = getLine(start);
-    var column = getColumn(line, start);
-
-    var source = url == null ? '' : ' of ${p.prettyUri(url)}';
-    var msg = 'line ${line + 1}, column ${column + 1}$source: $message';
-
-    if (_decodedChars == null) {
-      // We don't have any text to include, so exit.
-      return msg;
-    }
-
-    var buf = new StringBuffer(msg);
-    buf.write('\n');
-
-    // +1 for 0-indexing, +1 again to avoid the last line
-    var textLine = getText(getOffset(line, 0), getOffset(line + 1, 0));
-
-    column = min(column, textLine.length - 1);
-    int toColumn = min(column + end - start, textLine.length);
-    if (useColors) {
-      if (color == null) {
-        color = _RED_COLOR;
-      }
-      buf.write(textLine.substring(0, column));
-      buf.write(color);
-      buf.write(textLine.substring(column, toColumn));
-      buf.write(_NO_COLOR);
-      buf.write(textLine.substring(toColumn));
-    } else {
-      buf.write(textLine);
-      if (textLine != '' && !textLine.endsWith('\n')) buf.write('\n');
-    }
-
-    int i = 0;
-    for (; i < column; i++) {
-      buf.write(' ');
-    }
-
-    if (useColors) buf.write(color);
-    for (; i < toColumn; i++) {
-      buf.write('^');
-    }
-    if (useColors) buf.write(_NO_COLOR);
-    return buf.toString();
-  }
-}
-
-/// A convenience type to treat a code segment as if it were a separate
-/// [SourceFile]. A [SourceFileSegment] shifts all locations by an offset, which
-/// allows you to set source-map locations based on the locations relative to
-/// the start of the segment, but that get translated to absolute locations in
-/// the original source file.
-@Deprecated("Use the source_span package instead.")
-class SourceFileSegment extends SourceFile {
-  final int _baseOffset;
-  final int _baseLine;
-  final int _baseColumn;
-  final int _maxOffset;
-
-  SourceFileSegment(String url, String textSegment, Location startOffset)
-      : _baseOffset = startOffset.offset,
-        _baseLine = startOffset.line,
-        _baseColumn = startOffset.column,
-        _maxOffset = startOffset.offset + textSegment.length,
-        super.text(url, textSegment);
-
-  /// Craete a span, where [start] is relative to this segment's base offset.
-  /// The returned span stores the real offset on the file, so that error
-  /// messages are reported at the real location.
-  Span span(int start, [int end, bool isIdentifier = false]) =>
-      super.span(start + _baseOffset,
-          end == null ? null : end + _baseOffset, isIdentifier);
-
-  /// Create a location, where [offset] relative to this segment's base offset.
-  /// The returned span stores the real offset on the file, so that error
-  /// messages are reported at the real location.
-  Location location(int offset) => super.location(offset + _baseOffset);
-
-  /// Return the line on the underlying file associated with the [offset] of the
-  /// underlying file. This method operates on the real offsets from the
-  /// original file, so that error messages can be reported accurately. When the
-  /// requested offset is past the length of the segment, this returns the line
-  /// number after the end of the segment (total lines + 1).
-  int getLine(int offset) {
-    var res = super.getLine(max(offset - _baseOffset, 0)) + _baseLine;
-    return (offset > _maxOffset) ? res + 1 : res;
-  }
-
-  /// Return the column on the underlying file associated with [line] and
-  /// [offset], where [line] is absolute from the beginning of the underlying
-  /// file. This method operates on the real offsets from the original file, so
-  /// that error messages can be reported accurately.
-  int getColumn(int line, int offset) {
-    var col = super.getColumn(line - _baseLine, max(offset - _baseOffset, 0));
-    return line == _baseLine ? col + _baseColumn : col;
-  }
-
-  /// Return the offset associated with a line and column. This method operates
-  /// on the real offsets from the original file, so that error messages can be
-  /// reported accurately.
-  int getOffset(int line, int column) =>
-    super.getOffset(line - _baseLine,
-        line == _baseLine ? column - _baseColumn : column) + _baseOffset;
-
-  /// Retrieve the text associated with the specified range. This method
-  /// operates on the real offsets from the original file, so that error
-  /// messages can be reported accurately.
-  String getText(int start, [int end]) =>
-    super.getText(start - _baseOffset, end == null ? null : end - _baseOffset);
-}
-
-/// A class for exceptions that have source span information attached.
-@Deprecated("Use the source_span package instead.")
-class SpanException implements Exception {
-  /// A message describing the exception.
-  final String message;
-
-  /// The span associated with this exception.
-  ///
-  /// This may be `null` if the source location can't be determined.
-  final Span span;
-
-  SpanException(this.message, this.span);
-
-  String toString({bool useColors: false, String color}) {
-    if (span == null) return message;
-    return "Error on " + span.getLocationMessage(message,
-        useColors: useColors, color: color);
-  }
-}
-
-/// A [SpanException] that's also a [FormatException].
-@Deprecated("Use the source_span package instead.")
-class SpanFormatException extends SpanException implements FormatException {
-  final source;
-
-  SpanFormatException(String message, Span span, [this.source])
-      : super(message, span);
-
-  int get offset => span == null ? null : span.start.offset;
-}
diff --git a/lib/src/source_map_span.dart b/lib/src/source_map_span.dart
new file mode 100644
index 0000000..20eb17a
--- /dev/null
+++ b/lib/src/source_map_span.dart
@@ -0,0 +1,59 @@
+// Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library source_maps.source_map_span;
+
+import 'package:source_span/source_span.dart';
+
+/// A [SourceSpan] for spans coming from or being written to source maps.
+///
+/// These spans have an extra piece of metadata: whether or not they represent
+/// an identifier (see [isIdentifier]).
+class SourceMapSpan extends SourceSpanBase {
+  /// Whether this span represents an identifier.
+  ///
+  /// If this is `true`, [text] is the value of the identifier.
+  final bool isIdentifier;
+
+  SourceMapSpan(SourceLocation start, SourceLocation end, String text,
+          {this.isIdentifier: false})
+      : super(start, end, text);
+
+  /// Creates a [SourceMapSpan] for an identifier with value [text] starting at
+  /// [start].
+  ///
+  /// The [end] location is determined by adding [text] to [start].
+  SourceMapSpan.identifier(SourceLocation start, String text)
+      : this(
+          start,
+          new SourceLocation(start.offset + text.length,
+              sourceUrl: start.sourceUrl,
+              line: start.line,
+              column: start.column + text.length),
+          text,
+          isIdentifier: true);
+}
+
+/// A wrapper aruond a [FileSpan] that implements [SourceMapSpan].
+class SourceMapFileSpan implements SourceMapSpan, FileSpan {
+  final FileSpan _inner;
+  final bool isIdentifier;
+
+  SourceFile get file => _inner.file;
+  FileLocation get start => _inner.start;
+  FileLocation get end => _inner.end;
+  String get text => _inner.text;
+  Uri get sourceUrl => _inner.sourceUrl;
+  int get length => _inner.length;
+
+  SourceMapFileSpan(this._inner, {this.isIdentifier: false});
+
+  int compareTo(SourceSpan other) => _inner.compareTo(other);
+  SourceSpan union(SourceSpan other) => _inner.union(other);
+  FileSpan expand(FileSpan other) => _inner.expand(other);
+  String message(String message, {color}) =>
+      _inner.message(message, color: color);
+  String toString() => _inner.toString()
+      .replaceAll("FileSpan", "SourceMapFileSpan");
+}
diff --git a/lib/src/span_wrapper.dart b/lib/src/span_wrapper.dart
deleted file mode 100644
index e0c107b..0000000
--- a/lib/src/span_wrapper.dart
+++ /dev/null
@@ -1,85 +0,0 @@
-// Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-library source_maps.span_wrapper;
-
-import 'package:source_span/source_span.dart' as source_span;
-
-import '../span.dart';
-
-/// A wrapper that exposes a [source_span.SourceSpan] as a [Span].
-class SpanWrapper extends Span {
-  final source_span.SourceSpan _inner;
-
-  String get text => _inner.text;
-
-  SpanWrapper(source_span.SourceSpan inner, bool isIdentifier)
-      : _inner = inner,
-        super(
-            new LocationWrapper(inner.start),
-            new LocationWrapper(inner.end),
-            isIdentifier);
-
-  static Span wrap(span, [bool isIdentifier = false]) {
-    if (span is Span) return span;
-    return new SpanWrapper(span, isIdentifier);
-  }
-}
-
-/// A wrapper that exposes a [source_span.SourceLocation] as a [Location].
-class LocationWrapper extends Location {
-  final source_span.SourceLocation _inner;
-
-  String get sourceUrl => _inner.sourceUrl.toString();
-  int get line => _inner.line;
-  int get column => _inner.column;
-
-  LocationWrapper(source_span.SourceLocation inner)
-      : _inner = inner,
-        super(inner.offset);
-
-  static Location wrap(location) {
-    if (location is Location) return location;
-    return new LocationWrapper(location);
-  }
-}
-
-/// A wrapper that exposes a [source_span.SourceFile] as a [SourceFile].
-class SourceFileWrapper implements SourceFile {
-  final source_span.SourceFile _inner;
-
-  // These are necessary to avoid analyzer warnings;
-  final _lineStarts = null;
-  final _decodedChars = null;
-
-  String get url => _inner.url.toString();
-
-  SourceFileWrapper(this._inner);
-
-  static SourceFile wrap(sourceFile) {
-    if (sourceFile is SourceFile) return sourceFile;
-    return new SourceFileWrapper(sourceFile);
-  }
-
-  Span span(int start, [int end, bool isIdentifier = false]) {
-    if (end == null) end = start;
-    return new SpanWrapper(_inner.span(start, end), isIdentifier);
-  }
-
-  Location location(int offset) => new LocationWrapper(_inner.location(offset));
-
-  int getLine(int offset) => _inner.getLine(offset);
-
-  int getColumn(int line, int offset) => _inner.getColumn(offset, line: line);
-
-  int getOffset(int line, int column) => _inner.getOffset(line, column);
-
-  String getText(int start, [int end]) => _inner.getText(start, end);
-
-  String getLocationMessage(String message, int start, int end,
-      {bool useColors: false, String color}) {
-    return span(start, end).getLocationMessage(message,
-        useColors: useColors, color: color);
-  }
-}
diff --git a/test/builder_test.dart b/test/builder_test.dart
index 8842c62..ca0ca8d 100644
--- a/test/builder_test.dart
+++ b/test/builder_test.dart
@@ -16,7 +16,7 @@
         ..addSpan(inputFunction, outputFunction)
         ..addSpan(inputVar2, outputVar2)
         ..addSpan(inputExpr, outputExpr))
-        .build(output.url);
+        .build(output.url.toString());
     expect(map, equals(EXPECTED_MAP));
   });
 
@@ -26,7 +26,7 @@
         ..addLocation(inputFunction.start, outputFunction.start, 'longName')
         ..addLocation(inputVar2.start, outputVar2.start, 'longVar2')
         ..addLocation(inputExpr.start, outputExpr.start, null))
-        .toJson(output.url);
+        .toJson(output.url.toString());
     expect(str, JSON.encode(EXPECTED_MAP));
   });
 }
diff --git a/test/common.dart b/test/common.dart
index 0c1f28a..73a8d40 100644
--- a/test/common.dart
+++ b/test/common.dart
@@ -6,6 +6,7 @@
 library test.common;
 
 import 'package:source_maps/source_maps.dart';
+import 'package:source_span/source_span.dart';
 import 'package:unittest/unittest.dart';
 
 /// Content of the source file
@@ -18,40 +19,40 @@
   return longVar1 + longVar2;
 }
 ''';
-var input = new SourceFile.text('input.dart', INPUT);
+var input = new SourceFile(INPUT, url: 'input.dart');
 
 /// A span in the input file
-Span ispan(int start, int end, [bool isIdentifier = false]) =>
-    new FileSpan(input, start, end, isIdentifier);
+SourceMapSpan ispan(int start, int end, [bool isIdentifier = false]) =>
+    new SourceMapFileSpan(input.span(start, end), isIdentifier: isIdentifier);
 
-Span inputVar1 = ispan(30, 38, true);
-Span inputFunction = ispan(74, 82, true);
-Span inputVar2 = ispan(87, 95, true);
+SourceMapSpan inputVar1 = ispan(30, 38, true);
+SourceMapSpan inputFunction = ispan(74, 82, true);
+SourceMapSpan inputVar2 = ispan(87, 95, true);
 
-Span inputVar1NoSymbol = ispan(30, 38);
-Span inputFunctionNoSymbol = ispan(74, 82);
-Span inputVar2NoSymbol = ispan(87, 95);
+SourceMapSpan inputVar1NoSymbol = ispan(30, 38);
+SourceMapSpan inputFunctionNoSymbol = ispan(74, 82);
+SourceMapSpan inputVar2NoSymbol = ispan(87, 95);
 
-Span inputExpr = ispan(108, 127);
+SourceMapSpan inputExpr = ispan(108, 127);
 
 /// Content of the target file
 const String OUTPUT = '''
 var x = 3;
 f(y) => x + y;
 ''';
-var output = new SourceFile.text('output.dart', OUTPUT);
+var output = new SourceFile(OUTPUT, url: 'output.dart');
 
 /// A span in the output file
-Span ospan(int start, int end, [bool isIdentifier = false]) =>
-    new FileSpan(output, start, end, isIdentifier);
+SourceMapSpan ospan(int start, int end, [bool isIdentifier = false]) =>
+    new SourceMapFileSpan(output.span(start, end), isIdentifier: isIdentifier);
 
-Span outputVar1 = ospan(4, 5, true);
-Span outputFunction = ospan(11, 12, true);
-Span outputVar2 = ospan(13, 14, true);
-Span outputVar1NoSymbol = ospan(4, 5);
-Span outputFunctionNoSymbol = ospan(11, 12);
-Span outputVar2NoSymbol = ospan(13, 14);
-Span outputExpr = ospan(19, 24);
+SourceMapSpan outputVar1 = ospan(4, 5, true);
+SourceMapSpan outputFunction = ospan(11, 12, true);
+SourceMapSpan outputVar2 = ospan(13, 14, true);
+SourceMapSpan outputVar1NoSymbol = ospan(4, 5);
+SourceMapSpan outputFunctionNoSymbol = ospan(11, 12);
+SourceMapSpan outputVar2NoSymbol = ospan(13, 14);
+SourceMapSpan outputExpr = ospan(19, 24);
 
 /// Expected output mapping when recording the following four mappings:
 ///      inputVar1       <=   outputVar1
@@ -70,7 +71,8 @@
     'file': 'output.dart'
 };
 
-check(Span outputSpan, Mapping mapping, Span inputSpan, bool realOffsets) {
+check(SourceSpan outputSpan, Mapping mapping, SourceMapSpan inputSpan,
+    bool realOffsets) {
   var line = outputSpan.start.line;
   var column = outputSpan.start.column;
   var files = realOffsets ? {'input.dart': input} : null; 
diff --git a/test/end2end_test.dart b/test/end2end_test.dart
index 99fe40d..7dbc6bd 100644
--- a/test/end2end_test.dart
+++ b/test/end2end_test.dart
@@ -6,6 +6,7 @@
 
 import 'package:unittest/unittest.dart';
 import 'package:source_maps/source_maps.dart';
+import 'package:source_span/source_span.dart';
 import 'common.dart';
 
 main() {
@@ -33,7 +34,7 @@
         ..addSpan(inputFunction, outputFunction)
         ..addSpan(inputVar2, outputVar2)
         ..addSpan(inputExpr, outputExpr))
-        .build(output.url);
+        .build(output.url.toString());
     var mapping = parseJson(map);
     check(outputVar1, mapping, inputVar1, false);
     check(outputVar2, mapping, inputVar2, false);
@@ -47,7 +48,7 @@
         ..addSpan(inputFunctionNoSymbol, outputFunctionNoSymbol)
         ..addSpan(inputVar2NoSymbol, outputVar2NoSymbol)
         ..addSpan(inputExpr, outputExpr))
-        .build(output.url);
+        .build(output.url.toString());
     var mapping = parseJson(map);
     check(outputVar1NoSymbol, mapping, inputVar1NoSymbol, false);
     check(outputVar2NoSymbol, mapping, inputVar2NoSymbol, false);
@@ -65,7 +66,7 @@
         ..addSpan(inputVar2, outputVar2)
         ..addSpan(inputExpr, outputExpr)
         ..addSpan(inputExpr, outputExpr))
-        .build(output.url);
+        .build(output.url.toString());
     var mapping = parseJson(map);
     check(outputVar1, mapping, inputVar1, false);
     check(outputVar2, mapping, inputVar2, false);
@@ -82,7 +83,7 @@
         ..addSpan(inputVar2NoSymbol, outputVar2NoSymbol)
         ..addSpan(inputVar2NoSymbol, outputVar2NoSymbol)
         ..addSpan(inputExpr, outputExpr))
-        .build(output.url);
+        .build(output.url.toString());
     var mapping = parseJson(map);
     check(outputVar1NoSymbol, mapping, inputVar1NoSymbol, false);
     check(outputVar2NoSymbol, mapping, inputVar2NoSymbol, false);
@@ -96,7 +97,7 @@
         ..addSpan(inputFunction, outputFunction)
         ..addSpan(inputVar2, outputVar2)
         ..addSpan(inputExpr, outputExpr))
-        .toJson(output.url);
+        .toJson(output.url.toString());
     var mapping = parse(json);
     check(outputVar1, mapping, inputVar1, true);
     check(outputVar2, mapping, inputVar2, true);
@@ -106,7 +107,7 @@
 
   test('printer projecting marks + parse', () {
     var out = INPUT.replaceAll('long', '_s');
-    var file = new SourceFile.text('output2.dart', out);
+    var file = new SourceFile(out, url: 'output2.dart');
     var printer = new Printer('output2.dart');
     printer.mark(ispan(0, 0));
 
@@ -132,10 +133,11 @@
     expect(printer.text, out);
 
     var mapping = parse(printer.map);
-    checkHelper(Span inputSpan, int adjustment) {
+    checkHelper(SourceMapSpan inputSpan, int adjustment) {
       var start = inputSpan.start.offset - adjustment;
       var end = (inputSpan.end.offset - adjustment) - 2;
-      var span = new FileSpan(file, start, end, inputSpan.isIdentifier);
+      var span = new SourceMapFileSpan(file.span(start, end),
+          isIdentifier: inputSpan.isIdentifier);
       check(span, mapping, inputSpan, true);
     }
 
@@ -145,17 +147,16 @@
     checkHelper(inputExpr, 6);
 
     // We projected correctly lines that have no mappings
-    check(new FileSpan(file, 66, 66, false), mapping, ispan(45, 45), true);
-    check(new FileSpan(file, 63, 64, false), mapping, ispan(45, 45), true);
-    check(new FileSpan(file, 68, 68, false), mapping, ispan(70, 70), true);
-    check(new FileSpan(file, 71, 71, false), mapping, ispan(70, 70), true);
+    check(file.span(66, 66), mapping, ispan(45, 45), true);
+    check(file.span(63, 64), mapping, ispan(45, 45), true);
+    check(file.span(68, 68), mapping, ispan(70, 70), true);
+    check(file.span(71, 71), mapping, ispan(70, 70), true);
 
     // Start of the last line
     var oOffset = out.length - 2;
     var iOffset = INPUT.length - 2;
-    check(new FileSpan(file, oOffset, oOffset, false), mapping,
-        ispan(iOffset, iOffset), true);
-    check(new FileSpan(file, oOffset + 1, oOffset + 1, false), mapping,
+    check(file.span(oOffset, oOffset), mapping, ispan(iOffset, iOffset), true);
+    check(file.span(oOffset + 1, oOffset + 1), mapping,
         ispan(iOffset, iOffset), true);
   });
 }
diff --git a/test/parser_test.dart b/test/parser_test.dart
index 62cd08f..8528683 100644
--- a/test/parser_test.dart
+++ b/test/parser_test.dart
@@ -104,7 +104,7 @@
     var inputMap = new Map.from(MAP_WITH_SOURCE_LOCATION);
     inputMap['sourceRoot'] = '/pkg/';
     var mapping = parseJson(inputMap);
-    expect(mapping.spanFor(0, 0).sourceUrl, "/pkg/input.dart");
+    expect(mapping.spanFor(0, 0).sourceUrl, Uri.parse("/pkg/input.dart"));
 
     var newSourceRoot = '/new/';
 
diff --git a/test/printer_test.dart b/test/printer_test.dart
index fe27f76..e55ca9f 100644
--- a/test/printer_test.dart
+++ b/test/printer_test.dart
@@ -6,8 +6,8 @@
 
 import 'dart:convert';
 import 'package:unittest/unittest.dart';
-import 'package:source_maps/printer.dart';
-import 'package:source_maps/span.dart';
+import 'package:source_maps/source_maps.dart';
+import 'package:source_span/source_span.dart';
 import 'common.dart';
 
 main() {
@@ -53,13 +53,12 @@
     // 8 new lines in the source map:
     expect(printer.map.split(';').length, 8);
 
-    asFixed(Span s) => new FixedSpan(s.sourceUrl,
-        s.start.offset, s.start.line, s.start.column,
-        text: s.text, isIdentifier: s.isIdentifier);
+    asFixed(SourceMapSpan s) => new SourceMapSpan(s.start, s.end, s.text,
+        isIdentifier: s.isIdentifier);
 
     // The result is the same if we use fixed positions
     var printer2 = new Printer('output2.dart');
-    printer2..mark(new FixedSpan('input.dart', 0, 0, 0))
+    printer2..mark(new SourceLocation(0, sourceUrl: 'input.dart').pointSpan())
         ..add(segments[0], projectMarks: true)
         ..mark(asFixed(inputVar1))
         ..add('_s')
diff --git a/test/refactor_test.dart b/test/refactor_test.dart
index 5d0abf1..08b8965 100644
--- a/test/refactor_test.dart
+++ b/test/refactor_test.dart
@@ -6,13 +6,13 @@
 
 import 'package:unittest/unittest.dart';
 import 'package:source_maps/refactor.dart';
-import 'package:source_maps/span.dart';
 import 'package:source_maps/parser.dart' show parse, Mapping;
+import 'package:source_span/source_span.dart';
 
 main() {
   group('conflict detection', () {
     var original = "0123456789abcdefghij";
-    var file = new SourceFile.text('', original);
+    var file = new SourceFile(original);
 
     test('no conflict, in order', () {
       var txn = new TextEditTransaction(original, file);
@@ -48,7 +48,7 @@
   test('generated source maps', () {
     var original =
         "0123456789\n0*23456789\n01*3456789\nabcdefghij\nabcd*fghij\n";
-    var file = new SourceFile.text('', original);
+    var file = new SourceFile(original);
     var txn = new TextEditTransaction(original, file);
     txn.edit(27, 29, '__\n    ');
     txn.edit(34, 35, '___');
@@ -60,42 +60,90 @@
 
     // Line 1 and 2 are unmodified: mapping any column returns the beginning
     // of the corresponding line:
-    expect(_span(1, 1, map, file), "line 1, column 1 of .: \n0123456789");
-    expect(_span(1, 5, map, file), "line 1, column 1 of .: \n0123456789");
-    expect(_span(2, 1, map, file), "line 2, column 1 of .: \n0*23456789");
-    expect(_span(2, 8, map, file), "line 2, column 1 of .: \n0*23456789");
+    expect(_span(1, 1, map, file),
+        "line 1, column 1: \n"
+        "0123456789\n"
+        "^");
+    expect(_span(1, 5, map, file),
+        "line 1, column 1: \n"
+        "0123456789\n"
+        "^");
+    expect(_span(2, 1, map, file),
+        "line 2, column 1: \n"
+        "0*23456789\n"
+        "^");
+    expect(_span(2, 8, map, file),
+        "line 2, column 1: \n"
+        "0*23456789\n"
+        "^");
 
     // Line 3 is modified part way: mappings before the edits have the right
     // mapping, after the edits the mapping is null.
-    expect(_span(3, 1, map, file), "line 3, column 1 of .: \n01*3456789");
-    expect(_span(3, 5, map, file), "line 3, column 1 of .: \n01*3456789");
+    expect(_span(3, 1, map, file),
+        "line 3, column 1: \n"
+        "01*3456789\n"
+        "^");
+    expect(_span(3, 5, map, file),
+        "line 3, column 1: \n"
+        "01*3456789\n"
+        "^");
 
     // Start of edits map to beginning of the edit secion:
-    expect(_span(3, 6, map, file), "line 3, column 6 of .: \n01*3456789");
-    expect(_span(3, 7, map, file), "line 3, column 6 of .: \n01*3456789");
+    expect(_span(3, 6, map, file),
+        "line 3, column 6: \n"
+        "01*3456789\n"
+        "     ^");
+    expect(_span(3, 7, map, file),
+        "line 3, column 6: \n"
+        "01*3456789\n"
+        "     ^");
 
     // Lines added have no mapping (they should inherit the last mapping),
     // but the end of the edit region continues were we left off:
     expect(_span(4, 1, map, file), isNull);
-    expect(_span(4, 5, map, file), "line 3, column 8 of .: \n01*3456789");
+    expect(_span(4, 5, map, file),
+        "line 3, column 8: \n"
+        "01*3456789\n"
+        "       ^");
 
     // Subsequent lines are still mapped correctly:
     // a (in a___cd...)
-    expect(_span(5, 1, map, file), "line 4, column 1 of .: \nabcdefghij");
+    expect(_span(5, 1, map, file),
+        "line 4, column 1: \n"
+        "abcdefghij\n"
+        "^");
     // _ (in a___cd...)
-    expect(_span(5, 2, map, file), "line 4, column 2 of .: \nabcdefghij");
+    expect(_span(5, 2, map, file),
+        "line 4, column 2: \n"
+        "abcdefghij\n"
+        " ^");
     // _ (in a___cd...)
-    expect(_span(5, 3, map, file), "line 4, column 2 of .: \nabcdefghij");
+    expect(_span(5, 3, map, file),
+        "line 4, column 2: \n"
+        "abcdefghij\n"
+        " ^");
     // _ (in a___cd...)
-    expect(_span(5, 4, map, file), "line 4, column 2 of .: \nabcdefghij");
+    expect(_span(5, 4, map, file),
+        "line 4, column 2: \n"
+        "abcdefghij\n"
+        " ^");
     // c (in a___cd...)
-    expect(_span(5, 5, map, file), "line 4, column 3 of .: \nabcdefghij");
-    expect(_span(6, 1, map, file), "line 5, column 1 of .: \nabcd*fghij");
-    expect(_span(6, 8, map, file), "line 5, column 1 of .: \nabcd*fghij");
+    expect(_span(5, 5, map, file),
+        "line 4, column 3: \n"
+        "abcdefghij\n"
+        "  ^");
+    expect(_span(6, 1, map, file),
+        "line 5, column 1: \n"
+        "abcd*fghij\n"
+        "^");
+    expect(_span(6, 8, map, file),
+        "line 5, column 1: \n"
+        "abcd*fghij\n"
+        "^");
   });
 }
 
 String _span(int line, int column, Mapping map, SourceFile file) {
   var span = map.spanFor(line - 1, column - 1, files: {'': file});
-  return span == null ? null : span.getLocationMessage('').trim();
+  return span == null ? null : span.message('').trim();
 }
diff --git a/test/run.dart b/test/run.dart
index 21d2037..ec3c3ab 100755
--- a/test/run.dart
+++ b/test/run.dart
@@ -14,7 +14,6 @@
 import 'parser_test.dart' as parser_test;
 import 'printer_test.dart' as printer_test;
 import 'refactor_test.dart' as refactor_test;
-import 'span_test.dart' as span_test;
 import 'utils_test.dart' as utils_test;
 import 'vlq_test.dart' as vlq_test;
 
@@ -33,7 +32,6 @@
   addGroup('parser_test.dart', parser_test.main);
   addGroup('printer_test.dart', printer_test.main);
   addGroup('refactor_test.dart', refactor_test.main);
-  addGroup('span_test.dart', span_test.main);
   addGroup('utils_test.dart', utils_test.main);
   addGroup('vlq_test.dart', vlq_test.main);
 }
diff --git a/test/span_test.dart b/test/span_test.dart
deleted file mode 100644
index 190b7a6..0000000
--- a/test/span_test.dart
+++ /dev/null
@@ -1,341 +0,0 @@
-// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-library test.span_test;
-
-import 'package:unittest/unittest.dart';
-import 'package:source_maps/span.dart';
-
-const String TEST_FILE = '''
-+23456789_
- +       _123456789_123456789_123456789_123456789_123456789_123456789_123456789_
-  +                _123456789_1
-123+56789_123456789_1234567
-1234+6789_1234
-12345+789_123456789_12345
-123456+89_123456789_123456789_123456789_123456789_123456789_123456789_123456789
-1234567+9_123456789_123456789_123456789_123456789_123456789_123456789_123
-12345678+_123456789_123456789_123456789_123456789_1
-123456789+123456789_123456789_12345678
-123456789_+23456789_123456789_123456789_123
-123456789_1+3456789_123456789
-''';
-
-List<int> newLines = TEST_FILE.split('\n').map((s) => s.length).toList();
-
-main() {
-  var file = new SourceFile.text('file', TEST_FILE);
-  span(int start, int end) => file.span(start, end);
-  loc(int offset) => file.location(offset);
-
-  test('validate test input', () {
-    expect(newLines,
-      const [10, 80, 31, 27, 14, 25, 79, 73, 51, 38, 43, 29, 0]);
-  });
-
-  test('get line and column', () {
-    line(int n) => file.getLine(n);
-    col(int n) => file.getColumn(file.getLine(n), n);
-
-    expect(line(8), 0);
-    expect(line(10), 0);
-    expect(line(11), 1);
-    expect(line(12), 1);
-    expect(line(91), 1);
-    expect(line(92), 2);
-    expect(line(93), 2);
-    expect(col(11), 0);
-    expect(col(12), 1);
-    expect(col(91), 80);
-    expect(col(92), 0);
-    expect(col(93), 1);
-
-    int j = 0;
-    int lineOffset = 0;
-    for (int i = 0; i < TEST_FILE.length; i++) {
-      if (i > lineOffset + newLines[j]) {
-        lineOffset += newLines[j] + 1;
-        j++;
-      }
-      expect(line(i), j, reason: 'position: $i');
-      expect(col(i), i - lineOffset, reason: 'position: $i');
-    }
-  });
-
-  test('get text', () {
-    // fifth line (including 4 new lines), columns 2 .. 11
-    var line = 10 + 80 + 31 + 27 + 4;
-    expect(file.getText(line + 2, line + 11), '34+6789_1');
-  });
-
-  group('location message', () {
-    test('first line', () {
-      expect(file.getLocationMessage('the message', 1, 3),
-          'line 1, column 2 of file: the message\n'
-          '+23456789_\n'
-          ' ^^');
-    });
-
-    test('in the middle of the file', () {
-      // fifth line (including 4 new lines), columns 2 .. 11
-      var line = 10 + 80 + 31 + 27 + 4;
-      expect(file.getLocationMessage('the message', line + 2, line + 11),
-          'line 5, column 3 of file: the message\n'
-          '1234+6789_1234\n'
-          '  ^^^^^^^^^');
-    });
-
-    test('no file url', () {
-      var line = 10 + 80 + 31 + 27 + 4;
-      expect(new SourceFile.text(null, TEST_FILE).getLocationMessage(
-          'the message', line + 2, line + 11),
-          'line 5, column 3: the message\n'
-          '1234+6789_1234\n'
-          '  ^^^^^^^^^');
-    });
-
-    test('penultimate line', () {
-      // We search '\n' backwards twice because last line is \n terminated:
-      int index = TEST_FILE.lastIndexOf('\n');
-      var start = TEST_FILE.lastIndexOf('\n', index - 1) - 3;
-      expect(file.getLocationMessage('the message', start, start + 2),
-          'line 11, column 41 of file: the message\n'
-          '123456789_+23456789_123456789_123456789_123\n'
-          '                                        ^^');
-    });
-
-    test('last line', () {
-      var start = TEST_FILE.lastIndexOf('\n') - 2;
-      expect(file.getLocationMessage('the message', start, start + 1),
-          'line 12, column 28 of file: the message\n'
-          '123456789_1+3456789_123456789\n'
-          '                           ^');
-    });
-
-    group('no trailing empty-line at the end -', () {
-      var text = TEST_FILE.substring(0, TEST_FILE.length - 1);
-      var file2 = new SourceFile.text('file', text);
-
-      test('penultimate line', () {
-        var start = text.lastIndexOf('\n') - 3;
-        expect(file2.getLocationMessage('the message', start, start + 2),
-            'line 11, column 41 of file: the message\n'
-            '123456789_+23456789_123456789_123456789_123\n'
-            '                                        ^^');
-      });
-
-      test('last line', () {
-        var start = text.length - 2;
-        expect(file2.getLocationMessage('the message', start, start + 1),
-            'line 12, column 28 of file: the message\n'
-            '123456789_1+3456789_123456789\n'
-            '                           ^');
-      });
-    });
-
-    test('single line', () {
-      var text = "this is a single line";
-      int start = text.indexOf(' ') + 1;
-      var file2 = new SourceFile.text('file', text);
-      expect(file2.getLocationMessage('the message', start, start + 2),
-            'line 1, column ${start + 1} of file: the message\n'
-            'this is a single line\n'
-            '     ^^');
-    });
-  });
-
-  test('location getters', () {
-    expect(loc(8).line, 0);
-    expect(loc(8).column, 8);
-    expect(loc(9).line, 0);
-    expect(loc(9).column, 9);
-    expect(loc(8).formatString, 'file:1:9');
-    expect(loc(12).line, 1);
-    expect(loc(12).column, 1);
-    expect(loc(95).line, 2);
-    expect(loc(95).column, 3);
-  });
-
-  test('location compare', () {
-    var list = [9, 8, 11, 14, 6, 6, 1, 1].map((n) => loc(n)).toList();
-    list.sort();
-    var lastOffset = 0;
-    for (var location in list) {
-      expect(location.offset, greaterThanOrEqualTo(lastOffset));
-      lastOffset = location.offset;
-    }
-  });
-
-  test('span getters', () {
-    expect(span(8, 9).start.line, 0);
-    expect(span(8, 9).start.column, 8);
-    expect(span(8, 9).end.line, 0);
-    expect(span(8, 9).end.column, 9);
-    expect(span(8, 9).text, '9');
-    expect(span(8, 9).isIdentifier, false);
-    expect(span(8, 9).formatLocation, 'file:1:9');
-
-    var line = 10 + 80 + 31 + 27 + 4;
-    expect(span(line + 2, line + 11).getLocationMessage('the message'),
-        'line 5, column 3 of file: the message\n'
-        '1234+6789_1234\n'
-        '  ^^^^^^^^^');
-
-    expect(span(12, 95).start.line, 1);
-    expect(span(12, 95).start.column, 1);
-    expect(span(12, 95).end.line, 2);
-    expect(span(12, 95).end.column, 3);
-    expect(span(12, 95).text,
-        '+       _123456789_123456789_123456789_123456789_123456789_1234567'
-        '89_123456789_\n  +');
-    expect(span(12, 95).formatLocation, 'file:2:2');
-  });
-
-  test('span union', () {
-    var union = new FileSpan.union(span(8, 9), span(12, 95));
-    expect(union.start.offset, 8);
-    expect(union.start.line, 0);
-    expect(union.start.column, 8);
-    expect(union.end.offset, 95);
-    expect(union.end.line, 2);
-    expect(union.end.column, 3);
-    expect(union.text,
-        '9_\n'
-        ' +       _123456789_123456789_123456789_123456789_123456789_'
-        '123456789_123456789_\n  +');
-    expect(union.formatLocation, 'file:1:9');
-  });
-
-  test('span compare', () {
-    var list = [span(9, 10), span(8, 9), span(11, 12), span(14, 19),
-        span(6, 12), span(6, 8), span(1, 9), span(1, 2)];
-    list.sort();
-    var lastStart = 0;
-    var lastEnd = 0;
-    for (var span in list) {
-      expect(span.start.offset, greaterThanOrEqualTo(lastStart));
-      if (span.start.offset == lastStart) {
-        expect(span.end.offset, greaterThanOrEqualTo(lastEnd));
-      }
-      lastStart = span.start.offset;
-      lastEnd = span.end.offset;
-    }
-  });
-
-  test('range check for large offsets', () {
-    var start = TEST_FILE.length;
-    expect(file.getLocationMessage('the message', start, start + 9),
-        'line 13, column 1 of file: the message\n');
-  });
-
-  group('file segment', () {
-    var baseOffset = 123;
-    var segmentText = TEST_FILE.substring(baseOffset, TEST_FILE.length - 100);
-    var segment = new SourceFileSegment('file', segmentText, loc(baseOffset));
-    sline(int n) => segment.getLine(n);
-    scol(int n) => segment.getColumn(segment.getLine(n), n);
-    line(int n) => file.getLine(n);
-    col(int n) => file.getColumn(file.getLine(n), n);
-
-    test('get line and column', () {
-      int j = 0;
-      int lineOffset = 0;
-      for (int i = baseOffset; i < segmentText.length; i++) {
-        if (i > lineOffset + newLines[j]) {
-          lineOffset += newLines[j] + 1;
-          j++;
-        }
-        expect(segment.location(i - baseOffset).offset, i);
-        expect(segment.location(i - baseOffset).line, line(i));
-        expect(segment.location(i - baseOffset).column, col(i));
-        expect(segment.span(i - baseOffset).start.offset, i);
-        expect(segment.span(i - baseOffset).start.line, line(i));
-        expect(segment.span(i - baseOffset).start.column, col(i));
-
-        expect(sline(i), line(i));
-        expect(scol(i), col(i));
-      }
-    });
-
-    test('get text', () {
-      var start = 10 + 80 + 31 + 27 + 4 + 2;
-      expect(segment.getText(start, start + 9), file.getText(start, start + 9));
-    });
-
-    group('location message', () {
-      test('first line', () {
-        var start = baseOffset + 7;
-        expect(segment.getLocationMessage('the message', start, start + 2),
-            file.getLocationMessage('the message', start, start + 2));
-      });
-
-      test('in a middle line', () {
-        // Example from another test above:
-        var start = 10 + 80 + 31 + 27 + 4 + 2;
-        expect(segment.getLocationMessage('the message', start, start + 9),
-            file.getLocationMessage('the message', start, start + 9));
-      });
-
-      test('last segment line', () {
-        var start = segmentText.length - 4;
-        expect(segment.getLocationMessage('the message', start, start + 2),
-            file.getLocationMessage('the message', start, start + 2));
-      });
-
-      test('past segment, same as last segment line', () {
-        var start = segmentText.length;
-        expect(segment.getLocationMessage('the message', start, start + 2),
-            file.getLocationMessage('the message', start, start + 2));
-
-        start = segmentText.length + 20;
-        expect(segment.getLocationMessage('the message', start, start + 2),
-            file.getLocationMessage('the message', start, start + 2));
-      });
-
-      test('past segment, past its line', () {
-        var start = TEST_FILE.length - 2;
-        expect(file.getLocationMessage('the message', start, start + 1),
-          'line 12, column 29 of file: the message\n'
-          '123456789_1+3456789_123456789\n'
-          '                            ^');
-
-        // The answer below is different because the segment parsing only knows
-        // about the 10 lines it has (and nothing about the possible extra lines
-        // afterwards)
-        expect(segment.getLocationMessage('the message', start, start + 1),
-          'line 11, column 1 of file: the message\n');
-      });
-    });
-  });
-
-  test('span isIdentifier defaults to false', () {
-    var start = new TestLocation(0);
-    var end = new TestLocation(1);
-    expect(new TestSpan(start, end).isIdentifier, false);
-    expect(file.span(8, 9, null).isIdentifier, false);
-    expect(new FixedSpan('', 8, 1, 8, isIdentifier: null).isIdentifier, false);
-  });
-
-  test('span/location implement == and hashCode', () {
-    expect(identical(span(10, 14), span(10, 14)), isFalse);
-    expect(span(10, 14), equals(span(10, 14)));
-    expect(span(10, 14).hashCode, span(10, 14).hashCode);
-
-    expect(identical(loc(13), loc(13)), isFalse);
-    expect(loc(13), equals(loc(13)));
-    expect(loc(13).hashCode, loc(13).hashCode);
-  });
-}
-
-class TestSpan extends Span {
-  TestSpan(Location start, Location end) : super(start, end, null);
-  get text => null;
-}
-
-class TestLocation extends Location {
-  String get sourceUrl => '';
-  TestLocation(int offset) : super(offset);
-  get line => 0;
-  get column => 0;
-}