diff --git a/src/applications/differential/controller/DifferentialChangesetViewController.php b/src/applications/differential/controller/DifferentialChangesetViewController.php
index 437cd66d22..3a3e7883ca 100644
--- a/src/applications/differential/controller/DifferentialChangesetViewController.php
+++ b/src/applications/differential/controller/DifferentialChangesetViewController.php
@@ -1,345 +1,350 @@
 <?php
 
 final class DifferentialChangesetViewController extends DifferentialController {
 
   public function shouldRequireLogin() {
     return !$this->allowsAnonymousAccess();
   }
 
   public function processRequest() {
     $request = $this->getRequest();
 
     $author_phid = $request->getUser()->getPHID();
 
     $rendering_reference = $request->getStr('ref');
     $parts = explode('/', $rendering_reference);
     if (count($parts) == 2) {
       list($id, $vs) = $parts;
     } else {
       $id = $parts[0];
       $vs = 0;
     }
 
     $id = (int)$id;
     $vs = (int)$vs;
 
     $changeset = id(new DifferentialChangeset())->load($id);
     if (!$changeset) {
       return new Aphront404Response();
     }
 
     $view = $request->getStr('view');
     if ($view) {
       $changeset->attachHunks($changeset->loadHunks());
       $phid = idx($changeset->getMetadata(), "$view:binary-phid");
       if ($phid) {
         return id(new AphrontRedirectResponse())->setURI("/file/info/$phid/");
       }
       switch ($view) {
         case 'new':
           return $this->buildRawFileResponse($changeset, $is_new = true);
         case 'old':
           if ($vs && ($vs != -1)) {
             $vs_changeset = id(new DifferentialChangeset())->load($vs);
             if ($vs_changeset) {
               $vs_changeset->attachHunks($vs_changeset->loadHunks());
               return $this->buildRawFileResponse($vs_changeset, $is_new = true);
             }
           }
           return $this->buildRawFileResponse($changeset, $is_new = false);
         default:
           return new Aphront400Response();
       }
     }
 
     if ($vs && ($vs != -1)) {
       $vs_changeset = id(new DifferentialChangeset())->load($vs);
       if (!$vs_changeset) {
         return new Aphront404Response();
       }
     }
 
     if (!$vs) {
       $right = $changeset;
       $left  = null;
 
       $right_source = $right->getID();
       $right_new = true;
       $left_source = $right->getID();
       $left_new = false;
 
       $render_cache_key = $right->getID();
     } else if ($vs == -1) {
       $right = null;
       $left = $changeset;
 
       $right_source = $left->getID();
       $right_new = false;
       $left_source = $left->getID();
       $left_new = true;
 
       $render_cache_key = null;
     } else {
       $right = $changeset;
       $left = $vs_changeset;
 
       $right_source = $right->getID();
       $right_new = true;
       $left_source = $left->getID();
       $left_new = true;
 
       $render_cache_key = null;
     }
 
     if ($left) {
       $left->attachHunks($left->loadHunks());
     }
 
     if ($right) {
       $right->attachHunks($right->loadHunks());
     }
 
     if ($left) {
 
       $left_data = $left->makeNewFile();
       if ($right) {
         $right_data = $right->makeNewFile();
       } else {
         $right_data = $left->makeOldFile();
       }
 
       $engine = new PhabricatorDifferenceEngine();
       $synthetic = $engine->generateChangesetFromFileContent(
         $left_data,
         $right_data);
 
       $choice = clone nonempty($left, $right);
       $choice->attachHunks($synthetic->getHunks());
 
       $changeset = $choice;
     }
 
     $coverage = null;
     if ($right && $right->getDiffID()) {
       $unit = id(new DifferentialDiffProperty())->loadOneWhere(
         'diffID = %d AND name = %s',
         $right->getDiffID(),
         'arc:unit');
 
       if ($unit) {
         $coverage = array();
         foreach ($unit->getData() as $result) {
           $result_coverage = idx($result, 'coverage');
           if (!$result_coverage) {
             continue;
           }
           $file_coverage = idx($result_coverage, $right->getFileName());
           if (!$file_coverage) {
             continue;
           }
           $coverage[] = $file_coverage;
         }
 
         $coverage = ArcanistUnitTestResult::mergeCoverage($coverage);
       }
     }
 
     $spec = $request->getStr('range');
     list($range_s, $range_e, $mask) =
       DifferentialChangesetParser::parseRangeSpecification($spec);
 
     $parser = new DifferentialChangesetParser();
     $parser->setCoverage($coverage);
     $parser->setChangeset($changeset);
     $parser->setRenderingReference($rendering_reference);
     $parser->setRenderCacheKey($render_cache_key);
     $parser->setRightSideCommentMapping($right_source, $right_new);
     $parser->setLeftSideCommentMapping($left_source, $left_new);
     $parser->setWhitespaceMode($request->getStr('whitespace'));
 
+    if ($request->getStr('renderer') == '1up') {
+      $parser->setRenderer(new DifferentialChangesetOneUpRenderer());
+    }
+
+
     if ($left && $right) {
       $parser->setOriginals($left, $right);
     }
 
     // Load both left-side and right-side inline comments.
     $inlines = $this->loadInlineComments(
       array($left_source, $right_source),
       $author_phid);
 
     if ($left_new) {
       $inlines = array_merge(
         $inlines,
         $this->buildLintInlineComments($left));
     }
 
     if ($right_new) {
       $inlines = array_merge(
         $inlines,
         $this->buildLintInlineComments($right));
     }
 
     $phids = array();
     foreach ($inlines as $inline) {
       $parser->parseInlineComment($inline);
       if ($inline->getAuthorPHID()) {
         $phids[$inline->getAuthorPHID()] = true;
       }
     }
     $phids = array_keys($phids);
 
     $handles = $this->loadViewerHandles($phids);
     $parser->setHandles($handles);
 
     $engine = new PhabricatorMarkupEngine();
     $engine->setViewer($request->getUser());
 
     foreach ($inlines as $inline) {
       $engine->addObject(
         $inline,
         PhabricatorInlineCommentInterface::MARKUP_FIELD_BODY);
     }
 
     $engine->process();
     $parser->setMarkupEngine($engine);
 
     if ($request->isAjax()) {
       // TODO: This is sort of lazy, the effect is just to not render "Edit"
       // and "Reply" links on the "standalone view".
       $parser->setUser($request->getUser());
     }
 
     $output = $parser->render($range_s, $range_e, $mask);
 
     $mcov = $parser->renderModifiedCoverage();
 
     if ($request->isAjax()) {
       $coverage = array(
         'differential-mcoverage-'.md5($changeset->getFilename()) => $mcov,
       );
 
       return id(new PhabricatorChangesetResponse())
         ->setRenderedChangeset($output)
         ->setCoverage($coverage);
     }
 
     Javelin::initBehavior('differential-show-more', array(
       'uri' => '/differential/changeset/',
       'whitespace' => $request->getStr('whitespace'),
     ));
 
     Javelin::initBehavior('differential-comment-jump', array());
 
     $detail = new DifferentialChangesetDetailView();
     $detail->setChangeset($changeset);
     $detail->appendChild($output);
     $detail->setVsChangesetID($left_source);
 
     $panel = new DifferentialPrimaryPaneView();
     $panel->appendChild(phutil_render_tag('div',
       array(
         'class' => 'differential-review-stage',
         'id'    => 'differential-review-stage',
       ), $detail->render())
     );
 
     return $this->buildStandardPageResponse(
       array(
         $panel
       ),
       array(
         'title' => 'Changeset View',
       ));
   }
 
   private function loadInlineComments(array $changeset_ids, $author_phid) {
     $changeset_ids = array_unique(array_filter($changeset_ids));
     if (!$changeset_ids) {
       return;
     }
 
     return id(new DifferentialInlineComment())->loadAllWhere(
       'changesetID IN (%Ld) AND (commentID IS NOT NULL OR authorPHID = %s)',
       $changeset_ids,
       $author_phid);
   }
 
   private function buildRawFileResponse(
     DifferentialChangeset $changeset,
     $is_new) {
 
     if ($is_new) {
       $key = 'raw:new:phid';
     } else {
       $key = 'raw:old:phid';
     }
 
     $metadata = $changeset->getMetadata();
 
     $file = null;
     $phid = idx($metadata, $key);
     if ($phid) {
       $file = id(new PhabricatorFile())->loadOneWhere(
         'phid = %s',
         $phid);
     }
 
     if (!$file) {
       // This is just building a cache of the changeset content in the file
       // tool, and is safe to run on a read pathway.
       $unguard = AphrontWriteGuard::beginScopedUnguardedWrites();
 
       if ($is_new) {
         $data = $changeset->makeNewFile();
       } else {
         $data = $changeset->makeOldFile();
       }
 
       $file = PhabricatorFile::newFromFileData(
         $data,
         array(
           'name'      => $changeset->getFilename(),
           'mime-type' => 'text/plain',
         ));
 
       $metadata[$key] = $file->getPHID();
       $changeset->setMetadata($metadata);
       $changeset->save();
 
       unset($unguard);
     }
 
     return id(new AphrontRedirectResponse())
       ->setURI($file->getBestURI());
   }
 
   private function buildLintInlineComments($changeset) {
     $lint = id(new DifferentialDiffProperty())->loadOneWhere(
       'diffID = %d AND name = %s',
       $changeset->getDiffID(),
       'arc:lint');
     if (!$lint) {
       return array();
     }
     $lint = $lint->getData();
 
     $inlines = array();
     foreach ($lint as $msg) {
       if ($msg['path'] != $changeset->getFilename()) {
         continue;
       }
       $inline = new DifferentialInlineComment();
       $inline->setChangesetID($changeset->getID());
       $inline->setIsNewFile(true);
       $inline->setSyntheticAuthor('Lint: '.$msg['name']);
       $inline->setLineNumber($msg['line']);
       $inline->setLineLength(0);
 
       $inline->setContent('%%%'.$msg['description'].'%%%');
 
       $inlines[] = $inline;
     }
 
     return $inlines;
   }
 
 }
