diff options
Diffstat (limited to 'sharedstate.git/sharedstate/sharedtext.py')
-rw-r--r-- | sharedstate.git/sharedstate/sharedtext.py | 181 |
1 files changed, 181 insertions, 0 deletions
diff --git a/sharedstate.git/sharedstate/sharedtext.py b/sharedstate.git/sharedstate/sharedtext.py new file mode 100644 index 0000000..384f0a8 --- /dev/null +++ b/sharedstate.git/sharedstate/sharedtext.py @@ -0,0 +1,181 @@ +# sharedobjects.py, classes to aid activities in sharing a state +# Reinier Heeres, reinier@heeres.eu +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# +# Change log: +# 2007-06-21: rwh, first version + +import types +import difflib +import logging +_logger = logging.getLogger('sharinghelper') + +from sharedobject import SharedObject + +def my_joinlines(list, addsep=False, separator='\n'): + if len(list) == 0: + return '' + string = list[0] + for s in list[1:]: + if addsep: + string += separator + string += s + return string + +def my_splitlines(str, keepsep=False, separators=['\n']): + list = [] + if str is None: + return list + ofs = 0 + startofs = 0 + while ofs < len(str): + if str[ofs] in separators: + if keepsep: + list.append(str[startofs:ofs+1]) + else: + if startofs != ofs: + list.append(str[startofs:ofs]) + else: + list.append('') + startofs = ofs + 1 + ofs += 1 + return list + +def string_to_list(str): + list = [] + if str is not None: + for i in str: + list.append(i) + return list + +def list_to_string(l): + str = "" + for i in l: + str += i + return str + +class SharedText(SharedObject): + """Shared text object, generates line-by-line difference objects""" + + REPLACE = 0 + DELETE = 1 + INSERT = 2 + + BY_CHAR = 0 + BY_WORD = 1 + BY_LINE = 2 + + def __init__(self, name, helper, opt={}, by_what=BY_CHAR): + SharedObject.__init__(self, name, helper, opt=opt) + self._value = '' + self._by_what = by_what + + def split(self, s): + if self._by_what == SharedText.BY_CHAR: + return string_to_list(s) + elif self._by_what == SharedText.BY_WORD: + return my_splitlines(s, keepsep=True, separators=['\n', ' ']) + elif self._by_what == SharedText.BY_LINE: + return my_splitlines(s, keepsep=True) + else: + _logger.error('SharedText.split(): unknown splitting type') + + def join(self, l): + if self._by_what == SharedText.BY_CHAR: + return list_to_string(l) + elif self._by_what == SharedText.BY_WORD: + return my_joinlines(l) + elif self._by_what == SharedText.BY_LINE: + return my_joinlines(l) + else: + _logger.error('SharedText.split(): unknown splitting type') + + def _compatible_diffs(self, da, db): + return True + + def diff(self, cur, old): + """Generate a difference object between to text objects""" + + _logger.debug('Generating diff between %r and %r', cur, old) + + l1 = self.split(cur) + l2 = self.split(old) + sm = difflib.SequenceMatcher(None, l2, l1) + ret = [] + for (tag, i1, i2, j1, j2) in sm.get_opcodes(): + if tag is 'replace': + ret.append((SharedText.REPLACE, (i1, i2, j1, j2), l1[j1:j2])) + elif tag is 'delete': + ret.append((SharedText.DELETE, (i1, i2, j1, j2), None)) + elif tag is 'insert': + ret.append((SharedText.INSERT, (i1, i2, j1, j2), l1[j1:j2])) + elif tag is 'equal': + pass + else: + _logger.warning('SharedText.diff(): unkown tag: %s', tag) + + if len(ret) is 0: + return None + else: + return ret + + def _apply_diff_to(self, obj, diffobj): + """Apply a diff and return an object that describes the inverse diff""" + + if diffobj is None: + _logger.error('SharedText.apply_diff_to(): no diffobj given') + return (None, None) + + _logger.debug('Applying %r to %r', diffobj, obj) + ret = [] + d = 0 + l2 = self.split(obj) + for (tag, (i1, i2, j1, j2), val) in diffobj: + i1 -= d + i2 -= d +# print 'd: %r' % (d) + if tag is SharedText.REPLACE: + ret.append((SharedText.REPLACE, (j1, j2, i1, i2), l2[i1:i2])) + l2[i1:i2] = val + d -= (j2 - j1) - (i2 - i1) + elif tag is SharedText.DELETE: + ret.append((SharedText.INSERT, (j1, j2, i1, i2), l2[i1:i2])) + del(l2[i1:i2]) + d += i2 - i1 + elif tag is SharedText.INSERT: + ret.append((SharedText.DELETE, (j1, j2, i1, i2), None)) + l2[i1:i1] = val + d -= j2 - j1 + + obj = self.join(l2) + return (obj, ret) + + + def insert(self, ofs, str): + self._value = self._value[:ofs] + str + self._value[ofs:] + dobj = {"type": 'chars', "data": (SharedText.INSERT, ofs, str)} + self.changed(dobj, True) + + def remove(self, ofs, len): + del self._value[ofs:ofs+len] + dobj = {"type": 'chars', "data": (SharedText.REMOVE, ofs, len)} + self.changed(dobj, True) + + def replace(self, ofs, str): + self._value[ofs:ofs+len(str)] = str + dobj = {"type": 'chars', "data": (SharedText.REPLACE, ofs, str)} + self.changed(dobj, True) + |