 
   cm.on 'beforeChange', (_, c) ->
     if c.origin != 'D'
-      if (l = c.from.line) != c.to.line || c.text.length > 1
-        c.cancel(); $.alert 'Cannot make changes across multiple lines in session', 'Error'
-      else
-        mod[l] ?= cm.getLine l; cm.addLineClass l, 'background', 'modified'
+      l0 = c.from.line; l1 = c.to.line; m = l1 - l0 + 1; n = c.text.length
+      if n < m then c.update c.from, c.to, c.text.concat(for [0...m - n] then ''); n = m # pad shrinking changes with empty lines
+      if m < n then h = dirty; dirty = {}; for x, y of h then dirty[x + (n - m) * (x > l1)] = y
+      l = l0
+      while l <= l1    then dirty[l] ?= cm.getLine l; l++
+      while l < l0 + n then dirty[l] = 0;             l++
+    return
+
+  cm.on 'change', (_, c) ->
+    if c.origin != 'D'
+      l0 = c.from.line; l1 = c.to.line; m = l1 - l0 + 1; n = c.text.length
+      for l of dirty then cm.addLineClass +l, 'background', 'modified'
     return
 
   add: (s) ->

autocompletion = require './autocompletion'
module.exports = (e, opts = {}) ->

  # keep track of which lines have been modified and preserve the original content
  mod = {} # line number -> original content

  hist = ['']
  histIndex = 0
  histAdd = (lines) -> hist[0] = ''; hist[1...1] = lines; histIndex = 0; return

    cm.setOption 'readOnly', false
    cm.setOption 'cursorHeight', 1
    l = cm.lineCount() - 1
    if (why == 1 && !dirty[l]?) || why !in [1, 4]
      cm.replaceRange '      ', {line: l, ch: 0}, {line: l, ch: cm.getLine(l).length}, 'D'
    cm.setCursor l, cm.getLine(l).length
    return
