function updateDisplayInner(cm, changes, viewPort) {
    var display = cm.display, doc = cm.view.doc;
    if (!display.wrapper.clientWidth) {
      display.showingFrom = display.showingTo = display.viewOffset = 0;
      return;
    }

    // Compute the new visible window
    // If scrollTop is specified, use that to determine which lines
    // to render instead of the current scrollbar position.
    var visible = visibleLines(display, doc, viewPort);
    // Bail out if the visible area is already rendered and nothing changed.
    if (changes !== true && changes.length == 0 &&
        visible.from > display.showingFrom && visible.to < display.showingTo)
      return;

    if (changes && maybeUpdateLineNumberWidth(cm))
      changes = true;
    display.sizer.style.marginLeft = display.scrollbarH.style.left = display.gutters.offsetWidth + "px";

    // When merged lines are present, the line that needs to be
    // redrawn might not be the one that was changed.
    if (changes !== true && sawCollapsedSpans)
      for (var i = 0; i < changes.length; ++i) {
        var ch = changes[i], merged;
        while (merged = collapsedSpanAtStart(getLine(doc, ch.from))) {
          var from = merged.find().from.line;
          if (ch.diff) ch.diff -= ch.from - from;
          ch.from = from;
        }
      }

    // Used to determine which lines need their line numbers updated
    var positionsChangedFrom = changes === true ? 0 : Infinity;
    if (cm.options.lineNumbers && changes && changes !== true)
      for (var i = 0; i < changes.length; ++i)
        if (changes[i].diff) { positionsChangedFrom = changes[i].from; break; }

    var from = Math.max(visible.from - cm.options.viewportMargin, 0);
    var to = Math.min(doc.size, visible.to + cm.options.viewportMargin);
    if (display.showingFrom < from && from - display.showingFrom < 20) from = display.showingFrom;
    if (display.showingTo > to && display.showingTo - to < 20) to = Math.min(doc.size, display.showingTo);
    if (sawCollapsedSpans) {
      from = lineNo(visualLine(doc, getLine(doc, from)));
      while (to < doc.size && lineIsHidden(getLine(doc, to))) ++to;
    }

    // Create a range of theoretically intact lines, and punch holes
    // in that using the change info.
    var intact = changes === true ? [] :
      computeIntact([{from: display.showingFrom, to: display.showingTo}], changes);
    // Clip off the parts that won't be visible
    var intactLines = 0;
    for (var i = 0; i < intact.length; ++i) {
      var range = intact[i];
      if (range.from < from) range.from = from;
      if (range.to > to) range.to = to;
      if (range.from >= range.to) intact.splice(i--, 1);
      else intactLines += range.to - range.from;
    }
    if (intactLines == to - from && from == display.showingFrom && to == display.showingTo)
      return;
    intact.sort(function(a, b) {return a.from - b.from;});

    if (intactLines < (to - from) * .7) display.lineDiv.style.display = "none";
    patchDisplay(cm, from, to, intact, positionsChangedFrom);
    display.lineDiv.style.display = "";

    var different = from != display.showingFrom || to != display.showingTo ||
      display.lastSizeC != display.wrapper.clientHeight;
    // This is just a bogus formula that detects when the editor is
    // resized or the font size changes.
    if (different) display.lastSizeC = display.wrapper.clientHeight;
    display.showingFrom = from; display.showingTo = to;
    startWorker(cm, 100);

    var prevBottom = display.lineDiv.offsetTop;
    for (var node = display.lineDiv.firstChild, height; node; node = node.nextSibling) if (node.lineObj) {
      if (ie_lt8) {
        var bot = node.offsetTop + node.offsetHeight;
        height = bot - prevBottom;
        prevBottom = bot;
      } else {
        var box = node.getBoundingClientRect();
        height = box.bottom - box.top;
      }
      var diff = node.lineObj.height - height;
      if (height < 2) height = textHeight(display);
      if (diff > .001 || diff < -.001)
        updateLineHeight(node.lineObj, height);
    }
    display.viewOffset = heightAtLine(cm, getLine(doc, from));
    // Position the mover div to align with the current virtual scroll position
    display.mover.style.top = display.viewOffset + "px";
    return true;
  }