Beispiel #1
0
del l[0:2]
assert(l == ['e', 'f'])
del l[1:2]
assert(l == ['e'])
del l[0:1]
assert(l == [])

#####################
print("------------------------------------------------")
print(" UPDATE TEST")
print("--------------------- - - - - - - - - - - - - - - -")
l = ['a', 'b', 'c', 'd', 'e', 'f']
q = SBList(l)	
print('Original: ' + repr(q))
for j in range(0, len(q)):
	q.update(j, 'new' + q[j])
	#print(q.show_state())
	#print(repr(q))
	#print("- - - - - - ")

q.update(len(q) - 1, 'new last row')
print(repr(q))
assert(repr(q) == '[newa, newb, newc, newd, newe, new last row]')
######################################################################
#
#
#
l = ['a', 'b', 'c', 'zz', 'd', 'e', 'f']
q = SBList(l)	
print('Original: ' + repr(q))
Beispiel #2
0
class SBString(object):
	class StateLog(object):
		state_id = None
		vstate_id = None
		prior_state_id = None
		def __init__(self, state_id, vstate_id, prior_state_id):
			object.__init__(self)
			self.state_id = state_id
			self.vstate_id = vstate_id
			self.prior_state_id = prior_state_id
		def __repr__(self):
			return('[' + str(self.state_id) + ', ' + str(self.vstate_id) + ', ' \
				+ str(self.prior_state_id) + ']')

	def __init__(self, txt, nickname=''):
		'''SBString.__init__()
		'''
		object.__init__(self)
		# The underlying list object begins as a list 
		# that contains only one string. Insertions of character
		# into the string will be appended to the end of self.l and
		# presented in the correct place in the virtual view.
		self.l = [txt]
		# nickname might be useful for debugging when you have many
		# instances of thie object
		self.buff_len = SBList([len(txt)])
		self.nickname = nickname
		# self.point will be keyed with self.state_id
		self.point = [0]
		self.state_id = 0
		# self.virtview holds pointers that allow me to reassemble
		# self.l into the intended text.
		self.virtview = VirtView(VirtView.StateEntry(0, len(txt), 0))
		# state_log saves all relevant state IDs (state ID for l is len-1):
		# main, view, prior
		# I think I need this because deletions affect only the *view*
		# object and inserts affect both *l* and *view*, and batch
		# changes can increment l and view without incrementing the main
		# state_id
		self.state_log = [self.StateLog(self.state_id, self.virtview.get_state_id(), -1)]
		assert(self.virtview.get_state_id() == self.state_id)

	def __len__(self):
		# REPLACE THIS TO USE THE STATIC BUFF_LEN OR STR_LEN() FUNCTIONS
		#return(len(self.get_string()))
		return(self.str_len())

	def __repr__(self):
		'''SBString.__repr__()
		Return a string representation of this object.
		'''
		return(self.get_string())

	def delete(self, start, count, batch=False, state_id=-1):
		'''SBString.delete()
		Delete the characters starting at *start* for
		*count* characters.
		'''
		# Note that the delete range might span more
		# than one text chunk and require redefining
		# two *range* values at the end points of the 
		# deletion zone.
		#
		# Function:
		# 1) find all of the entries in self.virtview
		#    that span the deletion zone.
		# 2) modify *range* values that are only partially
		#    in the deletion zone and delete others.
		# 3) do not delete any of the underlying text, because
		#    that will be retained internally for possible
		#    undeletion.
		end = start + count # end pt in python slice() format
		if state_id == -1:
			state_id = self.state_id

		s_len = len(self)
		if start >= s_len:
			return(0)
		if end > s_len:
			end = s_len
		del_len = end - start

		#(low_offset, low_adj, low_l_idx, string_id) = self.virtview._get_vl_idx(start)
		low = self.virtview._get_vl_idx(start)

		
		# WARNING, high.state_offset is not in slice() format to avoid pointing
		# to the next state offset, so I will try fudging the high.state_adj value
		# to restore it to slice() format.
		#(high_offset, high.state_adj, high_l_idx, string_id) \
		high	= self.virtview._get_vl_idx(end - 1)
		high.state_adj += 1
		#
		# get_range() now returns a StateEntry object
		low_range = self.virtview.get_range(s_offset=low.state_offset)
		high_range = self.virtview.get_range(s_offset=high.state_offset)
		incr_offset = 0
		batch_code = batch
		for j in range(low.state_offset, high.state_offset + 1):
			# Delete the state *range* stored at low.state_offset
			# and the higher states will fall into the slot
			# for the next deletion if needed.
			# Note: allow no more that one transaction to affect self.virtview.l.state_id
			# by making the first iteration use the orginal value of 'batch,' then
			# the other iterations using batch=True:
			self.virtview.delete_state(low.state_offset, batch=batch_code)	
			batch_code = True
	
		if high.state_offset > low.state_offset:
			# at least two of the old ranges were affected by the deletiong
			if low.state_adj == 0:
				# The entire first range was destroyed by the delete
				pass
			else:
				# Load a modified first range
				self.virtview.insert_state(low.state_offset, VirtView.StateEntry(low_range.start_pt, \
					low_range.start_pt + low.state_adj, low_range.list_idx), batch=True)
				incr_offset += 1

			#
			high_vrow = end - high.state_adj + (high_range.end_pt - high_range.start_pt)
			if high_vrow == end:
				# The entire seconde range was destroyed by the delete
				pass
			else:
				# load a modified second range:
				# (the incr_offset is an adjustment in case a prevous state range was
				# inserted above.
				self.virtview.insert_state(low.state_offset + incr_offset, \
					VirtView.StateEntry(high_range.start_pt + high.state_adj, high_range.end_pt, high_range.list_idx), \
					batch=True)
			
		else:
			# No more than one old range was affected by the deletion
			old_rng_len = low_range.end_pt - low_range.start_pt
			if del_len == old_rng_len:
				# the existing range object is the exact length of 
				# the deletion range, so don't restore it
				pass
			else:
				if low.state_adj == 0:
					# The character(s) that I want to delete is the first
					# in this range, so modify the range.
					self.virtview.insert_state(low.state_offset, VirtView.StateEntry(low_range.start_pt \
					+ low.state_adj + del_len, low_range.end_pt, low_range.list_idx), batch=True)
				else:
					# I have to split the range entry in half
					self.virtview.insert_state(low.state_offset, VirtView.StateEntry(low_range.start_pt, \
						low_range.start_pt + low.state_adj, low_range.list_idx))
					self.virtview.insert_state(low.state_offset + 1, VirtView.StateEntry(low_range.start_pt \
						+ low.state_adj + del_len, low_range.end_pt, low_range.list_idx), batch=True)
		# 
		# capture state_ids
		if not batch:
			# (end - start) reflects any error correction if the user tries to delete beynd EOL.
			self.incr_str_len(-1 * (end - start), state_id=self.state_id)
			self.incr_state_id()# This also rolls point to the new state
			# Save to sate log: current state,  viewstate, prior state
			self.state_log.append(self.StateLog(self.state_id, \
				self.virtview.get_state_id(), state_id))
		else:
			self.incr_str_len(-1 * (end - start), state_id=state_id)
			# Save to sate log: current state,  viewstate, prior state
			self.state_log[self.state_id] = self.StateLog(self.state_id, \
				self.virtview.get_state_id(), state_id)
		return(0)# end of delete

	def get_char(self, offset, state_id=-1):
		if state_id == -1:
			state_id = self.state_id
			vstate_id = self.virtview.get_state_id()
		else:	
			vstate_id = self.state_log[state_id].vstate_id
		idx = 0
		if offset >= len(self):
			return('')
		s = ''
		for j in range(len(self.virtview)):
			# v is a StateEntry object with start_pt, end_pt, list_idx...
			v = self.virtview.get_item(j, state_id=vstate_id)

			# Accumulate the count of characters that WILL have been
			# counted by the end of this chunk of text. Do this by
			# finding the difference between the 
			# starting and ending slice() indexes into this piece
			# of text:
			if (idx + v.length) > offset:
				start = offset - idx + v.start_pt 
				#s = self.l[v.list_idx][start: start+ 1]
				s = self.l[v.list_idx][start: start+ 1]
				dprint('found get_char: ' + repr( self.l[v.list_idx]) \
					+ ' offset = ' + str(offset) + ' vstar=' + str(v.start_pt)
					+ ' idx = ' + str(idx)
					+ ' start=' + str(start) + ' len=' + str(v.length))
				break
			idx += v.length
		return(s)
		
	def get_state_id(self):
		'''SBString.get_state_id()
		'''
		return(self.state_id)
	
	def get_string(self, state_id=-1):
		'''SBString.get_string()
		'''
		if state_id == -1:
			self.dump()
			vstate_id = self.state_log[-1].vstate_id
		else:	
			vstate_id = self.state_log[state_id].vstate_id
		tmp_l = [] 
		for j in range(len(self.virtview)):
			# v = self.virtview[j]
			v = self.virtview.get_item(j, state_id=vstate_id)
			#tmp_l.append(self.l[v[2]][v[0]:v[1]])
			tmp_l.append(self.l[v.list_idx][v.start_pt:v.end_pt])
		
		return(''.join(tmp_l))

	def get_point(self, state_id=-1):
		if state_id == -1:
			state_id = self.state_id
		#else:	
		return(self.point[state_id])

	def incr_point(self, incr, state_id=-1):
		"""SBString.incr_point()
		Let the editor call this, don't call it here

		Set the point to the specified value, but
		correct for errors if the value goes below
		zero or beyond EOL.

		"""
		if state_id == -1:
			state_id = self.state_id
		#else:	
		self.set_point(self.point[state_id] + incr)
		return(0)

	def incr_str_len(self, incr, state_id=-1):
		"""SBString.incr_str_len()
		Increment the stored value for the logical length of the string.
		This does not alter any state_id values.  When incr_point is
		called, that routine advances the buff_len to the next state.

		This was added for performance reasons because navigating the
		state list became slow after 100 edits. Storing this value
		in an SBList object allows for the length to be correct
		after undo() or redo() actions, but perhaps I could use a 
		regular list as was done with incr_point (if that works).
		"""
		if state_id == -1:
			state_id = self.state_id

		##self.set_point(self.point[state_id] + incr)
		self.buff_len.update(0, \
			self.buff_len[0] + incr, \
			state_id=state_id, batch=True) 	
		return(self.point[state_id])


	def incr_state_id(self):
		'''SBString.incr_state_id()
		Always use this to increment the state after a insert, delete
		or other such operation (in the future).
		This routine is needed to set the state_id properly after
		an undo is made.
		'''
		self.buff_len.append(self.buff_len[0])
		self.point.append(self.point[self.state_id])
		self.state_id = len(self.state_log) 
		return(0)
		
	def insert(self, start, txt, state_id=-1, batch=False):
		'''SBString.insert()
		'''
		if state_id == -1:
			save_state_id = self.state_id
			vstate_id = -1
		else:
			save_state_id = state_id
			vstate_id = self.state_log[state_id].vstate_id

		# New text is always appended to the end of self.l,
		self.l.append(txt)# Never use batch here.
		# ... but then the index from l is inserted into the 
		# logical view of the string:
		self.virtview.insert(start, VirtView.StateEntry(0, len(txt), len(self.l) - 1), \
			state_id=vstate_id, batch=batch)
		##### The point here is not used and is probably wrong !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
		####self.point.append(self.point[save_state_id]) # Never use batch here.
		# capture state_ids
		if not batch:
			self.incr_state_id()
			#self.incr_point(len(txt), state_id=state_id)
			self.incr_str_len(len(txt), state_id=self.state_id)
			# Save to sate log: current state, lstate, viewstate, prior state
			self.state_log.append(self.StateLog(self.state_id, \
				self.virtview.get_state_id(), save_state_id))
		else:
			#self.incr_point(len(txt), state_id=state_id)
			self.incr_str_len(len(txt), state_id=state_id)
			# Save to sate log: current state, lstate, viewstate, prior state
			self.state_log[self.state_id] = self.StateLog(self.state_id, \
				self.virtview.get_state_id(), save_state_id)
		return(0)# end of insert()

	def set_point(self, pt, state_id=-1):
		if state_id == -1:
			state_id = self.state_id
		#else:	
		if pt > len(self):
			# apr 22, 00:004, was =lstate_id
			self.point[state_id] = self.str_len(state_id=state_id) - 1
		elif pt < 0:
			self.point[state_id] = 0
		else:
			self.point[state_id] = pt
		return(0)

	def show_state(self, state_id=-1):
		'''SBString.get_string()
		Return the set of *range* items that point to the 
		underlying strings.
		'''
		if state_id == -1:
			vstate_id = -1
		else:	
			vstate_id = self.state_log[state_id].vstate_id
		return('view state: ' + self.virtview.show_state(state_id=vstate_id))

	def str_len(self, state_id=-1):
		"""SBString.str_len()
		Return the length of the virtual string at the
		specified state ID (or current state if the state_id is
		not specified.
		"""
		###if state_id == -1:
		###	state_id = self.state_id
		#### was =lstate_id apr 22
		###return(len(self.get_string(state_id=state_id)))
		return(self.buff_len[0])

	def test_vl(self, str_offset):
		return(self.virtview._get_vl_idx(str_offset))

	def undo(self, count=1):
		"""SBString.undo()
		"""
		###self.state_id -= count
		##j = len(self.state_log) - 1
		##while j >= 0 and self.state_log[j - 1][0] == self.state_id:
		# Access the previous state id value from the log.
		# Remember that if the user used undo and then made edits,
		# the previous state value could point to nearly any of the valid states.
		self.state_id = self.state_log[-1].prior_state_id
		if self.state_id < 0:
			self.state_id = 0

		# Find the state IDs for the underlying list and the view-manager:
		vstate_id = self.state_log[self.state_id].vstate_id


		##self.l.set_state_id(lstate_id)
		self.virtview.set_state_id(vstate_id)
		return(self.state_id)
	def dump(self):
		return(0)
		print('-------------------------------------------------SBString Dump Start')
		print('state_id=' + str(self.state_id))
		print('point = ' + repr(self.point))
		print('bufflen ' + repr(self.buff_len))
		print('virtview ' + repr(self.virtview))
		print('log=' + repr(self.state_log))
		print('l ' + repr(self.l))