예제 #1
0
파일: SBStrList.py 프로젝트: rehoot/SBList
	def __init__(self, ListOfStrings):
		'''SBStrList.__init__()
		'''
		# MAYBE I SHOULD CODE ATTRIBUTES WITH THE VALUE OF THE
		# CURRENT INSTANCE SO EVERYTHING CAN GRAB THE CURRENT
		# STATE_ID WITHOUT REQUIRING INPUT FROM THE USER -- is that needed?
		object.__init__(self)
	
	
		# Cast the strings as SBSTring objects and load into a new list.
		# I tried embedding this into SBList, but it caused a loop in the 
		# definitions of SBList and SBString.
		self.buff_len = SBList([0])
		temp_l = []
		for j in range(len(ListOfStrings)):
			#if ListOfStrings[j][-1] not in ['\n', '\r'] \
			#and j != (len(ListOfStrings) - 1):
			#	temp_l.append(SBString(ListOfStrings[j]) + '\n')
			#else:
			if ListOfStrings[j][0:-1].count('\n') > 0:
				raise Exception('Your test string contains an embedded EOL in mid-line.')
			temp_l.append(SBString(ListOfStrings[j]))
			self.buff_len[0] += len(ListOfStrings[j])
		self.l = SBList(temp_l)
		#
		self.state_id = 0
		self.point = SBList([0])
		# The main state_id for SBStrList will be the state_id for
		# self.log?  To show the undo list of self.log, display
		# only values in the range self.log[0:self.log.get_state_id].
		# I might have to review how transactions
		# are chunked (such as 'replace' or 'update' transactions).
		self.log = SBList([]) 
		# Maybe use a list or function that lists all the 
		# objects that contain range-based lists so that users
		# can create one and have it automatically updated.
		self.range_lists = []
		self.range_funcs = []
		self.bookmarks = SBList([])
		# The *statements* field might be used to store the start and
		# end points of comment blocks or full statements in a
		# programming language. It might help the logic for applying
		# the syntax highligher by telling it to start on a prior line.
		self.statements = SBList([])
		self.range_lists.append(self.bookmarks)
		self.range_lists.append(self.statements)
		
		self.line_pts = None
		self.rebuild_line_pts()
		tmp_lp = None
		self.range_lists.append(self.line_pts)
예제 #2
0
파일: SBString.py 프로젝트: rehoot/SBList
	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)
예제 #3
0
파일: SBStrList.py 프로젝트: rehoot/SBList
	def rebuild_line_pts(self):
		tmp_lp = []
		tmp_pt = 0
		for j in range(len(self.l)):
			# Record the starting and ending point on each line.
			# This is NOT in slice() format.
			tmp_lp.append([tmp_pt, tmp_pt + len(self.l[j])])
			tmp_pt += len(self.l[j])
		self.line_pts = SBList(tmp_lp)
		return(0)
예제 #4
0
파일: SBLtest07.py 프로젝트: rehoot/SBList
list_len = 20
######################################################################
#
def random_string(len):
	s = ''
	for k in range(len):
		space_test = int(random.expovariate(.5)) + 1
		if space_test > 5:
			s += ' '
		else:
			s += chr(random.randint(ord('a'), ord('z')))
	return(s)
	

######################################################################
for r in range(replicates):
	l = []
	for j in range(list_len):
		l.append(random_string(3))
	
	q = SBList(l)
	
	for j in range(len(l) + 1):
		#print(q._get_l_idx(j))
		sd = q._get_l_idx(j)
		for k in sd:
			assert(k is not None)



예제 #5
0
파일: SBLtest02.py 프로젝트: rehoot/SBList
from SBList import *
#####################################################################
#####################################################################
#
#                          Testing Insert
#
l = ['a', 'b', 'c', 'd', 'e', 'f']
q = SBList(l)	
print('Original: ' + repr(q))

q.insert(3, 'after c')
l.insert(3, 'after c')
#
## Validate _get_l_idx:
#for j in range(len(q)):
#	(s_offset, s_adj, l_idx) = q._get_l_idx_(j)
#	print("logical line" + str(j) + " s_offset" + str(s_offset) \
#  + " sadj " + str(s_adj) + " l idx:" + str(l_idx))

q.insert(7, "last line")
l.insert(7, "last line")
q.insert(7, "next to last line")
l.insert(7, "next to last line")
#print('q is ' + repr(q))
#print('l is ' + repr(l))
for j in range(len(l)):
	assert(q[j] == l[j])

revised = SBList(l)
assert(revised == q)
#######################################################################
예제 #6
0
파일: SBLtest06.py 프로젝트: rehoot/SBList
	l = []
	l.append(chr(random.randint(ord('a'), ord('z'))))
	str_len = random.randint(1, max_str_len)
	s = ''
	for j in range(list_len):
		s = random_string(str_len)
		l.append(s)
	#
	# Now copy the list and test it
	# (I copy the list to avoid the possibility that some
	# testing operations will change the original list and
	# thereby ruin my ability to detect changes)
	#
	save_l = []
	save_l.extend(l)
	q = SBList(l)	
	insert_idx = []
	for j in range(inserts):
		#### EXPAND THIS LATER TO ALLOW LST_LEN OR MORE:
		insert_idx.append(random.randint(0, list_len - 1))
	#
	planned_inserts = []
	# Now perform the inserts
	for j in range(inserts):
		# insert to the SBList and the parallel regular list
		new_s = random_string(random.randint(1, max_str_len)) \
			+ 'insrt{0:05d}'.format(j)
		q.insert(insert_idx[j], new_s)
		planned_inserts.append(['i', insert_idx[j]])
	u = q.get_undo_list()
	for j in range(len(u)):