diff --git a/src/applications/differential/render/DifferentialChangesetOneUpRenderer.php b/src/applications/differential/render/DifferentialChangesetOneUpRenderer.php
index 919f753231..5ab1fa07b2 100644
--- a/src/applications/differential/render/DifferentialChangesetOneUpRenderer.php
+++ b/src/applications/differential/render/DifferentialChangesetOneUpRenderer.php
@@ -1,28 +1,74 @@
 <?php
 
 final class DifferentialChangesetOneUpRenderer
   extends DifferentialChangesetHTMLRenderer {
 
   public function isOneUpRenderer() {
     return true;
   }
 
-  public function renderChangesetTable($contents) {
-    throw new Exception("Not implemented!");
-  }
-
   public function renderTextChange(
     $range_start,
     $range_len,
     $rows) {
-    throw new Exception("Not implemented!");
+
+    $primitives = $this->buildPrimitives($range_start, $range_len);
+
+    $out = array();
+    foreach ($primitives as $p) {
+      $type = $p['type'];
+      switch ($type) {
+        case 'old':
+        case 'new':
+          $out[] = '<tr>';
+          if ($type == 'old') {
+            if ($p['htype']) {
+              $class = 'left old';
+            } else {
+              $class = 'left';
+            }
+            $out[] = '<th>'.$p['line'].'</th>';
+            $out[] = '<th></th>';
+            $out[] = '<td class="'.$class.'">'.$p['render'].'</td>';
+          } else if ($type == 'new') {
+            if ($p['htype']) {
+              $class = 'right new';
+              $out[] = '<th />';
+            } else {
+              $class = 'right';
+              $out[] = '<th>'.$p['oline'].'</th>';
+            }
+            $out[] = '<th>'.$p['line'].'</th>';
+            $out[] = '<td class="'.$class.'">'.$p['render'].'</td>';
+          }
+          $out[] = '</tr>';
+          break;
+        case 'inline':
+          $out[] = '<tr><th /><th />';
+          $out[] = '<td style="background: #ddd; color: #888;">';
+
+          $out[] = 'INLINE COMMENT<br />';
+          $out[] = phutil_escape_html($p['comment']->getContent());
+
+          $out[] = '</td></tr>';
+          break;
+        default:
+          $out[] = '<tr><th /><th /><td>'.$type.'</td></tr>';
+          break;
+      }
+    }
+
+    if ($out) {
+      return $this->wrapChangeInTable(implode('', $out));
+    }
+    return null;
   }
 
   public function renderFileChange($old_file = null,
                                    $new_file = null,
                                    $id = 0,
                                    $vs = 0) {
     throw new Exception("Not implemented!");
   }
 
 }
