   getBufferCheckpoint: (purpose) ->
     @bufferCheckpointByPurpose?[purpose]
 
+  deleteBufferCheckpoint: (purpose) ->
+    if @bufferCheckpointByPurpose?
+      delete @bufferCheckpointByPurpose[purpose]
+
+  groupChangesSinceBufferCheckpoint: (purpose) ->
+    console.log "called groupChangesSinceCheckpoint #{purpose}"
+    if checkpoint = @getBufferCheckpoint(purpose)
+      console.log "checkpoint found: #{purpose}"
+      @editor.groupChangesSinceCheckpoint(checkpoint)
+    else
+      console.log "checkpoint NOT found: #{purpose}"
+    @deleteBufferCheckpoint(purpose)
+
   # Main
   execute: ->
     canMutate = true

      row = cursor.getBufferRow()
      switch @location
        when 'before'
          setTextAtBufferPosition(@editor, [row, 0], text)
        when 'after'
          unless isEndsWithNewLineForBufferRow(@editor, row)
            eol = getEndOfLineForBufferRow(@editor, row)
            range = setTextAtBufferPosition(@editor, eol, "\n" + text.trimRight())
            range.traverse([1, 0], [0, 0])
          else
            setTextAtBufferPosition(@editor, [row + 1, 0], text)
    else
      if @isMode('visual', 'linewise')
        unless selection.getBufferRange().end.column is 0

        @mutateSelection(selection, stopMutation)
      @restoreCursorPositionsIfNecessary()

    @groupChangesSinceBufferCheckpoint('undo')

    # Even though we fail to select target and fail to mutate,
    # we have to return to normal-mode from operator-pending or visual