예제 #7
0
파일: SBLtest03.py 프로젝트: rehoot/SBList
			l.append(l[-1])
			while j < str_len and k < k_max:
				l.append(l[-1])
				k += 1
				j += 1
		else:
			l.append(chr(random.randint(ord('a'), ord('z'))))
		j += 1
	#
	# Now copy the list and test it
	#
	start_offset = random.randint(0, str_len - 3)
	end_offset = random.randint(start_offset + 1, str_len - 1)
	save_l = []
	save_l.extend(l)
	q = SBList(l)	
	q.sort(start=start_offset, end=end_offset)
	l_new = q.return_list()
	save_l2 = subsort(save_l, start_offset, end_offset)
	# The diff() function seems to remove the sort from
	# my saved list

	### TEMPORARY HACK TO TEST ERROR DETECTION:
	##save_l2[3] = 'zzTEST'
	difflist = list(difflib.ndiff(save_l2, l_new))
	if len(difflist) != len(q):
		print('================== verify failed')
		print("start idx " + str(start_offset) + ' end idx ' + str(end_offset))
		print('save:\n' + repr(save_l2))
		print('new:\n' + repr(l_new))
		raise Exception('Verify failed')
예제 #8
0
파일: SBString.py 프로젝트: rehoot/SBList
class VirtView():
	'''class VirtView()
	THIS SHOULD PROBABLY MOVE INSIDE SBSTRING OBJECT
	AND BE RENAMED.
 
	This object holds a list that contains abstracted
	pointers that allow for a logical view of the foreign
	text in an SBString object.  
	If edits are made to the foreign text, new text
	entries will be appened to the end of the foreign list but the pointers 
	here will be ordered so that the intended logical view
	of the foreign text is preserved.  

	Example: the foreign list contains the text "abcdefghij", then I 
	insert "zz" after "d", which is offset 4.  The new text ("zz")
	is appended to the end of the foreign list object
	but the entry in this object becomes:
	   [[0, 4, 0], [0, 2, 1], [4, 9, 0]]
	The entry for [0, 2, 1] means to grab bytes 0-1 (which
	is python slice s[0:2]) from the string that is stored
	at index 1 in the foreign list.
	
	*range* format: [start, end, list_offset_for_text]	
	where start and end* are python *slice()* numbers that
	point into the text that is referenced by the list offset.
	'''
	class StateEntry(object):
		start_pt = None
		end_pt = None
		list_idx = None
		length = None
		def __init__(self, start_pt, end_pt, list_idx):
			object.__init__(self)
			self.start_pt = start_pt
			self.end_pt = end_pt
			self.list_idx = list_idx 
			self.length = self.end_pt - self.start_pt
		def __repr__(self):
			return('[' + str(self.start_pt) + ', ' + str(self.end_pt) \
				+ ', '+ str(self.list_idx) + ', ' + str(self.length) + ']')

	class StateDeRef(object):
		'''class StateDeRef()
		This class will hold some values that will help to
		translate an entry in the state list to an entry
		in the main list.  

		The state list holds range entries with [start, end]
		indexes (in python slice() format) that refer to
		entries in the main list object.

		state_offset = the zero-based offset into the state
		               object.
		state_adj    = a virtual offset that would point to
		               a logical value in the range [start, end].
		               For example, if the state entry is [10, 20]
		               and state_adj = 3, then the reference is to
		               an entry in the main list at offset 13, which
		               is 10 + 3.
		list_idx     = an index into self.l that corresponds to
		               self.state[state_offset][0] + state_adj.
		'''

		state_offset = None
		state_adj = None
		str_ptr = None
		list_idx = None
		def __init__(self, s_offset, s_adj, str_ptr, l_idx):
			'''StateDeRef.__init()
			'''
			object.__init__(self)
			self.state_offset = s_offset
			self.state_adj = s_adj
			self.str_ptr = str_ptr
			self.list_idx = l_idx
		def __iter__ (self): 
			# Maybe add a flag that will lock the objects from
			# being altered when iter is active?
			self.iterindex = 4
			return(self)
		def __next__(self):
			self.iterindex -= 1
			if self.iterindex == 4:
				return(self.state_offset)
			elif self.iterindex == 3:
				return(self.state_adj)
			elif self.iterindex == 2:
				return(self.str_ptr)
			elif self.iterindex == 1:
				return(self.list_idx)
			else:
				raise StopIteration

		def __repr__(self):
			return([repr(self.state_offset) + ', ' + repr(self.state_adj) \
					+ repr(self.str_ptr) + repr(self.list_idx)])

	def __init__(self, state_entry):
		'''VirtView.__init__()
		'''
		self.state = SBList([state_entry])
		# use self.get_state_id()
		#self.state_id = 1 # NOT USED??

	def __iter__ (self): 
		# Maybe add a flag that will lock the objects from
		# being altered when iter is active?
		self.iterindex = len(self.state)
		return(self)

	def __getitem__(self, i):
		return(self.state[i])

	def __len__(self):
		'''VirtView.__len__()
		Returns the number of saved states.
		'''
		return(len(self.state))

	def __next__ (self): 
		'''VirtView.__next__
		Returns a range object, which contains
		a start offset, end offset, and an id for 
		a string stored elsewhere.  The start/end
		pair follows python slice() notation where
		end is one greater than the value that will
		be returned when used as list[start : end]
		'''
		# return an object of type xxx with each iteration.
		if self.iterindex == 0:
			raise StopIteration
		self.iterindex -= 1
		##(s_offset, s_adj, l_idx) = self._get_vl_idx(self.iterindex)
		##return(self.state[l_idx])
		return(self.state[len(self.state) - self.iterindex])

	def __repr__(self):
		return(repr(self.state))


	def _get_vl_idx(self, str_offset, state_id=-1):
		'''VirtView._get_vl_idx()
		Return pointers into state and a dereferenced
		pointer into the the foreign list for the given
		character offset.

		If the character offset is beyond the 
		virtual length of the underlying object, this
		returns [None, None, None, None], which might mean 
		to append a proposed insert at the end of the list.
		'''
		if state_id == -1: state_id = self.get_state_id()
		#assert(state_id < (len(self.state) ))
		str_len = self.get_llen(state_id=state_id)
		if str_offset >= str_len: #l_list_len:
			return([None, None, None, None])
		# temp_str_offset will be the starting offset into the string
		# in the range of pointers at this state offset.  If temp_str_offset
		# is less than the desired offset, read the next range of
		# pointers from this state and see if the span of that range
		# adds enough to temp_str_offset to contain the desired offset.
		temp_str_offset = 0	
		l_adjust = None
		#print('SBSTring. char offset=' + str(str_offset) +' state: ' + repr(self.state))
		for s_offset in range(len(self.state)):
			# For each s_offset in the current state (s_offset
			# is an index into character offset
			# ranges entered into the current state).
			#
			# 'highest_str_offset' is the highest character offset that would
			# be captured by the tuple in self.state[state_id][s_offset].
			highest_str_offset = temp_str_offset + self.state[s_offset].end_pt \
				- self.state[s_offset].start_pt - 1
			if highest_str_offset < str_offset:
				# We have not scanned forward far enough to reach the
				# desired virtual row, advance the search row index:
				temp_str_offset = highest_str_offset + 1
			else:
				# The desired string offset is in this range
				#
				# Within the specified range, I want to find the entry
				# in l that is 'offset' from the first entry in
				# the current range, so the real data will be in
				# l[state[state_id][0] + l_adjust]
				l_adjust = str_offset - temp_str_offset 

				temp_str_offset += l_adjust
				break
		# Return 
		# 1) s_offset = (state offset) an offset into the current state that contains
		#    a range that spans the specified virtual row.
		# 2) l_adjust = an offset that should be added to the contents of
		#    state[s_offset][0] to point into l to
		#    find the specifed virtual row; e.g.:
		#    l[self.state[s_offset][0] + l_adjust]
		# 3) l_offset = the desired string? offset into the text object?
		#  
		# I think str_pt is now used to point to the character offset within
		# a chunk of text, and string_id is the index of the list entry in
		# the foreign list.
		str_pt = self.state[s_offset].start_pt + l_adjust
		string_id = self.state[s_offset].list_idx 
		# IS STRING_ID THE SAME AS str_pt?
		# THIS ASSERTION FAILED: assert(string_id == str_pt)
		sd = self.StateDeRef(s_offset, l_adjust,  str_pt, string_id)
		return(sd)

	def delete_state(self, s_offset, batch=False, state_id=-1):
		'''VirtView.delete_state()
		Delete a *range* item from the state.
		'''
		if state_id == -1: state_id = self.get_state_id()
		#del self.state[s_offset]
		self.state.delete(s_offset, state_id=state_id, batch=batch)
		return(0)

	def get_item(self, idx, state_id=-1):
		if state_id == -1:
			state_id = self.get_state_id()
		return(self.state.get_item(idx, state_id))

	def get_list(self):
		return(self.state.return_list())
	
	def get_llen(self, state_id=-1):
		'''VirtView.get_llen()
		Return the length of the virtual string in the
		specified state.
		'''
		if state_id == -1: state_id = self.get_state_id()
		llen = 0
		#for s in self.state: ################################ THIS DIDN'T WORK
		startt =  datetime.datetime.now()
		#for j in range(len(self.state)):
		#	s = self.state[j]
		# The 'for s in self.state' version was 25% faster than for j in range(len(self.state
		# but is still slow. I then stored the list len in SBList and reduce run time by
		# half.
		for s in self.state:
			# Remember that the last index is in python format is
			# is one greater than the real index of what will
			# be includuded in this state element.
			#print('adding virtual len of state ' + repr(s) + ' totlen = ' + str(len(self.state)))
			#llen += s[1] - s[0]
			# The change from calculating length to storing made no difference
			llen += s.length
		endt = datetime.datetime.now()
		#dprint('llen took ' + str((endt - startt).microseconds) )#+ ' for loop count: ' + str(j))
		return(llen)

	def get_range(self, s_offset=-1, v_row=-1, state_id=-1):
		'''VirtView.get_range()
		Given either a state_offset or a virtual row index,
		return the *range* entry, which contains  
		start, end, string_id (where start and end are in
		python slice() format).

		You must pass a keyword argument for either s_offset
		or v_row.
		'''
		if state_id == -1: state_id = self.get_state_id()
		if s_offset == -1:
			if v_row == -1:
				raise Exception('You must pass either a state offset or a v_row " \
					+ "as a keyword parameter to get_range')
			else:
				#(s_offset, s_adj, l_idx, str_idx) = self._get_vl_idx(v_row)
				sd = self._get_vl_idx(v_row)
				return(self.state[sd.state_offset])
		else:
			return(self.state[s_offset])

	def get_state_id(self):
		'''VirtView.et_state_id()
		This gets the state ID from the underlying SBLIst object
		that holds the state information.

		see also self.state.incr_state_id()
		'''
		return(self.state.get_state_id())

	def insert(self, v_row, state_entry, state_id=-1, batch=False,):
		'''VirtView.insert()
		l_range is now a state_entry object

		*range* points to a slice of a foreign list, but
		the contents of that slice will be displayed 
		starting at the specified virtual row.

		I think *range* contains [start, end] but I'm not sure.
		
		Insert the range into the state list, and ideally
		I should merge state entries if the starting or ending
		values inside *range* are contiguous to those entries
		near it in the *state* list.
		'''
		times = []
		#dprint('-----------------insert start')
		if state_id == -1:
			state_id = self.get_state_id()
		v_idx = 0
		s_offset = 0
		# * a *range* is a 3-item list like [5, 8, 2], the fist two
		#            numbers of which are 
		#            a 'slice()' referece to a range of entries in 
		#            a foreign list object that can be identifed with the third nbr.
		# * s_offset points to an existing *range* entry in self.state
		# * sd.state_adj is an offset between l_range[0] and l_range[1]
		# * list_idx is an offset into the foreign table where the associated
		#            virtual item (v_row) is stored. Its value is between
		#            l_range[0] and l_range[1]
		#
		# Length of the string before insertion.
		times.append(['a', datetime.datetime.now()])	
		virtual_len = self.get_llen(state_id=state_id)
		times.append(['b', datetime.datetime.now()])	
		if virtual_len == 0:
			# The state list can be empty if the user deleted everything,
			# but point to row zero to facilitate insert()
			v_row = 0
		#print('vv, vrow = ' + str(v_row) + ' len ' + str(virtual_len) + ' state: ' + repr(self.state))
		if v_row >= virtual_len:
			# Append the new row at the end of self.state, regardless of how far 
			# v_row exceeds the length of self.state 
			self.state.append(state_entry, state_id=state_id, batch=batch)
			return(0)
		# For the given virtual row, find the index into self.state. I think
		# l_row is a character offset
		#(s_offset, s_adj, l_row, string_id) = self._get_vl_idx(v_row)
		sd = self._get_vl_idx(v_row)
		assert(sd.list_idx is not None)
		if sd.state_adj == 0:
			# The new *range* item belongs at the start of an existing range
			# so insert the new item here:
			times.append(['c', datetime.datetime.now()])
			self.state.insert(sd.state_offset, state_entry, state_id=state_id, batch=batch)
			times.append(['c2', datetime.datetime.now()])
		else:
			if self.state[sd.state_offset].end_pt == v_row:
				# The new item goes after this state entry
				times.append(['d', datetime.datetime.now()])
				self.state.insert(sd.state_offset + 1, state_entry, state_id=state_id, batch=batch)
				times.append(['d2', datetime.datetime.now()])
			else:
				# split the old range entry
				low = self.state[sd.state_offset].start_pt
				high = self.state[sd.state_offset].end_pt
				# I think str_id is an offset into the sblist for an immutable string.
				str_id = self.state[sd.state_offset].list_idx
				self.state.delete(sd.state_offset, state_id=state_id, batch=batch)
				# The deletion above should have creatd a new state for the self.state object.
				# I should now use that for the remainin transactions in this batch:
				state_id = self.state.get_state_id()
				# Insert the lower part of the state entry:
				times.append(['e', datetime.datetime.now()])
				self.state.insert(sd.state_offset, self.StateEntry(low, low + sd.state_adj, sd.list_idx), 
					batch=True, state_id=state_id)# batch is always true here
				# Insert the entry for the newly inserted text:
				self.state.insert(sd.state_offset + 1, state_entry, batch=True, state_id=state_id)
				# Insert the top half of the old range that was split
				self.state.insert(sd.state_offset+ 2, self.StateEntry(low + sd.state_adj, high, sd.list_idx), 
					batch=True, state_id=state_id)
				times.append(['e2', datetime.datetime.now()])
		#for j in range(1, len(times)):
		#	#dprint(times[j][0] + ', ' + times[j - 1][0] + ' ' 
		#	#	+ repr((times[j][1] - times[j - 1][1]).microseconds))
		return(0)


	def insert_state(self, s_offset, state_entry, state_id=-1, batch=False):
		'''VirtView.insert_state()
		Insert a range object into self.state.
		This is intended to facilitate rewriting
		the state for deletions in the virual
		representation of the underlyling list.
		see also: delete_state()
		'''
		if state_id == -1: state_id = self.get_state_id()
		self.state.insert(s_offset, state_entry, state_id=state_id, batch=batch)
		return(0)

	def set_state_id(self, id):
		self.state.set_state_id(id)
		return(0)
	
	def show_state(self, state_id):
		'''VirtView.show_state()
		return some debugging info
		'''
		#return(self.state.show_state(state_id=state_id))
		return(repr(self.state))