diff --git a/src/applications/differential/render/DifferentialChangesetRenderer.php b/src/applications/differential/render/DifferentialChangesetRenderer.php
index 6424d96da9..b592e988eb 100644
--- a/src/applications/differential/render/DifferentialChangesetRenderer.php
+++ b/src/applications/differential/render/DifferentialChangesetRenderer.php
@@ -1,495 +1,501 @@
 <?php
 
 abstract class DifferentialChangesetRenderer {
 
   private $user;
   private $changeset;
   private $renderingReference;
   private $renderPropertyChangeHeader;
   private $hunkStartLines;
   private $oldLines;
   private $newLines;
   private $oldComments;
   private $newComments;
   private $oldChangesetID;
   private $newChangesetID;
   private $oldAttachesToNewFile;
   private $newAttachesToNewFile;
   private $highlightOld = array();
   private $highlightNew = array();
   private $codeCoverage;
   private $handles;
   private $markupEngine;
   private $oldRender;
   private $newRender;
   private $originalOld;
   private $originalNew;
   private $gaps;
   private $mask;
   private $depths;
 
   public function setDepths($depths) {
     $this->depths = $depths;
     return $this;
   }
   protected function getDepths() {
     return $this->depths;
   }
 
   public function setMask($mask) {
     $this->mask = $mask;
     return $this;
   }
   protected function getMask() {
     return $this->mask;
   }
 
   public function setGaps($gaps) {
     $this->gaps = $gaps;
     return $this;
   }
   protected function getGaps() {
     return $this->gaps;
   }
 
   public function setOriginalNew($original_new) {
     $this->originalNew = $original_new;
     return $this;
   }
   protected function getOriginalNew() {
     return $this->originalNew;
   }
 
   public function setOriginalOld($original_old) {
     $this->originalOld = $original_old;
     return $this;
   }
   protected function getOriginalOld() {
     return $this->originalOld;
   }
 
   public function setNewRender($new_render) {
     $this->newRender = $new_render;
     return $this;
   }
   protected function getNewRender() {
     return $this->newRender;
   }
 
   public function setOldRender($old_render) {
     $this->oldRender = $old_render;
     return $this;
   }
   protected function getOldRender() {
     return $this->oldRender;
   }
 
   public function setMarkupEngine(PhabricatorMarkupEngine $markup_engine) {
     $this->markupEngine = $markup_engine;
     return $this;
   }
   public function getMarkupEngine() {
     return $this->markupEngine;
   }
 
   public function setHandles(array $handles) {
     assert_instances_of($handles, 'PhabricatorObjectHandle');
     $this->handles = $handles;
     return $this;
   }
   protected function getHandles() {
     return $this->handles;
   }
 
   public function setCodeCoverage($code_coverage) {
     $this->codeCoverage = $code_coverage;
     return $this;
   }
   protected function getCodeCoverage() {
     return $this->codeCoverage;
   }
 
   public function setHighlightNew($highlight_new) {
     $this->highlightNew = $highlight_new;
     return $this;
   }
   protected function getHighlightNew() {
     return $this->highlightNew;
   }
 
   public function setHighlightOld($highlight_old) {
     $this->highlightOld = $highlight_old;
     return $this;
   }
   protected function getHighlightOld() {
     return $this->highlightOld;
   }
 
   public function setNewAttachesToNewFile($attaches) {
     $this->newAttachesToNewFile = $attaches;
     return $this;
   }
   protected function getNewAttachesToNewFile() {
     return $this->newAttachesToNewFile;
   }
 
   public function setOldAttachesToNewFile($attaches) {
     $this->oldAttachesToNewFile = $attaches;
     return $this;
   }
   protected function getOldAttachesToNewFile() {
     return $this->oldAttachesToNewFile;
   }
 
   public function setNewChangesetID($new_changeset_id) {
     $this->newChangesetID = $new_changeset_id;
     return $this;
   }
   protected function getNewChangesetID() {
     return $this->newChangesetID;
   }
 
   public function setOldChangesetID($old_changeset_id) {
     $this->oldChangesetID = $old_changeset_id;
     return $this;
   }
   protected function getOldChangesetID() {
     return $this->oldChangesetID;
   }
 
   public function setNewComments(array $new_comments) {
     foreach ($new_comments as $line_number => $comments) {
       assert_instances_of($comments, 'PhabricatorInlineCommentInterface');
     }
     $this->newComments = $new_comments;
     return $this;
   }
   protected function getNewComments() {
     return $this->newComments;
   }
 
   public function setOldComments(array $old_comments) {
     foreach ($old_comments as $line_number => $comments) {
       assert_instances_of($comments, 'PhabricatorInlineCommentInterface');
     }
     $this->oldComments = $old_comments;
     return $this;
   }
   protected function getOldComments() {
     return $this->oldComments;
   }
 
   public function setNewLines(array $new_lines) {
     $this->newLines = $new_lines;
     return $this;
   }
   protected function getNewLines() {
     return $this->newLines;
   }
 
   public function setOldLines(array $old_lines) {
     $this->oldLines = $old_lines;
     return $this;
   }
   protected function getOldLines() {
     return $this->oldLines;
   }
 
   public function setHunkStartLines(array $hunk_start_lines) {
     $this->hunkStartLines = $hunk_start_lines;
     return $this;
   }
 
   protected function getHunkStartLines() {
     return $this->hunkStartLines;
   }
 
   public function setUser(PhabricatorUser $user) {
     $this->user = $user;
     return $this;
   }
   protected function getUser() {
     return $this->user;
   }
 
   public function setChangeset(DifferentialChangeset $changeset) {
     $this->changeset = $changeset;
     return $this;
   }
   protected function getChangeset() {
     return $this->changeset;
   }
 
   public function setRenderingReference($rendering_reference) {
     $this->renderingReference = $rendering_reference;
     return $this;
   }
   protected function getRenderingReference() {
     return $this->renderingReference;
   }
 
   public function setRenderPropertyChangeHeader($should_render) {
     $this->renderPropertyChangeHeader = $should_render;
     return $this;
   }
   private function shouldRenderPropertyChangeHeader() {
     return $this->renderPropertyChangeHeader;
   }
 
   final public function renderChangesetTable($content) {
     $props = null;
     if ($this->shouldRenderPropertyChangeHeader()) {
       $props = $this->renderPropertyChangeHeader();
     }
 
     $force = (!$content && !$props);
     $notice = $this->renderChangeTypeHeader($force);
 
     $result = $notice.$props.$content;
 
     // TODO: Let the user customize their tab width / display style.
     // TODO: We should possibly post-process "\r" as well.
     // TODO: Both these steps should happen earlier.
     $result = str_replace("\t", '  ', $result);
 
     return $result;
   }
 
   abstract public function isOneUpRenderer();
   abstract public function renderTextChange(
     $range_start,
     $range_len,
     $rows
   );
   abstract public function renderFileChange(
     $old = null,
     $new = null,
     $id = 0,
     $vs = 0
   );
 
   abstract protected function renderChangeTypeHeader($force);
 
   protected function didRenderChangesetTableContents($contents) {
     return $contents;
   }
 
   /**
    * Render a "shield" over the diff, with a message like "This file is
    * generated and does not need to be reviewed." or "This file was completely
    * deleted." This UI element hides unimportant text so the reviewer doesn't
    * need to scroll past it.
    *
    * The shield includes a link to view the underlying content. This link
    * may force certain rendering modes when the link is clicked:
    *
    *    - `"default"`: Render the diff normally, as though it was not
    *      shielded. This is the default and appropriate if the underlying
    *      diff is a normal change, but was hidden for reasons of not being
    *      important (e.g., generated code).
    *    - `"text"`: Force the text to be shown. This is probably only relevant
    *      when a file is not changed.
    *    - `"whitespace"`: Force the text to be shown, and the diff to be
    *      rendered with all whitespace shown. This is probably only relevant
    *      when a file is changed only by altering whitespace.
    *    - `"none"`: Don't show the link (e.g., text not available).
    *
    * @param   string        Message explaining why the diff is hidden.
    * @param   string|null   Force mode, see above.
    * @return  string        Shield markup.
    */
   abstract protected function renderShield($message, $force = 'default');
 
   abstract protected function renderPropertyChangeHeader();
 
   protected function renderInlineComment(
     PhabricatorInlineCommentInterface $comment,
     $on_right = false) {
 
     $user = $this->getUser();
     $edit = $user &&
             ($comment->getAuthorPHID() == $user->getPHID()) &&
             ($comment->isDraft());
     $allow_reply = (bool)$user;
 
     return id(new DifferentialInlineCommentView())
       ->setInlineComment($comment)
       ->setOnRight($on_right)
       ->setHandles($this->getHandles())
       ->setMarkupEngine($this->getMarkupEngine())
       ->setEditable($edit)
       ->setAllowReply($allow_reply)
       ->render();
   }
 
   protected function buildPrimitives($range_start, $range_len) {
     $primitives = array();
 
     $hunk_starts = $this->getHunkStartLines();
 
     $mask = $this->getMask();
     $gaps = $this->getGaps();
 
     $old = $this->getOldLines();
     $new = $this->getNewLines();
     $old_render = $this->getOldRender();
     $new_render = $this->getNewRender();
     $old_comments = $this->getOldComments();
     $new_comments = $this->getNewComments();
 
     $size = count($old);
     for ($ii = $range_start; $ii < $range_start + $range_len; $ii++) {
       if (empty($mask[$ii])) {
         list($top, $len) = array_pop($gaps);
         $primitives[] = array(
           'type' => 'context',
           'top' => $top,
           'len' => $len,
         );
 
         $ii += ($len - 1);
         continue;
       }
 
       $ospec = array(
         'type' => 'old',
         'htype' => null,
         'cursor' => $ii,
         'line' => null,
+        'oline' => null,
         'render' => null,
       );
 
       $nspec = array(
         'type' => 'new',
         'htype' => null,
         'cursor' => $ii,
         'line' => null,
+        'oline' => null,
         'render' => null,
         'copy' => null,
         'coverage' => null,
       );
 
       if (isset($old[$ii])) {
-        $ospec['line'] = $old[$ii]['line'];
+        $ospec['line'] = (int)$old[$ii]['line'];
+        $nspec['oline'] = (int)$old[$ii]['line'];
         $ospec['htype'] = $old[$ii]['type'];
         if (isset($old_render[$ii])) {
           $ospec['render'] = $old_render[$ii];
         }
       }
 
       if (isset($new[$ii])) {
-        $nspec['line'] = $new[$ii]['line'];
+        $nspec['line'] = (int)$new[$ii]['line'];
+        $ospec['oline'] = (int)$new[$ii]['line'];
         $nspec['htype'] = $new[$ii]['type'];
         if (isset($new_render[$ii])) {
           $nspec['render'] = $new_render[$ii];
         }
       }
 
       if (isset($hunk_starts[$ospec['line']])) {
         $primitives[] = array(
           'type' => 'no-context',
         );
       }
 
       $primitives[] = $ospec;
       $primitives[] = $nspec;
 
       if ($ospec['line'] !== null && isset($old_comments[$ospec['line']])) {
         foreach ($old_comments[$ospec['line']] as $comment) {
           $primitives[] = array(
             'type' => 'inline',
             'comment' => $comment,
             'right' => false,
           );
         }
       }
 
       if ($nspec['line'] !== null && isset($new_comments[$nspec['line']])) {
         foreach ($new_comments[$nspec['line']] as $comment) {
           $primitives[] = array(
             'type' => 'inline',
-            'comment' => 'comment',
+            'comment' => $comment,
             'right' => true,
           );
         }
       }
 
       if ($hunk_starts && ($ii == $size - 1)) {
         $primitives[] = array(
           'type' => 'no-context',
         );
       }
     }
 
     if ($this->isOneUpRenderer()) {
       $primitives = $this->processPrimitivesForOneUp($primitives);
     }
 
     return $primitives;
   }
 
   private function processPrimitivesForOneUp(array $primitives) {
     // Primitives come out of buildPrimitives() in two-up format, because it
     // is the most general, flexible format. To put them into one-up format,
     // we need to filter and reorder them. In particular:
     //
     //   - We discard unchanged lines in the old file; in one-up format, we
     //     render them only once.
     //   - We group contiguous blocks of old-modified and new-modified lines, so
     //     they render in "block of old, block of new" order instead of
     //     alternating old and new lines.
 
     $out = array();
 
     $old_buf = array();
     $new_buf = array();
     foreach ($primitives as $primitive) {
       $type = $primitive['type'];
+
       if ($type == 'old') {
         if (!$primitive['htype']) {
           // This is a line which appears in both the old file and the new
           // file, or the spacer corresponding to a line added in the new file.
           // Ignore it when rendering a one-up diff.
           continue;
         }
         if ($new_buf) {
           $out[] = $new_buf;
           $new_buf = array();
         }
         $old_buf[] = $primitive;
       } else if ($type == 'new') {
         if ($primitive['line'] === null) {
           // This is an empty spacer corresponding to a line removed from the
           // old file. Ignore it when rendering a one-up diff.
           continue;
         }
         if ($old_buf) {
           $out[] = $old_buf;
           $old_buf = array();
         }
         $new_buf[] = $primitive;
       } else if ($type == 'context' || $type == 'no-context') {
         $out[] = $old_buf;
         $out[] = $new_buf;
         $old_buf = array();
         $new_buf = array();
         $out[] = array($primitive);
       } else if ($type == 'inline') {
-        if ($primitive['right']) {
-          $new_buf[] = $primitive;
-        } else {
-          $old_buf[] = $primitive;
-        }
+        $out[] = $old_buf;
+        $out[] = $new_buf;
+        $old_buf = array();
+        $new_buf = array();
+
+        $out[] = array($primitive);
       } else {
         throw new Exception("Unknown primitive type '{$primitive}'!");
       }
     }
 
     $out[] = $old_buf;
     $out[] = $new_buf;
     $out = array_mergev($out);
 
     return $out;
   }
 
 }
