Skip to content

Commit

Permalink
Allow editing of conflicting values with an editor
Browse files Browse the repository at this point in the history
Also improves the way long conflicting values are displayed
  • Loading branch information
agateau committed Nov 10, 2016
1 parent 67fd462 commit f2f8cc8
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 5 deletions.
61 changes: 60 additions & 1 deletion yokadi/tests/textpulluitestcase.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import textwrap
import unittest

from tempfile import TemporaryDirectory
Expand All @@ -6,7 +7,8 @@
from yokadi.tests.pulltestcase import createBothModifiedConflictFixture
from yokadi.tests.pulltestcase import createModifiedDeletedConflictFixture
from yokadi.ycli import tui
from yokadi.ycli.synccmd import TextPullUi
from yokadi.ycli.synccmd import TextPullUi, prepareConflictText, shortenText, SHORTENED_SUFFIX, \
SHORTENED_TEXT_MAX_LENGTH
from yokadi.sync import ALIASES_DIRNAME
from yokadi.sync.syncmanager import SyncManager

Expand Down Expand Up @@ -50,3 +52,60 @@ def testAddRename(self):
self.assertEqual(renames, {
ALIASES_DIRNAME: [("a", "a_1"), ("b", "b_1")]
})

def testPrepareConflictText(self):
data = (
("foo", "bar", "L> foo\nR> bar\n"),
(
textwrap.dedent("""\
Common
Local1
More common
Local2
Local3
Even more common"""),
textwrap.dedent("""\
Common
Remote1
More common
Remote2
Even more common"""),
textwrap.dedent("""\
Common
L> Local1
R> Remote1
More common
R> Remote2
L> Local2
L> Local3
Even more common
"""),
)
)

for local, remote, expected in data:
output = prepareConflictText(local, remote)
self.assertEqual(output, expected)

def testShortenText(self):
data = (
("foo", "foo"),
(
textwrap.dedent("""\
Common
Local1
More common
Local2
Local3
Even more common"""),
"Common" + SHORTENED_SUFFIX
),
(
"a" * 160,
"a" * (SHORTENED_TEXT_MAX_LENGTH - len(SHORTENED_SUFFIX)) + SHORTENED_SUFFIX
)
)

for src, expected in data:
output = shortenText(src)
self.assertEqual(output, expected)
50 changes: 46 additions & 4 deletions yokadi/ycli/synccmd.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import os
from cmd import Cmd
from collections import defaultdict
from difflib import Differ

from yokadi.core import basepaths
from yokadi.core.yokadioptionparser import YokadiOptionParser
Expand All @@ -12,6 +13,12 @@
from yokadi.ycli import tui


LOCAL_PREFIX = "L> "
REMOTE_PREFIX = "R> "

SHORTENED_SUFFIX = " (...)"
SHORTENED_TEXT_MAX_LENGTH = 40

# Keys are a tuple of (prompt, fieldName)
HEADER_INFO = {
ALIASES_DIRNAME: ("Alias named \"{}\"", "name"),
Expand All @@ -32,6 +39,37 @@ def printConflictObjectHeader(obj):
print("\n# {}".format(prompt))


def prepareConflictText(local, remote):
differ = Differ()
diff = differ.compare(local.splitlines(keepends=True),
remote.splitlines(keepends=True))
lines = []
for line in diff:
code = line[0]
rest = line[2:]
if rest[-1] != "\n":
rest += "\n"
if code == "?":
continue
if code == "-":
lines.append(LOCAL_PREFIX + rest)
elif code == "+":
lines.append(REMOTE_PREFIX + rest)
else:
lines.append(rest)
return "".join(lines)


def shortenText(text):
"""Takes a potentially multi-line text and returns a one-line, shortened version of it"""
cr = text.find("\n")
if cr >= 0:
text = text[:cr]
if cr >= 0 or len(text) > SHORTENED_TEXT_MAX_LENGTH:
text = text[:SHORTENED_TEXT_MAX_LENGTH - len(SHORTENED_SUFFIX)] + SHORTENED_SUFFIX
return text


class TextPullUi(PullUi):
def __init__(self):
self._renames = defaultdict(list)
Expand All @@ -53,16 +91,20 @@ def resolveBothModifiedObject(self, obj):
printConflictObjectHeader(obj)
for key in set(obj.conflictingKeys):
oldValue = obj.ancestor[key]
print("\nConflict on \"{}\" key. Old value was \"{}\".\n".format(key, oldValue))
print("\nConflict on \"{}\" key. Old value was \"{}\".\n".format(key, shortenText(oldValue)))
answers = (
(1, "Local value: \"{}\"".format(obj.local[key])),
(2, "Remote value: \"{}\"".format(obj.remote[key]))
(1, "Local value: \"{}\"".format(shortenText(obj.local[key]))),
(2, "Remote value: \"{}\"".format(shortenText(obj.remote[key]))),
(3, "Edit"),
)
answer = tui.selectFromList(answers, prompt="Which version do you want to keep".format(key), default=None)
if answer == 1:
value = obj.local[key]
else:
elif answer == 2:
value = obj.remote[key]
else:
conflictText = prepareConflictText(obj.local[key], obj.remote[key])
value = tui.editText(conflictText)
obj.selectValue(key, value)

def resolveModifiedDeletedObject(self, obj):
Expand Down

0 comments on commit f2f8cc8

Please sign in to comment.