     return if @destroyed
 
     modifiedStatus = @isModified()
-    composedChanges = @getChangesSinceCheckpoint(@lastStoppedChangingCheckpoint)
-    @lastStoppedChangingCheckpoint = @history.createCheckpoint({})
+    composedChanges = Patch.compose(@patchesSinceLastStoppedChangingEvent).getChanges()
     @emitter.emit(
       'did-stop-changing',
-      {changes: Object.freeze(composedChanges)}
+      {changes: Object.freeze(normalizePatchChanges(composedChanges))}
     )
+    @patchesSinceLastStoppedChangingEvent = []
     @emitModifiedStatusChanged(modifiedStatus)
 
   emitModifiedStatusChanged: (modifiedStatus) ->

      params?.text

    @emitter = new Emitter
    @id = crypto.randomBytes(16).toString('hex')
    @buffer = new NativeTextBuffer(text)
    @debouncedEmitDidStopChangingEvent = debounce(@emitDidStopChangingEvent.bind(this), @stoppedChangingDelay)
    @textDecorationLayers = new Set()
    maxUndoEntries = params?.maxUndoEntries ? @defaultMaxUndoEntries
    @history = new History(this, maxUndoEntries)
    @lastStoppedChangingCheckpoint = @history.createCheckpoint({})
    @nextMarkerLayerId = 0
    @nextDisplayLayerId = 0
    @defaultMarkerLayer = new MarkerLayer(this, String(@nextMarkerLayerId++))

    hunks = patch.getChanges()
    if hunks.length > 0
      @emitter.emit 'did-change-text', {changes: Object.freeze(normalizePatchChanges(hunks))}
      @patchesSinceLastStoppedChangingEvent.push(patch)
      @debouncedEmitDidStopChangingEvent()

  # Identifies if the buffer belongs to multiple editors.