diff --git a/webroot/rsrc/js/application/core/behavior-device.js b/webroot/rsrc/js/application/core/behavior-device.js
index 40eca13e9c..df6d9c6cd5 100644
--- a/webroot/rsrc/js/application/core/behavior-device.js
+++ b/webroot/rsrc/js/application/core/behavior-device.js
@@ -1,49 +1,53 @@
 /**
  * @provides javelin-behavior-device
  * @requires javelin-behavior
  *           javelin-stratcom
  *           javelin-dom
  *           javelin-vector
  *           javelin-install
  */
 
 JX.install('Device', {
   statics : {
     _device : null,
-    getDevice : function() {
-      return JX.Device._device;
-    }
-  }
-});
+    recalculate: function() {
+      var v = JX.Vector.getViewport();
+      var self = JX.Device;
 
-JX.behavior('device', function() {
+      var device = 'desktop';
+      if (v.x <= 768) {
+        device = 'tablet';
+      }
+      if (v.x <= 480) {
+        device = 'phone';
+      }
 
-  function onresize() {
-    var v = JX.Vector.getViewport();
+      if (device == self._device) {
+        return;
+      }
 
-    var device = 'desktop';
-    if (v.x <= 768) {
-      device = 'tablet';
-    }
-    if (v.x <= 480) {
-      device = 'phone';
-    }
+      self._device = device;
 
-    if (device == JX.Device.getDevice()) {
-      return;
-    }
-
-    JX.Device._device = device;
+      var e = document.body;
+      JX.DOM.alterClass(e, 'device-phone', (device == 'phone'));
+      JX.DOM.alterClass(e, 'device-tablet', (device == 'tablet'));
+      JX.DOM.alterClass(e, 'device-desktop', (device == 'desktop'));
+      JX.DOM.alterClass(e, 'device', (device != 'desktop'));
 
-    var e = document.body;
-    JX.DOM.alterClass(e, 'device-phone', (device == 'phone'));
-    JX.DOM.alterClass(e, 'device-tablet', (device == 'tablet'));
-    JX.DOM.alterClass(e, 'device-desktop', (device == 'desktop'));
-    JX.DOM.alterClass(e, 'device', (device != 'desktop'));
+      JX.Stratcom.invoke('phabricator-device-change', null, device);
+    },
 
-    JX.Stratcom.invoke('phabricator-device-change', null, device);
+    getDevice : function() {
+      var self = JX.Device;
+      if (self._device === null) {
+        self.recalculate();
+      }
+      return self._device;
+    }
   }
+});
 
