anchor.diff:  
   import 'definitions.dart';
   
  -// TODO make all members private
  -class MarkdownWriter {
  +class _MarkdownBuilder extends StringBuffer {
     Map<String, Target> _references;
  -  String write(Document document) {
  -    _references = <String, Target>{};
  -    var blocks = writeBlocks(document.contents) + "\n\n";
  -    return blocks + _writeReferences();
  -  }
  -
  -  String writeBlocks(Iterable<Block> blocks,
  +
  +
  +  _MarkdownBuilder([this._references]) : super();
  +
  +
  +  void writeDocument(Document document) {
  +    writeBlocks(document.contents);
  +    write("\n\n");
  +    writeReferences();
  +  }
  +
  +
  +  void writeBlocks(Iterable<Block> blocks,
                        {bool tight: false, String unorderedListChar: "*"}) {
  -
  -    StringBuffer result = new StringBuffer();
  -
       Block prevBlock = null;
       Iterator<Block> it = blocks.iterator;
       bool first = true;
  
positive.code:       prevBlock = block;
  
        if (block is Para) {
          result.write(writePara(block));
        } else if (block is Header) {
          result.write(writeHeader(block));
        } else if (block is HorizontalRule) {
          result.write(writeHorizontalRule(block, unorderedListChar));
        } else if (block is CodeBlock) {
          if (_b is ListBlock) {
            result.write('\n');
          }
          result.write(writeCodeBlock(block));
        } else if (block is Blockquote) {
          result.write(writeBlockquote(block));
        } else if (block is RawBlock) {
          result.write(block.contents);
          result.write("\n");
        } else if (block is UnorderedList) {
          result.write(writeUnorderedList(block, prevBlock: _b));
        } else if (block is OrderedList) {
          result.write(writeOrderedList(block, prevBlock: _b));
        } else {
          throw new UnimplementedError(block.toString());
        }
      }
  
      return result.toString();
    }
  
    String writeHorizontalRule(HorizontalRule hRule, [String unorderedListChar = "*"]) {
      if (unorderedListChar == "-") {
        return '*' * 10;
      }
      return '-' * 10;
    }
  
    String writePara(Para para) {
      return writeInlines(para.contents) + "\n";
    }
  
    String writeBlockquote(Blockquote blockquote) {
      String contents = writeBlocks(blockquote.contents);
      if (contents.endsWith('\n')) {
        contents = contents.substring(0, contents.length - 1);
      }
      return contents.splitMapJoin("\n", onNonMatch: (String str) => "> $str") + "\n";
    }
  
    String writeHeader(Header header) {
      String inlines = writeInlines(header.contents);
      if (header is SetextHeader && header.level == 2) {
        return inlines + "\n" + (header.level == 1 ? '=' : '-') * inlines.length + "\n";
      }
      return "#" * header.level + " " + inlines + "\n";
    }
  
    String writeCodeBlock(CodeBlock codeBlock) {
      if (codeBlock is FencedCodeBlock) {
        String fence = (codeBlock.fenceType == FenceType.BacktickFence ? '`' : '~') * codeBlock.fenceSize;
        String result = fence;
        if (codeBlock.attributes is InfoString) {
          result += ' ${codeBlock.attributes.language}';
        }
        result += '\n${codeBlock.contents}${fence}\n';
        return result;
      }
      // Indented code block
      return codeBlock.contents.splitMapJoin("\n", onNonMatch: (str) => str == "" ? str : "    " + str);
    }
  
    String writeUnorderedList(UnorderedList list, {Block prevBlock: null}) {
      String result = "";
      if (prevBlock is UnorderedList && prevBlock.bulletType == list.bulletType) {
        result += "\n";
      }
  
      Iterable<String> items = list.items.map((ListItem listItem) {
        String pad;
        String contents = writeBlocks(listItem.contents, tight: list.tight, unorderedListChar: list.bulletType.char);
  
        return contents.splitMapJoin("\n", onNonMatch: (String str) {
          if (pad == null) {
            String marker = list.bulletType.char + " ";
            pad = " " * marker.length;
            return marker + str;
          } else if (str != "") {
            return pad + str;
          }
          return str;
        });
      });
  
      return result + items.join(list.tight ? "" : "\n");
    }
  
    String writeOrderedList(OrderedList list, {Block prevBlock: null}) {
      String result = "";
      if (prevBlock is OrderedList && prevBlock.indexSeparator == list.indexSeparator) {
        result += "\n";
      }
      int index = list.startIndex;
      Iterable<String> items = list.items.map((ListItem listItem) {
        String pad;
        String contents = writeBlocks(listItem.contents, tight: list.tight);
        return contents.splitMapJoin("\n", onNonMatch: (String str) {
          if (pad == null) {
            String marker = index.toString() + list.indexSeparator.char + " ";
            pad = " " * marker.length;
  
negative.code:           return pad + str;
          }
          return str;
        }));
        ++index;
      }
    }
  
  
    // Inlines
    void writeInlines(Iterable<Inline> inlines, {String prevEmph}) {
      if (inlines.length == 1 && prevEmph != null) {
        if (inlines.first is Emph) {
          writeEmph(inlines.first, delimiter: prevEmph == "*" ? "_" : "*");
          return;
        }
        if (inlines.first is Strong) {
          writeStrong(inlines.first, delimiter: prevEmph == "*" ? "_" : "*");
          return;
        }
      }
  
      Iterator<Inline> it = inlines.iterator;
      while(it.moveNext()) {
        Inline inline = it.current;
        if (inline is Str) {
          write(escapeString(inline.contents));
        } else if (inline is Space) {
          write(' ');
        } else if (inline is NonBreakableSpace) {
          write('&nbsp;');
        } else if (inline is LineBreak) {
          write('\\\n');
        } else if (inline is Emph) {
          writeEmph(inline);
        } else if (inline is Strong) {
          writeStrong(inline);
        } else if (inline is Link) {
          writeLink(inline);
        } else if (inline is Image) {
          writeImage(inline);
        } else if (inline is Code) {
          writeCodeInline(inline);
        } else if (inline is RawInline) {
          write(inline.contents);
        } else {
          throw new UnimplementedError(inline.toString());
        }
      }
    }
  
    RegExp escapedChars = new RegExp(r'[!\"#\$%&' r"'()*+,-./:;<=>?@\[\\\]^_`{|}~]");
    String escapeString(String str) => str.replaceAllMapped(escapedChars, (Match m) => r"\" + m.group(0));
  
    void writeCodeInline(Code code) {
      String fence = '`' * code.fenceSize;
      String contents = code.contents;
      if (contents == '') {
  