Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/sharedstate.git/sharedstate/sharedtext.py
diff options
context:
space:
mode:
Diffstat (limited to 'sharedstate.git/sharedstate/sharedtext.py')
-rw-r--r--sharedstate.git/sharedstate/sharedtext.py181
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)
+