-  JX.Stratcom.listen('resize', null, onresize);
-  onresize();
+JX.behavior('device', function() {
+  JX.Stratcom.listen('resize', null, JX.Device.recalculate);
+  JX.Device.recalculate();
 });
diff --git a/webroot/rsrc/js/application/differential/behavior-populate.js b/webroot/rsrc/js/application/differential/behavior-populate.js
index 44b5786058..79c9732fa8 100644
--- a/webroot/rsrc/js/application/differential/behavior-populate.js
+++ b/webroot/rsrc/js/application/differential/behavior-populate.js
@@ -1,125 +1,132 @@
 /**
  * @provides javelin-behavior-differential-populate
  * @requires javelin-behavior
  *           javelin-workflow
  *           javelin-util
  *           javelin-dom
  *           javelin-stratcom
+ *           javelin-behavior-device
  *           phabricator-tooltip
  */
 
 JX.behavior('differential-populate', function(config) {
 
   function onresponse(target, response) {
     JX.DOM.replace(JX.$(target), JX.$H(response.changeset));
     if (response.coverage) {
       for (var k in response.coverage) {
         try {
           JX.DOM.replace(JX.$(k), JX.$H(response.coverage[k]));
         } catch (ignored) {
           // Not terribly important.
         }
       }
     }
   }
 
+  // NOTE: If you load the page at one device resolution and then resize to
+  // a different one we don't re-render the diffs, because it's a complicated
+  // mess and you could lose inline comments, cursor positions, etc.
+  var renderer = (JX.Device.getDevice() == 'desktop') ? '2up' : '1up';
+
   for (var k in config.registry) {
     var data = {
       ref : config.registry[k],
-      whitespace: config.whitespace
+      whitespace: config.whitespace,
+      renderer: renderer
     };
 
     new JX.Workflow(config.uri, data)
       .setHandler(JX.bind(null, onresponse, k))
       .start();
   }
 
   var highlighted = null;
   var highlight_class = null;
 
   JX.Stratcom.listen(
     'click',
     'differential-load',
     function(e) {
       var meta = e.getNodeData('differential-load');
       var diff;
       try {
         diff = JX.$(meta.id);
       } catch (e) {
         // Already loaded.
       }
       if (diff) {
         JX.DOM.setContent(
           diff,
           JX.$H('<div class="differential-loading">Loading...</div>'));
         var data = {
           ref : meta.ref,
           whitespace : config.whitespace
         };
         new JX.Workflow(config.uri, data)
           .setHandler(JX.bind(null, onresponse, meta.id))
           .start();
       }
       if (meta.kill) {
         e.kill();
       }
     });
 
   JX.Stratcom.listen(
     ['mouseover', 'mouseout'],
     ['differential-changeset', 'tag:td'],
     function(e) {
       var t = e.getTarget();
 
       // NOTE: Using className is not best practice, but the diff UI is perf
       // sensitive.
       if (!t.className.match(/cov|copy/)) {
         return;
       }
 
       if (e.getType() == 'mouseout') {
         JX.Tooltip.hide();
         if (highlighted) {
           JX.DOM.alterClass(highlighted, highlight_class, false);
           highlighted = null;
         }
       } else {
         highlight_class = null;
         var msg;
         var align = 'W';
         var sibling = 'previousSibling';
         var width = 120;
         if (t.className.match(/cov-C/)) {
           msg = 'Covered';
           highlight_class = 'source-cov-C';
         } else if (t.className.match(/cov-U/)) {
           msg = 'Not Covered';
           highlight_class = 'source-cov-U';
         } else if (t.className.match(/cov-N/)) {
           msg = 'Not Executable';
           highlight_class = 'source-cov-N';
         } else {
           var match = /new-copy|new-move/.exec(t.className);
           if (match) {
             sibling = 'nextSibling';
             width = 500;
             msg = JX.Stratcom.getData(t).msg;
             highlight_class = match[0];
           }
         }
 
         if (msg) {
           JX.Tooltip.show(t, width, align, msg);
         }
 
         if (highlight_class) {
           highlighted = t[sibling];
           JX.DOM.alterClass(highlighted, highlight_class, true);
         }
       }
 
     });
 
 
 
 });