def messarg_is_None(messarg): """Indicates whether or not a message argument looks like the value None. Returns true if the message argument is one of the following: *''* (the string) *'None'* (the string) *'[]'* (the list, not the string... this corresponds to the EmacsLisp *nil* value) **INPUTS** STR *messarg* -- The message argument to be converted. **OUTPUTS** BOOL *answer* -- True iif the message argument can be interpreted as the value None """ if messarg == 'None' or messarg == '' or messarg == []: # # Note: MessEncoder_LenPrefArgs encodes None value as string 'None', # while MessEncoderWDDX encodes it as the empty string, and # EmacsLisp's nil value gets encoded as an empty list. # if tracing('messarg_is_None'): trace('messarg_is_None', 'messarg=%s, returning 1' % messarg) return 1 else: if tracing('messarg_is_None'): trace('messarg_is_None', 'messarg=%s, returning 0' % messarg) return 0
def send_mess(self, mess_name, mess_argvals=None): """Sends a message to the external editor. **INPUTS** STR *mess_name* -- Identifier indicating what kind of message this is. {STR: STR} *mess_argvals* -- Dictionary of arguments and values for the message to be sent to the editor. **OUTPUTS** *none* response -- """ trace_id = 'send_mess.%s' % mess_name if tracing(trace_id): trace(trace_id, 'self=%s, mess_name=\'%s\'' % (self, mess_name)) if mess_argvals == None: tmp_args = {} else: tmp_args = copy.copy(mess_argvals) if tracing(trace_id): trace(trace_id, 'mess_argvals=\'%s\'' % tmp_args) unpkd_mess = self.encoder.encode(mess_name, tmp_args) pkd_mess = self.packager.pack_mess(unpkd_mess) self.packager.send_packed_mess(pkd_mess, self.transporter)
def send_mess(self, mess_name, mess_argvals=None): """Sends a message to the external editor. **INPUTS** STR *mess_name* -- Identifier indicating what kind of message this is. {STR: STR} *mess_argvals* -- Dictionary of arguments and values for the message to be sent to the editor. **OUTPUTS** *none* response -- """ trace_id = 'send_mess.%s' % mess_name if tracing(trace_id): ## trace(trace_id, 'self=%s, mess_name=\'%s\'' % (self, mess_name)) trace(trace_id, '--mess_name=\'%s\'' % mess_name) if mess_argvals == None: tmp_args = {} else: tmp_args = copy.copy(mess_argvals) if tracing(trace_id): trace(trace_id, 'mess_argvals=\'%s\'' % tmp_args) unpkd_mess = self.encoder.encode(mess_name, tmp_args) pkd_mess = self.packager.pack_mess(unpkd_mess) self.packager.send_packed_mess(pkd_mess, self.transporter)
def _push_change(self, change): """ private method for pushing a new ReverseBufferChange object onto the change_history stack **INPUTS** *ReverseBufferChange change* -- the object representing the reverse diff (would undo the change the editor reported) **OUTPUTS** *none* """ if tracing('SourceBuffWithDiffs._push_change'): debug.trace('SourceBuffWithDiffs._push_change', 'change to buff %s: old text "%s", replaced range = %s' \ % (self.name(), change.old_text, repr(change.range))) dropped = self.change_history.push(change) if dropped: if tracing('SourceBuffWithDiffs._push_change'): debug.trace('SourceBuffWithDiffs._push_change', 'dropped old text "%s", replaced range = %s' \ % (dropped.old_text, repr(dropped.range))) # level of the dropped change was: dropped_level = self.change_history.lowest() debug.trace('SourceBuffWithDiffs._push_change', 'dropped level %d' % dropped_level) # look through the cookie stack for cookies which referred to changes at or # below the dropped level, and drop them, since they are no longer valid lowest_cookie = self.cookie_jar.lowest() jar_height = self.cookie_jar.height() debug.trace('SourceBuffWithDiffs._push_change', 'cookie jar lowest, height = %d, %d' % (lowest_cookie, jar_height)) debug.trace('SourceBuffWithDiffs._push_change', 'looking through cookie jar...') found = 0 for i in range(lowest_cookie, jar_height): cookie_data = self.cookie_jar.peek(i) level = cookie_data.level debug.trace('SourceBuffWithDiffs._push_change', 'at index %d, level = %d' % (i, level)) if level > dropped_level: debug.trace('SourceBuffWithDiffs._push_change', 'found level') found = 1 break if found: debug.trace('SourceBuffWithDiffs._push_change', 'dropping cookies below index %d' % i) dropped_cookies = self.cookie_jar.drop_below(i) debug.trace('SourceBuffWithDiffs._push_change', 'dropped %d cookies' % dropped_cookies) else: debug.trace('SourceBuffWithDiffs._push_change', 'dropping cookies below height' % i) self.cookie_jar.drop_below(self.cookie_jar.height()) debug.trace('SourceBuffWithDiffs._push_change', 'dropped %d cookies' % dropped_cookies)
def insert_cbk(self, range, text): """External editor invokes that callback to notify VoiceCode of a deletion event. **INPUTS** (INT, INT) *range* -- Start and end position of text to be replaced by the insertion. If end of range is None, default to the end of the buffer. STR *text* -- Text to be inserted **OUTPUTS** *none* -- """ if tracing('SourceBuffWithDiffs.insert_cbk'): debug.trace('SourceBuffWithDiffs.insert_cbk', 'buff %s: replacing range %s with "%s"' \ % (self.name(), repr(range), text)) if self.undoing: debug.trace('SourceBuffWithDiffs.insert_cbk', 'in process of undoing') self.during_undo(text = text, range = range) else: if self._not_cached('get_text'): # if we don't have the buffer contents cached, we don't know what text # was replaced, so we can't create a reverse diff, and all our previous # change_history is invalid debug.trace('SourceBuffWithDiffs.insert_cbk', 'not cached') if range[1] != range[0] and range[1] != range[0] + 1: debug.trace('SourceBuffWithDiffs.insert_cbk', 'non-empty change') self.clear_stacks() else: # we need the old text, so we have to do all this processing before # calling SourceBuffCached.insert_cbk range_non_nil = [range[0], range[1]] if range_non_nil[1] == None: range_non_nil[1] = len(self._get_cache('get_text')) - 1 replaced = self.cache['get_text'][range_non_nil[0]:range_non_nil[1]] if tracing('SourceBuffWithDiffs.insert_cbk'): debug.trace('SourceBuffWithDiffs.insert_cbk', 'replaced text "%s"' % replaced) # don't record non-changes if replaced != text: # for the reverse diff, we need the range of the new text # The start of the new text is the same as the start of the old text, # but the end is offset from the start by one more than the length of # the new text start = range_non_nil[0] # we use the same convention for ranges as Python's slice end = start + len(text) reverse = ReverseBufferChange(replaced, (start, end)) self._push_change(reverse) SourceBuffCached.insert_cbk(self, range, text)
def insert_cbk(self, range, text): """External editor invokes that callback to notify VoiceCode of an insertion event. **INPUTS** (INT, INT) *range* -- Start and end position of text to be replaced by the insertion. STR *text* -- Text to be inserted **OUTPUTS** *none* -- """ if tracing('SourceBuffCached.insert_cbk.short'): trace('SourceBuffCached.insert_cbk.short', 'range=%s, len(text) = %d, text="%s..."' \ % (range, len(text), text[0:60])) if tracing('SourceBuffCached.insert_cbk'): trace('SourceBuffCached.insert_cbk', 'range=%s, text=\'%s\'' % (range, text)) # if range == None: # range = self.get_selection() # bad: if this gets the selection from the application, it will be all # screwed up because the application will already have made the change. # Basically, callbacks should never use defaults for the range SourceBuff.SourceBuff.insert_cbk(self, range, text) if self._not_cached('get_text'): # if we don't have the buffer contents cached, just get the entire # current contents (which should already include the deletion), thereby # caching it # self.get_text() # # Oops - this causes major problems because there may already have been # other changes to the buffer, whose change callbacks are still in the # queue. Therefore, the safe thing to do is to leave the buffer # uncached until the next time we explicitly synchronize with the # application (which first flushes all updates from the listen_msgr) trace('SourceBuffCached.insert_cbk.short', 'no cache - ignoring callback') pass else: trace('SourceBuffCached.insert_cbk', 'updating cached value') old_text = self.get_text() self._put_cache('get_text', old_text[:range[0]] + text + \ old_text[range[1]:]) self.uncache_data_after_buffer_change(what_changed = 'get_text')
def get_mess(self, expect=None): """Gets a message from the external editor. **NOTE:** In this version, get_mess won't block, but will return None if no message is available. **INPUTS** [STR] *expect* -- If not *None*, then make sure the message's name is listed in *expect*. If not, send an error message. **OUTPUTS** (STR, {STR: STR}) name_argvals_mess -- The message retrieved from external editor in *(mess_name, {arg:val})* format, or None if no message is available.""" trace('get_mess', 'self=%s, expecting %s' % (self, repr(expect))) try: name_argvals_mess = self.receiver.get(block=0) except Queue.Empty: return None if expect != None and (not (name_argvals_mess[0] in expect)): self.wrong_message(name_argvals_mess, expect) if tracing('get_mess.%s' % name_argvals_mess[0]): trace('get_mess.%s' % name_argvals_mess[0], 'got one of %s! It was: %s' \ % (repr(expect), repr(name_argvals_mess))) return name_argvals_mess
def decode(self, str_mess): """Decodes a message to {arg:val} format. **INPUTS** *STR* str_mess -- The message in raw string format **OUTPUTS** *(STR, {STR: STR}) name_argvals_mess* -- First element is the message name, second element is message arguments in *(name, {arg:val})* format. """ if tracing('messaging.MessEncoderWDDX.decode'): trace('messaging.MessEncoderWDDX.decode', 'decoding str_mess="%s"' % str_mess) mess_argvals = self.unmarshaller.loads(str_mess) # # Name of message is one of the entries in the unmarshalled dictionnary. # Remove it from there. # mess_name = mess_argvals['message_name'] del mess_argvals['message_name'] return (mess_name, mess_argvals)
def get_mess(self, expect=None): """Gets a message from the external editor. **NOTE:** In this version, get_mess won't block, but will return None if no message is available. **INPUTS** [STR] *expect* -- If not *None*, then make sure the message's name is listed in *expect*. If not, send an error message. **OUTPUTS** (STR, {STR: STR}) name_argvals_mess -- The message retrieved from external editor in *(mess_name, {arg:val})* format, or None if no message is available.""" trace('get_mess', 'expecting %s' % repr(expect)) try: name_argvals_mess = self.receiver.get(block=0) except Queue.Empty: return None if expect != None and (not (name_argvals_mess[0] in expect)): self.wrong_message(name_argvals_mess, expect) if tracing('get_mess.%s' % name_argvals_mess[0]): trace('get_mess.%s' % name_argvals_mess[0], 'got one of %s! It was: %s' \ % (repr(expect), repr(name_argvals_mess))) return name_argvals_mess
def get_mess(self, expect=None): """Gets a message from the external editor. **NOTE:** get_mess may block if no message is available. **INPUTS** [STR] *expect* -- If not *None*, then make sure the message's name is listed in *expect*. If not, send an error message. **OUTPUTS** (STR, {STR: STR}) name_argvals_mess -- The message retrieved from external editor in *(mess_name, {arg:val})* format, or None if no message is available.""" trace('get_mess', 'self=%s, expecting %s' % (self, repr(expect))) pkd_mess = self.packager.get_packed_mess(self.transporter) unpkd_mess = self.packager.unpack_mess(pkd_mess) name_argvals_mess = self.encoder.decode(unpkd_mess) if expect != None and (not (name_argvals_mess[0] in expect)): trace('get_mess', 'wrong_message %s, expecting %s' % \ (repr(name_argvals_mess), repr(expect))) self.wrong_message(name_argvals_mess, expect) if tracing('get_mess.%s' % name_argvals_mess[0]): trace('get_mess.%s' % name_argvals_mess[0], 'got one of %s! It was: %s' \ % (repr(expect), repr(name_argvals_mess))) return name_argvals_mess
def get_mess(self, expect=None): """Gets a message from the external editor. **NOTE:** get_mess may block if no message is available. **INPUTS** [STR] *expect* -- If not *None*, then make sure the message's name is listed in *expect*. If not, send an error message. **OUTPUTS** (STR, {STR: STR}) name_argvals_mess -- The message retrieved from external editor in *(mess_name, {arg:val})* format, or None if no message is available.""" # leave away self here (see send_mess.) QH trace('get_mess', '--expecting %s' % repr(expect)) pkd_mess = self.packager.get_packed_mess(self.transporter) unpkd_mess = self.packager.unpack_mess(pkd_mess) name_argvals_mess = self.encoder.decode(unpkd_mess) if expect != None and (not (name_argvals_mess[0] in expect)): trace('get_mess', 'wrong_message %s, expecting %s' % \ (repr(name_argvals_mess), repr(expect))) self.wrong_message(name_argvals_mess, expect) if tracing('get_mess.%s' % name_argvals_mess[0]): trace('get_mess.%s' % name_argvals_mess[0], 'got one of %s! It was: %s' \ % (repr(expect), repr(name_argvals_mess))) return name_argvals_mess
def _get_cache_multiple(self, names): trace('SourceBuffCached._get_cache_multiple', 'names=%s' % repr(names)) values = [] for a_name in names: values.append(self._get_cache(a_name)) if tracing('SourceBuffCached._get_cache_multiple'): trace('SourceBuffCached._get_cache_multiple', 'returning values=%s' % repr(values)) return values
def delete_cbk(self, range): """External editor invokes that callback to notify VoiceCode of a deletion event. **INPUTS** (INT, INT) *range* -- Start and end pos of range to be deleted **OUTPUTS** *none* -- """ debug.trace('SourceBuffWithDiffs.delete_cbk', 'buff %s: deleting range = %s' % (self.name(), repr(range))) if self.undoing: debug.trace('SourceBuffWithDiffs.delete_cbk', 'in process of undoing') self.during_undo(text = "", range = range) else: if self.cache['get_text'] == None: # if we don't have the buffer contents cached, we don't know what text # was deleted, so we can't create a reverse diff, and all our previous # change_history is invalid debug.trace('SourceBuffWithDiffs.delete_cbk', 'not cached') if range[1] != range[0] and range[1] != range[0] + 1: debug.trace('SourceBuffWithDiffs.delete_cbk', 'non-empty change') self.clear_stacks() else: # we need the old text, so we have to do all this processing before # calling SourceBuffCached.delete_cbk deleted = self.cache['get_text'][range[0]:range[1]] # don't record deletions of nothing if tracing('SourceBuffWithDiffs.delete_cbk'): debug.trace('SourceBuffWithDiffs.delete_cbk', 'deleted text "%s"' % deleted) if deleted: # for the reverse diff, we need the range of the new text # The start of the new text is the same as the start of the old text, # but the end is offset from the start by one more than the length of # the new text start = range[0] end = range[0] # for deletions, the range of the new text is empty (technically, we reverse = ReverseBufferChange(deleted, (start, end)) self._push_change(reverse) SourceBuffCached.delete_cbk(self, range)
def contents_cbk(self, text): """External editor invokes that callback to inform VoiceCode of the complete contents of the buffer. **INPUTS** STR *text* -- Up-to-date contents of the buffer **OUTPUTS** *none* -- """ if tracing('SourceBuffCached.contents_cbk.short'): trace('SourceBuffCached.contents_cbk.short', 'len(text) = %d, text="%s..."' \ % (len(text), text[0:60])) if tracing('SourceBuffCached.contents_cbk'): trace('SourceBuffCached.contents_cbk', 'range=%s, text=\'%s\'' % (range, text)) SourceBuff.SourceBuff.contents_cbk(self, text) if self._not_cached('get_text'): # if contents are not cached, cache them self._put_cache('get_text', text) self.uncache_data_after_buffer_change(what_changed = 'get_text') if tracing('SourceBuffCached.contents_cbk'): trace('SourceBuffCached.contents_cbk', ('** upon exit, self._get_cache("cur_pos")=%s,' + ' self._get_cache("get_text")=%s') % \ (self._get_cache("cur_pos"), repr(self._get_cache("get_text")))) else: # otherwise, treat this as an insert_cbk start, end, change = \ find_difference.find_difference(self.cache['get_text'], text) self.insert_cbk(range = (start, end), text = change)
def _get_cache_element_multiple(self, elt_names, get_from_app_method): """Gets the value of one or more elements that could be in cache. **INPUTS** [STR] *elt_names* -- list of names of the elements in the cache. METHOD *get_from_app_method* -- the method to invoke in order to get the value of the elements directly from the client application instead of from the cache. We assume that the method returns the elements in the same order as they are listed in *elt_names*. **OUTPUTS** [ANY] *values* -- values of each of the elements. """ trace('SourceBuffCached._get_cache_element_multiple', 'elt_names=%s, get_from_app_method=%s, self.use_cache=%s' % (repr(elt_names), get_from_app_method, self.use_cache)) if not self.use_cache: debug.trace('SourceBuffCached._get_cache_element_multiple', 'not using cache') values = apply(get_from_app_method) if len(elt_names) == 1: # if only one element, assume that get_from_app_method returns a single # value for that element, as opposed to a list of values for earch # element values = [values] else: debug.trace('SourceBuffCached._get_cache_element_multiple', 'looking up in cache') if self._not_cached_multiple(elt_names): debug.trace('SourceBuffCached._get_cache_element_multiple', 'cache is dirty... retrieving from app') values_from_app = apply(get_from_app_method) if len(elt_names) == 1: # if only one element, assume that get_from_app_method returns a single # value for that element, as opposed to a list of values for earch # element values_from_app = [values_from_app] self._put_cache_multiple(elt_names, values_from_app) else: debug.trace('SourceBuffCached._get_cache_element_multiple', 'cache element was up to date') values = self._get_cache_multiple(elt_names) if tracing('SourceBuffCached._get_cache_element_multiple'): debug.trace('SourceBuffCached._get_cache_element_multiple', 'returning values=%s' % repr(values)) return values
def app_change_buffer(self, buff_name): """Changes the external application's active buffer. **INPUTS** STR *buff_name* -- Name of the buffer to switch to. **OUTPUTS** *BOOL* -- true if buff_name exists and the application successfully switches to it """ self.talk_msgr.send_mess('change_buff', {'buff_name': buff_name}) response = self.talk_msgr.get_mess(expect=['change_buff_resp']) value = messaging.messarg2int(response[1]['value']) if tracing('AppStateEmacs.app_change_buffer'): trace('AppStateEmacs.app_change_buffer', 'response was %d' % value) return value
def receive_string(self, num_bytes): """Receives a string on the Socket connection. **INPUTS** INT *num_bytes* -- Number of bytes to receive. **OUTPUTS** STR *a_string* -- The received string """ a_string = '' while len(a_string) < num_bytes: if self.sleep: while not self.data_available(): self.sleeper.sleep(self.sleep) if self.sleeper.was_woken(): raise WokenUp("receive_string woken up") # time.sleep(self.sleep) try: chunk = self.sock.recv(num_bytes - len(a_string)) if tracing('receive_string'): trace('receive_string', 'read chunk=\'%s\'' % chunk); except socket.error: chunk = '' if chunk == '': sys.stderr.write('MessTransporter_Socket.receive_string:') sys.stderr.write(' no data received in %s' % \ threading.currentThread().getName()) raise SocketError("socket connection broken (receiving)") # raise SocketError, "socket connection broken" a_string = a_string + chunk # if tracing('receive_string'): # trace('receive_string', "received string '%s'" % a_chunk) return a_string
def send_packed_mess(self, pkd_mess, transporter): """Send a packaged message as a sequence of fixed length chunks. **INPUTS** STR *pkd_mess* -- The packed message [MessTransporter] *transporter* -- Transport channel to be used **OUTPUTS** *none* -- ..[MessTransporter] file:///./messaging.MessTransporter.html""" if tracing('send_packed_mess'): trace('send_packed_mess', 'pkd_mess="%s"' % pkd_mess) # # Nothing particular about how such messages need to be sent. # transporter.send_string(pkd_mess)
def _put_cache_multiple(self, names, values): if tracing('SourceBuffCached._put_cache_multiple'): trace('SourceBuffCached._put_cache_multiple', 'names=%s, values=%s' % (repr(names), repr(values))) for ii in range(len(names)): self._put_cache(names[ii], values[ii])
class ListenAndQueueMsgsThread(threading.Thread, Object.Object): """class for a thread which listens for messages using a given Messenger puts completed messages on a Queue. **INSTANCE ATTRIBUTES** [Messenger] *underlying* -- underlying messenger (usually [MessengerBasic]) used to receive and unpack them messages. Queue.Queue *completed_msgs* -- Queue on which to deposit the completed messages. SocketHasDataEvent *event* -- object used to notify the main thread that a socket has data Event *connection_ending* -- threading.Event object which will be set to true if the connection has been terminated and the thread should die (STR, {STR: STR}) conn_broken_msg -- message to put onto the Queue to indicate that the connection was broken unexpectedly. Note that because of thread timing issues, this can occur even if the mediator previously received an editor_disconnecting message, or even if the editor disconnected in response to a mediator_closing or terminating message. However, in the former case, AppStateMessaging should process the editor_disconnecting message first, and will therefore ignore any subsequent messages in the queue (including the conn_broken one). In the latter case, the mediator should already have left the message loop, so, again, the conn_broken message should not be processed. CLASS ATTRIBUTES** *none* -- .. [Messenger] file:///./messenger.Messenger.html .. [MessengerBasic] file:///./messenger.MessengerBasic.html""" def __init__(self, underlying, completed_msgs, event, connection_ending, conn_broken_msg, **args_super): self.deep_construct(ListenAndQueueMsgsThread, { 'underlying': underlying, 'completed_msgs': completed_msgs, 'event': event, 'connection_ending': connection_ending, 'conn_broken_msg': conn_broken_msg }, args_super, exclude_bases={'threading.Thread': 1}) # provides debug messages # threading.Thread.__init__(self, verbose = 1) threading.Thread.__init__(self) def message_queue(self): """returns a reference to the message queue in which the thread puts completed messages **INPUTS** *none* **OUTPUTS** *Queue.Queue* -- the message queue """ debug.trace('ListenAndQueueMsgsThread.message_queue', '** invoked, call stack is:') # debug.trace_call_stack('ListenAndQueueMsgsThread.message_queue', '**') return self.completed_msgs def get_mess(self): """Gets a message from the external editor. **INPUTS** *none* **OUTPUTS** (STR, {STR: STR}) name_argvals_mess -- The message retrieved from external editor in *(mess_name, {arg:val})* format. from external editor in *(mess_name, {arg:val})* format, or None if no message is available.""" debug.trace('ListenAndQueueMsgsThread.get_mess', 'invoked') return self.underlying.get_mess() def notify_main(self): """notify the main thread that there is a new message waiting in the Queue, and return asynchronously. **INPUTS** **OUTPUTS** *none* """ debug.trace('ListenAndQueueMsgsThread.notify_main', 'self.event=%s' % self.event) # debug.trace_call_stack('ListenAndQueueMsgsThread.notify_main', '**') self.event.notify() def run(self): """Start listening for data. **INPUTS** *none* -- **OUTPUTS** *none* -- """ debug.trace('ListenAndQueueMsgsThread.run', 'thread %s starting' % threading.currentThread().getName()) while 1: try: debug.trace('ListenAndQueueMsgsThread.run', '** getting a message') data = self.get_mess() except messaging.SocketError, err: if self.connection_ending.isSet(): # sys.stderr.write('SocketError, but connection_ending was set\n') break # connection broken unexpectedly (unless we just didn't get the # connection_ending event in time) # sys.stderr.write('unexpected SocketError\n') self.completed_msgs.put(self.conn_broken_msg) self.notify_main() break except messaging.WokenUp: break if debug.tracing('ListenAndQueueMsgsThread.run'): debug.trace('ListenAndQueueMsgsThread.run', '** data=%s' % repr(data)) if data: debug.trace( 'ListenAndQueueMsgsThread.run', '** sending notification message that data was received.') self.completed_msgs.put(data) self.notify_main() # time.sleep(0.01) # time.sleep(1) # waits for timeout, or until connection_ending is set self.connection_ending.wait(0.01) # self.connection_ending.wait(1.0) if self.connection_ending.isSet(): # sys.stderr.write('connection_ending detected\n') break
def restore_state(self, cookie): """restores the buffer to its state at the time when the cookie was returned by store_current_state. Both the contents and the selection will be restored. However, other data, such as the search history, may not. The restore operation can fail, which will be indicated by a return value of 0, so the caller should always check the return value. **INPUTS** *SourceBuffCookie cookie* -- see above. Note that SourceBuffCookie is a dummy type, not an actual class. The actual type will vary with SourceBuff subclass. **OUTPUTS** *BOOL* -- true if restore was successful """ if not self.valid_cookie(cookie): return 0 if not (self.accumulated is None): debug.trace('SourceBuffWithDiffs.restore_state', 'already inside a restore_state call') return 0 debug.trace('SourceBuffWithDiffs.restore_state', 'key is %s' % cookie.cookie_key) index = self.cookie_jar.index(cookie.cookie_key) debug.trace('SourceBuffWithDiffs.restore_state', 'at index %d in the cookie jar' % index) data = self.cookie_jar.peek(index) level = data.level debug.trace('SourceBuffWithDiffs.restore_state', 'data.level = %d out of %d' % \ (level, self.change_history.height())) # later, we may put this stuff on a forward stack, but for now, just pop # it while self.cookie_jar.height() > index: debug.trace('SourceBuffWithDiffs.restore_state', 'cookie jar height = %d, popping' \ % self.cookie_jar.height()) self.cookie_jar.pop() self.undoing = 1 success = 0 try: while self.change_history.height() > level: debug.trace('SourceBuffWithDiffs.restore_state', 'change history height = %d, popping' \ % self.change_history.height()) change = self.change_history.pop() text = change.old_text start, end = change.range self.accumulated = [] if tracing('SourceBuffWithDiffs.restore_state'): debug.trace('SourceBuffWithDiffs.restore_state', 'popped text "%s", range = (%d, %d)' \ % (text, start, end)) self.set_text(text, start = start, end = end) # the callback should clear this if we got the expected change. # if it didn't, then we're in trouble. Since we don't have a forward # stack yet, there's nothing we can do except return false to indicated # failure if len(self.accumulated) != 1: break accumulated_text = self.accumulated[0].text accumulated_range = self.accumulated[0].range if text != accumulated_text: if tracing('SourceBuffWithDiffs.restore_state'): debug.trace('SourceBuffWithDiffs.restore_state', 'text "%s" != expected "%s"' % (accumulated_text, text)) break if change.range != accumulated_range: debug.trace('SourceBuffWithDiffs.restore_state', 'range %s != expected %s' \ % (repr(accumulated_range), repr(change.range))) break success = 1 finally: self.undoing = 0 self.accumulated = None if success: self.set_selection(data.get_selection(), cursor_at = data.cursor_at()) self.last_search = data.last_search() self.print_buff_if_necessary() return success