예제 #9
0
파일: SBString.py 프로젝트: rehoot/SBList
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))
예제 #10
0
파일: SBString.py 프로젝트: rehoot/SBList
	def __init__(self, state_entry):
		'''VirtView.__init__()
		'''
		self.state = SBList([state_entry])
예제 #11
0
파일: SBLtest04.py 프로젝트: rehoot/SBList
	l = []
	l.append(chr(random.randint(ord('a'), ord('z'))))
	str_len = random.randint(1, max_str_len)
	s = ''
	for j in range(list_len):
		s = random_string(str_len)
		l.append(s)
	#
	# Now copy the list and test it
	# (I copy the list to avoid the possibility that some
	# testing operations will change the original list and
	# thereby ruin my ability to detect changes)
	#
	save_l = []
	save_l.extend(l)
	q = SBList(l)	
	for j in range(inserts):
		# insert to the SBList and the parallel regular list
		new_s = random_string(random.randint(1,max_str_len)) + 'insrt'
		loc = random.randint(0, len(q))
		q.insert(loc, new_s)
		save_l.insert(loc, new_s)
	#
	l_new = q.return_list()

	### TEMPORARY HACK TO TEST ERROR DETECTION:
	##save_l[3] = 'zzTEST'
	difflist = list(difflib.ndiff(save_l, l_new))
	if len(difflist) != len(q):
		print('================== verify failed')
		print('save:\n' + repr(save_l))
