diff --git a/services/web/public/coffee/ide/editor/Document.coffee b/services/web/public/coffee/ide/editor/Document.coffee index 78431b8b6b..7b41efdc12 100644 --- a/services/web/public/coffee/ide/editor/Document.coffee +++ b/services/web/public/coffee/ide/editor/Document.coffee @@ -69,6 +69,12 @@ define [ getPendingOp: () -> @doc?.getPendingOp() + getRecentAck: () -> + @doc?.getRecentAck() + + getOpSize: (op) -> + @doc?.getOpSize(op) + hasBufferedOps: () -> @doc?.hasBufferedOps() @@ -143,24 +149,35 @@ define [ clearChaosMonkey: () -> clearTimeout @_cm + MAX_PENDING_OP_SIZE: 30 # pending ops bigger than this are always + # considered unsaved + pollSavedStatus: () -> # returns false if doc has ops waiting to be acknowledged or # sent that haven't changed since the last time we checked. # Otherwise returns true. inflightOp = @getInflightOp() pendingOp = @getPendingOp() + recentAck = @getRecentAck() + pendingOpSize = pendingOp? && @getOpSize(pendingOp) if !inflightOp? and !pendingOp? - # there's nothing going on + # there's nothing going on, this is ok. saved = true sl_console.log "[pollSavedStatus] no inflight or pending ops" else if inflightOp? and inflightOp == @oldInflightOp # The same inflight op has been sitting unacked since we - # last checked. + # last checked, this is bad. saved = false sl_console.log "[pollSavedStatus] inflight op is same as before" - else + else if pendingOp? and recentAck && pendingOpSize < @MAX_PENDING_OP_SIZE + # There is an op waiting to go to server but it is small and + # within the flushDelay, this is ok for now. saved = true - sl_console.log "[pollSavedStatus] assuming saved (inflightOp?: #{inflightOp?}, pendingOp?: #{pendingOp?})" + sl_console.log "[pollSavedStatus] pending op (small with recent ack) assume ok", pendingOp, pendingOpSize + else + # In any other situation, assume the document is unsaved. + saved = false + sl_console.log "[pollSavedStatus] assuming not saved (inflightOp?: #{inflightOp?}, pendingOp?: #{pendingOp?})" @oldInflightOp = inflightOp return saved diff --git a/services/web/public/coffee/ide/editor/ShareJsDoc.coffee b/services/web/public/coffee/ide/editor/ShareJsDoc.coffee index 27d325676c..8b9623c270 100644 --- a/services/web/public/coffee/ide/editor/ShareJsDoc.coffee +++ b/services/web/public/coffee/ide/editor/ShareJsDoc.coffee @@ -46,6 +46,7 @@ define [ @_doc.on "change", () => @trigger "change" @_doc.on "acknowledge", () => + @lastAcked = new Date() # note time of last ack from server @trigger "acknowledge" @_doc.on "remoteop", () => # As soon as we're working with a collaborator, start sending @@ -101,12 +102,26 @@ define [ @connection.id = @socket.socket.sessionid @_doc.autoOpen = false @_doc._connectionStateChanged(state) + @lastAcked = null # reset the last ack time when connection changes hasBufferedOps: () -> @_doc.inflightOp? or @_doc.pendingOp? getInflightOp: () -> @_doc.inflightOp getPendingOp: () -> @_doc.pendingOp + getRecentAck: () -> + # check if we have received an ack recently (within the flush delay) + @lastAcked? and new Date() - @lastAcked < @_doc._flushDelay + getOpSize: (op) -> + # compute size of an op from its components + # (total number of characters inserted and deleted) + size = 0 + for component in op or [] + if component?.i? + size += component.i.length + if component?.d? + size += component.d.length + return size attachToAce: (ace) -> @_doc.attach_ace(ace, false, window.maxDocLength) detachFromAce: () -> @_doc.detach_ace?()