   private var keymanWeb: KeymanWebViewController
   
   private var swallowBackspaceTextChange: Bool = false
+  private var swallowContextChangeCount: Int = 0
 
   open class var isPortrait: Bool {
     return UIScreen.main.bounds.width < UIScreen.main.bounds.height

  }
  
  func resetContext() {
    keymanWeb.resetContext()
  }

  internal func setSentryState(enabled: Bool) {

      textDocumentProxy.insertText(newText)
    }

    // Should never change throughout the next loop; right-deletions only!
    let oldLeftContext = textDocumentProxy.documentContextBeforeInput ?? ""
    var pointsToDelete = numCharsToRightDelete
    while pointsToDelete > 0 {
      let oldContext = textDocumentProxy.documentContextAfterInput ?? ""
      self.swallowContextChangeCount += 1
      // Asynchronously triggers a context change.
      textDocumentProxy.adjustTextPosition(byCharacterOffset: 1)
      let newContext = textDocumentProxy.documentContextAfterInput ?? ""

      var failsafeCount = 0
      while (textDocumentProxy.documentContextBeforeInput ?? "" != oldLeftContext)
            && failsafeCount < numCharsToRightDelete { // mild breakage > locked kbd
        // While adjustTextPosition makes cluster-based jumps...
        // deleteBackward does not.  So, we 'force' it.
        textDocumentProxy.deleteBackward()
        failsafeCount += 1
      }

      // Determine the removed codepoint count.  Also, find the index
      // of the first character that shouldn't be removed, if possible.
      let unitsDeleted = oldContext.utf16.count - newContext.utf16.count
      var pointsDeleted = 0

      // Our current index within the deleted context.
      var deletedIndex = oldContext.utf16.startIndex
      // Marks the first index we wish to NOT remove.
      var remnantIndex: String.Index? = nil
      // Marks the first index that was NOT removed by deleteBackward.
      let undeletedStartIndex = oldContext.utf16.index(deletedIndex, offsetBy: unitsDeleted)

      while deletedIndex < undeletedStartIndex {
        if(InputViewController.isSurrogate(oldContext.utf16[deletedIndex])) {
          // Check - is it truly a surrogate pair?
          let pairedIndex = oldContext.utf16.index(after: deletedIndex)
          if(InputViewController.isSurrogate(oldContext.utf16[pairedIndex])) {
            // If so, pre-emptively increase the index - the pair will count
            // as a single character as a result.
            deletedIndex = pairedIndex
          }
        }
        pointsDeleted += 1
        deletedIndex = oldContext.utf16.index(after: deletedIndex)

        // Intended end of deletion found!
        if pointsDeleted == pointsToDelete {
          remnantIndex = deletedIndex
          break // There's nothing to be gained by further loop iterations.
        }
      }

      if let remnantIndex = remnantIndex, remnantIndex < undeletedStartIndex {
        // We need to restore some of the deleted text!
        let remnant = String(oldContext.utf16[remnantIndex..<undeletedStartIndex]) ?? ""
        textDocumentProxy.insertText(remnant)

        // Must place the caret back in its correct position!
        // Unfortunately, Apple's adjustTextPosition is definitely cluster-based.

        var leftContext = textDocumentProxy.documentContextBeforeInput ?? ""
        // Ideally, we want to stop at the exact position, but the lack of
        // granularity on adjustTextPosition may not cooperate with that goal.
        while(leftContext.count > oldLeftContext.count) {
          textDocumentProxy.adjustTextPosition(byCharacterOffset: -remnant.count)
          leftContext = textDocumentProxy.documentContextBeforeInput ?? ""
        }

        // FAILSAFE, non-ideal behavior below.
        //
        // It's probably (?) better to keep the caret at the end of a cluster that
        // previously started before the caret, even if right-deletions add new
        // characters to the cluster.  I think.
        //
        // Khmer example:  if right-deletes leave a joeung-S that can attach
        // to a main consonant to the left of my cursor, it will 'snap' into
        // a cluster.  It'd be more natural to have that cluster on the left,
        // so I can add more chars to the cluster if desired.
        //
        // It's also easier to immediately backspace the extra chars if needed
        // this way, rather than forcing a caret reposition.
        if(leftContext.count < oldLeftContext.count) {
          log.debug("Could not reposition caret perfectly after a right-delete!")
          textDocumentProxy.adjustTextPosition(byCharacterOffset: 1)
        }
      }

      // We've handled a set of code points; make sure we mark our progress!
      pointsToDelete -= pointsDeleted
    }
  }