예제 #12
0
파일: SBLtest01.py 프로젝트: rehoot/SBList
from SBList import *

# The first test will ensure that the append function works properly.
# A prior version would insert before the last entry instead of appending
sb = SBList([])
sb.append(0)
sb.append(1)
sb.append(2)
sb.append(3)
assert(sb == [0,1,2,3])
sb.insert(4, 4)
assert(sb == [0,1,2,3,4])
sb.insert(4, 3.5)
assert(sb == [0,1,2,3,3.5,4])
sb.insert(0, 0.5)
assert(sb == [0.5, 0,1,2,3,3.5,4])

# Test a simple undo and some insertions
sl = SBList([1,2,6,3,12,7])
sl.insert(2, 90)
sl.undo()
sl.insert(2, 90)
sl.insert(4, 50)
sl.insert(5, 9999999)
sl.insert(3, 20)
sl.delete(6)
sl.insert(3, 30)
sl
sl.sort()
sl
sl.delete(3)
예제 #13
0
파일: SBLtest03b.py 프로젝트: rehoot/SBList
	and ending python offsets (the value of *end* is
	used as an array index capturing value n-1)
	"""
	lstart = l[0:start]
	lend = l[end:]
	lmid = l[start:end]
	lmid.sort()
	final = []
	final.extend(lstart)
	final.extend(lmid)
	final.extend(lend)
	return(final)

l = ['a', 'g','e', 't','g','h','b','n']

q = SBList(l)
q.sort()
q.sort()
q.sort()
q.sort()
q.sort()
print(repr(q.get_undo_list()))
#####################################################################
#
#                          Testing
#
######################################################################
# generate an auto-correlated series of characters to test the
# sort routine.  The challenge is to sort a subset of input rows
# when the old sate ranges spand into or across the sort region
# of a subsort
예제 #14
0
파일: SBTests.py 프로젝트: rehoot/SBList
		print('File not found ' + new_fname)
	return(0)


debug = 0

def dprint( txt):
	global debug
	if debug > 0:
		print(txt)
	return(0)


from SBList import *
l = [1,2,6,3,12,7]
sl = SBList(l)

# check the list length routine:
assert(sl.get_llen(0) == 6)

m = []
for n in sl:
  m.append(n)

assert(m == l)

sl.insert(2, 99)
assert(sl.get_llen(1) == 7)
assert(sl.get_llen(0) == 6)
sl.insert(2, 98)
sl.insert(7, 88898)
예제 #15
0
파일: SBLtest05.py 프로젝트: rehoot/SBList
from SBList import *

import sys
print('------------------------------------------------------------')
print(sys.argv[0])

l = ['h', 'e', 'b', 'u', 's', 'p', 'x', 'a']
n = ['h', 'e', 'b', 'aaa', 's', 'p', 'x', 'a']
o = ['h', 'e', 'b', 'zzz', 's', 'p', 'x', 'a']

m = [4, 5, 7, 1, 33, 44, 22, 88, 54]

ql = SBList(l)
qn = SBList(n)
qo = SBList(o)
qm = SBList(m)

#print(repr(ql))
#ql.delete(2)
#ql.delete(2, batch=True)
#ql.delete(2, batch=True)
#print(repr(ql))
#ql.get_undo_list()
#raise Exception("temp stop")

assert(ql[3] == 'u')
assert(qm[1] == 5)
assert(ql[-1] == 'a')
assert(qm[-1] == 54)
assert(ql[2] + ql[4] == 'bs')
assert(qm[2] + qm[4] == 40)
예제 #16
0
파일: SBStrList.py 프로젝트: rehoot/SBList
class SBStrList(object):
	'''class SBStrList()
	This is a list of strings that is built
	on state-based objects that facilitate
	undo().
	'''
	class StorageRef(object):
		storage_start = None
		storage_end = None
		logical_start = None
		logical_end = None
		def __init__(self):#, storage_range, logical_range):
			object.__init__(self)
			
	class LogEntry():
			'''class SBStrList.LogEntry
			This holds information about one edit action.
			The properties are populated according to what makes
			sense for the given action.

			t_pt is a pointer into the text of the current line,
			not a global text pointer.
			sbstr will be an SBString object for line-deletion transaction 
			or line cut/paste.

			Action codes:
				'i' for insert text, 
				'd' for delete text,
				'il' = insertline, 
				'dl' = delete line. 
			Possible future codes would be 'm' for move-a-line, or 'u' for
			updated. Update is currently processed by a deletion and 
			an insert.
			'''
			action = None
			time = None
			old_lstate = None
			new_lstate = None
			old_str_state = None
			new_str_state = None
			row_idx = None
			t_pt = None
			txt = None
			sbstr = None
			def __init__(self, action, time, old_lstate, new_lstate, old_str_state, \
				new_str_state, row_idx, del_count=None, t_pt=None, txt=None, sbstr=None):
				'''UndoChanges.__init__
				'''
				self.action = action
				self.time = time
				self.old_lstate = old_lstate
				self.new_lstate = new_lstate
				self.old_str_state = old_str_state
				self.new_str_state = new_str_state
				self.row_idx = row_idx
				# for text edits only (not line insertion or deletion:
				self.t_pt = t_pt
				self.txt = txt
				# for line deletion, cut and paste
				self.sbstr # a SBString object
			def __repr__(self):
				'''log.__repr__
				'''
				return('[' + self.action + ', ' + self.time.strftime("%Y %h %d %H:%M:%S.") \
					+ str(self.time.time().microsecond) + ', ' \
					+ str(self.old_lstate) + ', ' + str(self.new_lstate) + ', ' \
					+ str(self.old_str_state) + ', ' + str(self.new_str_state) + ', ' \
					+ str(self.row_idx) + ', ' + repr(self.t_pt) + ', ' 
					+ repr(self.txt) + ']\n')
					

	def __init__(self, ListOfStrings):
		'''SBStrList.__init__()
		'''
		# MAYBE I SHOULD CODE ATTRIBUTES WITH THE VALUE OF THE
		# CURRENT INSTANCE SO EVERYTHING CAN GRAB THE CURRENT
		# STATE_ID WITHOUT REQUIRING INPUT FROM THE USER -- is that needed?
		object.__init__(self)
	
	
		# Cast the strings as SBSTring objects and load into a new list.
		# I tried embedding this into SBList, but it caused a loop in the 
		# definitions of SBList and SBString.
		self.buff_len = SBList([0])
		temp_l = []
		for j in range(len(ListOfStrings)):
			#if ListOfStrings[j][-1] not in ['\n', '\r'] \
			#and j != (len(ListOfStrings) - 1):
			#	temp_l.append(SBString(ListOfStrings[j]) + '\n')
			#else:
			if ListOfStrings[j][0:-1].count('\n') > 0:
				raise Exception('Your test string contains an embedded EOL in mid-line.')
			temp_l.append(SBString(ListOfStrings[j]))
			self.buff_len[0] += len(ListOfStrings[j])
		self.l = SBList(temp_l)
		#
		self.state_id = 0
		self.point = SBList([0])
		# The main state_id for SBStrList will be the state_id for
		# self.log?  To show the undo list of self.log, display
		# only values in the range self.log[0:self.log.get_state_id].
		# I might have to review how transactions
		# are chunked (such as 'replace' or 'update' transactions).
		self.log = SBList([]) 
		# Maybe use a list or function that lists all the 
		# objects that contain range-based lists so that users
		# can create one and have it automatically updated.
		self.range_lists = []
		self.range_funcs = []
		self.bookmarks = SBList([])
		# The *statements* field might be used to store the start and
		# end points of comment blocks or full statements in a
		# programming language. It might help the logic for applying
		# the syntax highligher by telling it to start on a prior line.
		self.statements = SBList([])
		self.range_lists.append(self.bookmarks)
		self.range_lists.append(self.statements)
		
		self.line_pts = None
		self.rebuild_line_pts()
		tmp_lp = None
		self.range_lists.append(self.line_pts)

	def __getitem__(self, x):
		# This returns an item from the main list based
		# on the virtual row index, *x*.	That index
		# can be a slice(), which is a range() specifed
		# in python format (i.e., the last index is one
		# greater than what you want returned).
		if x > len(self):
			raise IndexError
		if type(x) == type(slice(1,2)):
			temp = []
			if x.step is None:
				step = 1
			else:
				step = x.step
			for j in range(x.start, x.stop, step):
				temp.append(self.l[self.get_lrow_idx(j)])
			return(temp)
		else:
			# The requested item is not a slice:
			return(self.l[x])
			#return(self.l[self.get_lrow_idx(x)])

	def __iter__ (self): 
		# Maybe add a flag that will lock the objects from
		# being altered when iter is active?
		self.iterindex = len(self.l)
		return(self)

	def __len__(self):
		'''SBStrList.__len__()
		Returns the length of the virtual list object.
		'''
		return(len(self.l))

	
	def __next__ (self): 
		'''SBStrList.__next__()
		Return a list entry with each call.
		'''
		self.iterindex -= 1
		if self.iterindex < 0:
			raise StopIteration
		#
		# the countdown starts at the maximum value, but I want
		# to reverse the index to start at zero:
		new_idx = len(self.l) - self.iterindex - 1
		return(self.l[new_idx])

	def __repr__(self):
		#s = ''
		tmp_l = []
		for j in range(self.line_count()):
			#s += self.l[j].get_string()
			tmp_l.append(self.l[j].get_string())
		return(''.join(tmp_l))

	def __len__(self):
		return(len(self.l))

	def _dump(self, msg):
		'''SBStrList._dump()
		Dump many important objects to sys.stderr

		Run this program from the command line like this:
		env python sbed02.py -f infile.txt 2> debug.log
		where 2> is the redirection for stderr.
		'''
		dprint('---------------------------------------DUMP START')
		dprint(msg)
		dprint('')
		dprint('state_id= ' + str(self.state_id))
		dprint('point state id=' + str(self.point.get_state_id()) +' contents = ' + repr(self.point))
		dprint('log state id =' + str(self.log.get_state_id()) + ' contents=\n' + repr(self.log))
		dprint('line_pts state id= ' + str(self.line_pts.get_state_id()) + ', contents:\n' + repr(self.line_pts))
		dprint('self.l:\n' + repr(self.l))
		return(0)

	def add_bookmark(self, range):
		pass

	def add_statement(self, range):
		pass

	def add_range_list(self, lst):
		'''SBStrList.add_range_list(
		Add a list object that contains [start, end]
		pointers into the text using python slice()
		format.

		Lists entered here need corresponding entries
		loaded via add_range_updater() that are run
		via update_range_lists()
		'''
		self.rangelist.append(lst)
		return(0)
	
	def delete(self, pt, count, batch=False):
		# 1) Find the affected line
		# 2) determine if the full line is being deleted
		# 3) delete as needed
		# 4) update line_pts
		# 5) update bookmarks
		# 6) update statements
		idx = self.pt_to_line(pt)
		# Get the pointer within the appropriate text chunk:
		t_pt = self.pt_to_t_pt(pt)
		#
		save_lstate = self.l.get_state_id()
		save_str_state = self.l[idx].get_state_id()
		save_t_pt = t_pt	
		dprint('del a ' + str(self.l[idx].get_point()))
		self.l[idx].delete(t_pt,  count, batch=batch)
		dprint('del b ' + str(self.l[idx].get_point()))
		self.update_line_pts(pt, -1 * count, batch=batch)
		self.log.append(self.LogEntry('d', datetime.datetime.now(), save_lstate, \
			self.l.get_state_id(), save_str_state, \
			self.l[idx].get_state_id(), idx, t_pt=t_pt, del_count=count))
		return(0)

	def get_char(self, pt):
		idx = self.pt_to_line(pt)
		t_pt = self.pt_to_t_pt(pt)
		return(self.l[idx].get_char(t_pt))

	def get_lines(self, idx, count=1):
		pass

	def get_line_pts(self, idx):
		'''SBStrList.get_line_pts()
		return a range that contains the starting and ending
		points for the specified line in [star, end] slice()
		format.
		'''
		if idx > len(self.line_pts) or idx < 0:
			raise IndexError
		return(self.line_pts[idx])

	def get_point(self, state_id=-1):
		pass
	
	def get_str_list(self):
		'''StrList.get_str_list()
		Return a list object that contains the regular text
		stored in this object.
		'''
		tmp_l = []
		for j in range(len(self.l)):
			l.append(self.l[j].get_string())
		return(''.join(tmp_l))

	def goto_bookmark(self, bmark):
		# maybe use a dictionary to handle bookmarks,
		# so this arg would be the string name of the 
		# bookmark.
		pass	

	def insert(self, pt, txt, batch=False):
		# make all these methods so that they operate on
		# the current state (no state_id override)
		# Note: pt is the global point. t_point is the point
		# into the current text object.
		#
		# Function:
		# 1) Find the affected line
		# 2) determine if new lines are being created (embedded EOL)
		# 3) insert as needed
		# 4) update the log
		# 5) update line_pts
		# 6) update bookmarks
		# 7) update statements

		# TODO: consider keeping the EOL marker as the indicator of 
		# where existing SBString object will be copied.  In other
		# words, If I insert a block of text with embedded EOL,
		# the first part of the insertion would generate a new SBString
		# as if the existing EOL is fixed in concrete thereby forcing
		# the inserted lines to be inserted above.  This will help to
		# keep undo history predictable.  If an EOL marker is deleted,
		# then that SBString object will be deleted from the virtual view.
		idx = self.pt_to_line(pt)
		# Get the pointer within the appropriate text chunk:
		t_pt = self.pt_to_t_pt(pt)
		#
		save_lstate = self.l.get_state_id()
		save_str_state = self.l[idx].get_state_id()
		dprint('in SBStrList.insert a: str sid = ' + str(self.l[idx].get_state_id()) \
			+ ' lstateid = ' + str(self.l.get_state_id()) + ' batch is ' + repr(batch))
		save_t_pt = t_pt	
		# DO I NEED EXTRA LOGIC FOR NON-STANDARD EOL?
		lines = txt.split(os.linesep)
		end_txt = ''
		for j in range(len(lines)):
			if j == 0:
				# Insert the first part of the new text:
				self.l[idx].insert(t_pt, lines[j], batch=batch)
				dprint('in SBStrList.insert B: str sid = ' + str(self.l[idx].get_state_id()) \
					+ ' lstateid = ' + str(self.l.get_state_id()))
				self.update_line_pts(pt, len(lines[j]))
				pt += len(lines[j])
				if len(lines) > 1:
					# The insertion contains embedded EOL, so
					# delete the end of the line that was displaced by 
					# inserting the EOL (but leave the existing EOL)
					t_pt = self.pt_to_t_pt(pt)#pointer within this text chunck
					end_txt = self.l[idx].get_string()[t_pt:] 
					#print("multi-line insert, pt=" + str(pt) + ' deleting from tpt=' \
					#	+ str(t_pt) + ' endtxt= ' + end_txt)
					self.l[idx].delete(t_pt, len(end_txt), batch=True)
					self.update_line_pts(pt, -1 * len(end_txt))

					# Insert an EOL after the first insertion
					# (note that the last line in the file might not have had one)
					self.l[idx].insert(t_pt, '\n', batch=True)
					self.update_line_pts(pt, 1)
					pt += 1

					# Push the end of the insertion line to a new line.
					self.l.insert(idx + 1, SBString(end_txt ), batch=True)
					self.rebuild_line_pts()
					#self.update_line_pts(pt, len(end_txt) + 1)
					#pt += 1 #eol
			else:
				# Insert the second (or higher) line of new text.
				if len(lines[j]) != 0:
					if j == (len(lines) - 1):
						# The last portion of the split text in 'lines'
						# If it is empty, don't do anything, others insert 
						# the text into an existing SBText object
						t_pt = self.pt_to_t_pt(pt)#pointer within this text chunck
						self.l[idx + j].insert(t_pt, lines[j], batch=True)
						dprint('in SBStrList.insert B: str sid = ' + str(self.l[idx].get_state_id()) \
							+ ' lstateid = ' + str(self.l.get_state_id()))
						self.update_line_pts(pt, len(lines[j]))
						pt += len(lines[j])
					else:
						# This is a new, full line with EOL.
						# Insert a new line into the file.
						self.l.insert(idx + j, SBString(lines[j]), batch=True)
						# RUN REBUILD_LINE_PTS HERE !!!!!!!!!!!!!!!!!!!!!!!!
						self.update_line_pts(pt, len(lines[j]))
						pt += len(lines[j])
		# Now insert that end text at the end
		#if end_txt != '':
		#	t_pt = self.pt_to_t_pt(pt)#pointer within this text chunck
		#	print('Appending end, pt=' + str(pt) + ', tpt= ' + str(t_pt))
		#	print('chcking line nb ' + str(self.pt_to_line(pt)))
		#	self.l[idx].insert(t_pt, end_txt, batch=False)
		#	self.update_line_pts(pt, len(end_txt))
		#	print('line pts= ' + repr(self.line_pts))
		
			# Construct the log entry:	
		self.log.append(self.LogEntry('i', datetime.datetime.now(), save_lstate, \
			self.l.get_state_id(), save_str_state, \
			self.l[idx].get_state_id(), idx, t_pt=t_pt, txt=txt))
		self.buff_len[0] += len(txt)
		return(0)

	def insert_line(self, before_line, txt):
		pass

	def line_count(self):
		return(len(self.l))

	def list_bookmarks(self):
		pass

	def list_statements(self):
		pass

	def pt_to_line(self, pt):
		'''SBStrList.pt_to_line()
		Return the index number of the line that contains the
		specified point in the current state.
		'''
		#print(repr(self.line_pts))	
		found = False
		j = 0
		while not found and j < len(self.line_pts):
			if self.line_pts[j][0] <= pt and (self.line_pts[j][1] - 1) >= pt:
				found = True	
			j += 1
		return(j - 1)

	def pt_to_t_pt(self, pt):
		'''SBStrList.pt_to_t_pt()
		Convert a global point into the buffer to a t_pt,
		which is a zero-based index into the text at the current line
		'''
		line = self.pt_to_line(pt)
		assert( line >= 0)
		return(pt - self.line_pts[line][0])	

	def rebuild_line_pts(self):
		tmp_lp = []
		tmp_pt = 0
		for j in range(len(self.l)):
			# Record the starting and ending point on each line.
			# This is NOT in slice() format.
			tmp_lp.append([tmp_pt, tmp_pt + len(self.l[j])])
			tmp_pt += len(self.l[j])
		self.line_pts = SBList(tmp_lp)
		return(0)

	def redo(self):
		pass

	def set_bookmark(self, pt_start, len):
		pass

	def show_line_pts(self):
		'''SBStrList.show_line_pts()
		This is for debugging purposes to show the saved
		list of starting and ending points (slice() format)
		for each buffer line.
		'''
		return(repr(self.line_pts))

	def show_undo_list(self):
		return(repr(self.log))

	def set_point(self, pt):
		pass

	def str_len(self):
		'''SbStrList.str_len()
		Return the full length of the buffer in characters.
		'''
		#ln = 0
		#for sbs in self.l:
		#	ln += len(sbs)
		#return(ln)
		reuturn(self.buff_len[0])

	def list_len(self):
		return(len(self.l))

	def undo(self):
		if len(self.log) == 0:
			return(0)

		top_state_id = self.log.get_state_id()
		#top_log_entry = self.log[-1]
		#self.log[0:self.log.get_state_id]
		top_log_entry = self.log[top_state_id]
		top_action_code = top_log_entry.action
		top_idx = top_log_entry.row_idx
		# The undo() below undoes the record of the transaction
		# and thereby leaves the top entry with the l_state and
		# v_state IDs that are desired.
		# ONE UNDO ON THE LOG ENTRY WILL NOT UNDO MANY BATCH ENTRIES !!!!!!!!!!!!!!!!!!!!i!!!!!!!
		self.log.undo() #this undoes only the transaction
		if top_action_code in ['i', 'd']:
			#print('UNDO TEXT EDIT for line idx ' + str(top_idx))
			self.l[top_idx].undo(count=1)
			self.buff_len.undo()
		else:
			raise Exception ("not ready to undo line-oriented actions")
		
		pass

	def update_line_pts(self, pt, incr, batch=False):
		'''SBStrList.update_line_pts()
		Update self.line_pts to reflect inserted or deleted
		text. Do NOT call this if there are new or delete lines.
	  self.line_pts is a list of the starting points
		and ending points for each line.

		see also: rebuild_line_pts
		'''
		idx = self.pt_to_line(pt)
		# I will delete all the existing range entries and then append
		# new ones
		old_lp_state = self.line_pts.get_state_id()
		b_code = batch
		len_pts = len(self.line_pts) 
		save_line_pts = []
		for j in range(len_pts):
			# Save the current state of line_pts because if actions
			# are taken in batch mode, I'll need this to get the 
			# prior state.
			save_line_pts.append(self.line_pts[j])

		for j in range(len_pts -1, idx - 1 , -1):
			self.line_pts.delete(j, batch=b_code)
			b_code = True

		# Now insert the updated entries (there might be new or delete rows)
		for j in range(idx, len(save_line_pts)):
			# The current state of self.line_pts should contain one entry 
			# for each record in self.l. Get that entry to find the starting and
			# ending point for each line:
			low = save_line_pts[j][0]
			high = save_line_pts[j][1]
			if low <= pt:
				self.line_pts.append([low , high+ incr], batch=True)
			else:
				self.line_pts.append([low + incr, high+ incr], batch=True)

		return(0)

	def update_range_lists(self, pt, incr):
		'''SBStrList.update_ranges()
		Update all the list objects that contain ranges of 
		start/end points.  This processes anything that was
		registered with add_range_list()

		see also: rebuild_line_pts
		'''
		# MODIFY THIS TO MAKE IT GENERIC FOR THE REGISTERED LISTSV
		# MODIFY THIS TO MAKE IT GENERIC FOR THE REGISTERED LISTSV
		# MODIFY THIS TO MAKE IT GENERIC FOR THE REGISTERED LISTSV
		# MODIFY THIS TO MAKE IT GENERIC FOR THE REGISTERED LISTSV
		# MODIFY THIS TO MAKE IT GENERIC FOR THE REGISTERED LISTSV
		# MODIFY THIS TO MAKE IT GENERIC FOR THE REGISTERED LISTSV
		# MODIFY THIS TO MAKE IT GENERIC FOR THE REGISTERED LISTSV
		# MODIFY THIS TO MAKE IT GENERIC FOR THE REGISTERED LISTSV
		# MODIFY THIS TO MAKE IT GENERIC FOR THE REGISTERED LISTSV
		idx = self.pt_to_line(pt)
		# I will delete all the existing range entries and then append
		# new ones
		old_lp_state = self.line_pts.get_state_id()
		b_code = False
		for j in range(idx, len(self.line_pts)):
			self.line_pts.delete(idx, batch=b_code)
			b_code = True

		# Now insert the updated entries (there might be new or delete rows)
		for j in range(idx, len(self.l)):
			low = self.line_pts.get_item(j, state_id=old_lp_state)[0]
			high = self.line_pts.get_item(j, state_id=old_lp_state)[1]
			if low < pt:
				self.line_pts.append([low , high+ incr], batch=True)
			else:
				self.line_pts.append([low + incr, high+ incr], batch=True)

		return(0)