Vim: use Levenshtein line-wise and hunk-wise
This commit is contained in:
parent
742c479dcd
commit
7936f6b3a7
1 changed files with 133 additions and 15 deletions
|
@ -1,4 +1,4 @@
|
||||||
import sys
|
from hunk_changes import InlineLevenshtein, HunkLevenshtein
|
||||||
|
|
||||||
|
|
||||||
class LineMovement:
|
class LineMovement:
|
||||||
|
@ -30,6 +30,8 @@ class LineMovement:
|
||||||
class VimSession:
|
class VimSession:
|
||||||
""" A Vim session instrumented through tmux """
|
""" A Vim session instrumented through tmux """
|
||||||
|
|
||||||
|
debug = False
|
||||||
|
|
||||||
def __init__(self, tmux_session, file_path=None):
|
def __init__(self, tmux_session, file_path=None):
|
||||||
self.tmux_session = tmux_session
|
self.tmux_session = tmux_session
|
||||||
self.file_path = file_path
|
self.file_path = file_path
|
||||||
|
@ -39,10 +41,65 @@ class VimSession:
|
||||||
self.tmux_session.type_keys(" {}".format(self.file_path))
|
self.tmux_session.type_keys(" {}".format(self.file_path))
|
||||||
self.tmux_session.type_keys("enter")
|
self.tmux_session.type_keys("enter")
|
||||||
|
|
||||||
self.tmux_session.send_keys(":set paste", "enter")
|
self.mode = "command"
|
||||||
|
|
||||||
|
self.log_open = False
|
||||||
|
|
||||||
|
def log(self, msg):
|
||||||
|
if not self.debug:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.set_mode("command")
|
||||||
|
if not self.log_open:
|
||||||
|
self.tmux_session.send_keys(":new", "enter", "C-w", "j")
|
||||||
|
self.log_open = True
|
||||||
|
|
||||||
|
self.tmux_session.send_keys("C-w", "k")
|
||||||
|
self.tmux_session.send_keys("o", msg.rstrip(), "escape")
|
||||||
|
self.tmux_session.send_keys("C-w", "j")
|
||||||
|
|
||||||
|
def set_mode(self, new_mode, dry_run=False, ofs_balance=True):
|
||||||
|
""" Sets Vim to mode `new_mode`. Returns the resulting cursor movement, in
|
||||||
|
column offset. If `dry_run`, do not actually change the mode, just compute the
|
||||||
|
offset. If `ofs_balance`, balance the induced offset with appropriate cursor
|
||||||
|
movement. """
|
||||||
|
|
||||||
|
if new_mode == self.mode:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
if dry_run and ofs_balance:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
ofs = 0
|
||||||
|
|
||||||
|
if self.mode != "command":
|
||||||
|
ofs -= 1
|
||||||
|
|
||||||
|
if not dry_run:
|
||||||
|
self.tmux_session.type_keys("escape")
|
||||||
|
self.mode = "command"
|
||||||
|
|
||||||
|
if ofs_balance and ofs: # implies `not dry_run`
|
||||||
|
if ofs < 0:
|
||||||
|
self.tmux_session.type_keys("{}l".format(-ofs))
|
||||||
|
else:
|
||||||
|
self.tmux_session.type_keys("{}h".format(ofs))
|
||||||
|
ofs = 0
|
||||||
|
|
||||||
|
if new_mode == "insert":
|
||||||
|
if not dry_run:
|
||||||
|
self.tmux_session.type_keys("i")
|
||||||
|
self.mode = "insert"
|
||||||
|
elif new_mode == "replace":
|
||||||
|
if not dry_run:
|
||||||
|
self.tmux_session.type_keys("R")
|
||||||
|
self.mode = "replace"
|
||||||
|
|
||||||
|
return ofs
|
||||||
|
|
||||||
def edit_file(self, file_path):
|
def edit_file(self, file_path):
|
||||||
self.tmux_session.type_keys("escape", ":e ", file_path, "enter")
|
self.set_mode("command")
|
||||||
|
self.tmux_session.type_keys(":e ", file_path, "enter")
|
||||||
self.file_path = file_path
|
self.file_path = file_path
|
||||||
|
|
||||||
def apply_patchset(self, patchset):
|
def apply_patchset(self, patchset):
|
||||||
|
@ -53,48 +110,109 @@ class VimSession:
|
||||||
source = patch.source.decode("utf8")
|
source = patch.source.decode("utf8")
|
||||||
target = patch.target.decode("utf8")
|
target = patch.target.decode("utf8")
|
||||||
if source != target:
|
if source != target:
|
||||||
self.tmux_session.type_keys(
|
self.set_mode("command")
|
||||||
"escape", ":!mv ", source, " ", target, "enter",
|
self.tmux_session.type_keys(":!mv ", source, " ", target, "enter")
|
||||||
)
|
|
||||||
if self.file_path != target:
|
if self.file_path != target:
|
||||||
self.edit_file(target)
|
self.edit_file(target)
|
||||||
|
|
||||||
for hunk in patch:
|
for hunk in patch:
|
||||||
self.apply_hunk(hunk)
|
self.apply_hunk(hunk)
|
||||||
|
|
||||||
self.tmux_session.type_keys("escape", ":w", "enter")
|
self.set_mode("command")
|
||||||
|
self.tmux_session.type_keys(":w", "enter")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def tabify(text):
|
||||||
|
""" Substitute groups of four spaces by a tabulation, as much as possible. """
|
||||||
|
return text.replace(" ", "\t")
|
||||||
|
|
||||||
def write_line(self, line):
|
def write_line(self, line):
|
||||||
""" Write a line to the vim buffer, assuming everything is set up for it and it
|
""" Write a line to the vim buffer, assuming everything is set up for it and it
|
||||||
must be insterted above. """
|
must be insterted above. """
|
||||||
|
|
||||||
|
line = line.rstrip()
|
||||||
|
|
||||||
if line.startswith(" "):
|
if line.startswith(" "):
|
||||||
lead_spaces = 0
|
lead_spaces = 0
|
||||||
while lead_spaces < len(line) and line[lead_spaces] == " ":
|
while lead_spaces < len(line) and line[lead_spaces] == " ":
|
||||||
lead_spaces += 1
|
lead_spaces += 1
|
||||||
line = line.strip()
|
line = line.strip()
|
||||||
|
|
||||||
|
self.set_mode("command")
|
||||||
self.tmux_session.type_keys(
|
self.tmux_session.type_keys(
|
||||||
"O", "escape", "{}a ".format(lead_spaces), "escape", "A", line, "escape"
|
"O", "escape", "{}a ".format(lead_spaces), "escape", "A", line, "escape"
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
self.tmux_session.type_keys("O", line, "escape")
|
self.tmux_session.type_keys("O", line, "escape")
|
||||||
|
|
||||||
|
def subst_line(self, pre, post):
|
||||||
|
""" Substitute the current line of the vim buffer, assuming everything is set
|
||||||
|
up for it """
|
||||||
|
|
||||||
|
line_levenshtein = InlineLevenshtein(pre, post).compute()
|
||||||
|
ops = line_levenshtein["ops"]
|
||||||
|
|
||||||
|
edit_pos = 1
|
||||||
|
rel_pos = 0
|
||||||
|
|
||||||
|
for op, (_, span), values, _ in ops:
|
||||||
|
if op == "L":
|
||||||
|
edit_pos += span
|
||||||
|
rel_pos += span
|
||||||
|
else:
|
||||||
|
if rel_pos > 0:
|
||||||
|
self.set_mode("command")
|
||||||
|
self.tmux_session.type_keys("{}|".format(edit_pos))
|
||||||
|
rel_pos = 0
|
||||||
|
if op == "I":
|
||||||
|
self.set_mode("insert")
|
||||||
|
self.tmux_session.type_keys(self.tabify(values))
|
||||||
|
edit_pos += span
|
||||||
|
elif op == "D":
|
||||||
|
self.set_mode("command")
|
||||||
|
self.tmux_session.type_keys("x")
|
||||||
|
elif op == "S":
|
||||||
|
self.set_mode("replace")
|
||||||
|
self.tmux_session.type_keys(self.tabify(values[1]))
|
||||||
|
edit_pos += span
|
||||||
|
|
||||||
|
self.set_mode("command")
|
||||||
|
|
||||||
def apply_hunk(self, hunk):
|
def apply_hunk(self, hunk):
|
||||||
# So far, very naive.
|
pre_lines = []
|
||||||
|
post_lines = []
|
||||||
|
for b_line in hunk.text:
|
||||||
|
u_line = b_line.decode("utf8")
|
||||||
|
op = u_line[0]
|
||||||
|
line = u_line[1:]
|
||||||
|
|
||||||
|
if op in ["+", " "]:
|
||||||
|
post_lines.append(line)
|
||||||
|
if op in ["-", " "]:
|
||||||
|
pre_lines.append(line)
|
||||||
|
hunk_levenshtein = HunkLevenshtein(pre_lines, post_lines).compute()
|
||||||
|
line_ops = hunk_levenshtein["ops"]
|
||||||
|
|
||||||
line_mvt = LineMovement(absolute=hunk.starttgt)
|
line_mvt = LineMovement(absolute=hunk.starttgt)
|
||||||
|
|
||||||
for b_line in hunk.text:
|
for op, positions, values, cost in line_ops:
|
||||||
line = b_line.decode("utf8")
|
if op == "L":
|
||||||
if line[0] == " ":
|
self.log("LEAVE {} -- L{}".format(values[0].strip(), line_mvt.absolute))
|
||||||
line_mvt += 1
|
line_mvt += 1
|
||||||
else:
|
else:
|
||||||
line_mvt.do(self.tmux_session)
|
line_mvt.do(self.tmux_session)
|
||||||
if line[0] == "-":
|
if op == "I":
|
||||||
|
self.log("INSERT {}".format(values.strip()))
|
||||||
|
self.write_line(values)
|
||||||
|
line_mvt += 1
|
||||||
|
elif op == "D":
|
||||||
|
self.log("DELETE {}".format(values.strip()))
|
||||||
self.tmux_session.type_keys("dd")
|
self.tmux_session.type_keys("dd")
|
||||||
elif line[0] == "+":
|
elif op == "S":
|
||||||
self.write_line(line.strip()[1:])
|
self.log("SUBST {}/{}".format(values[0].strip(), values[1].strip()))
|
||||||
|
self.subst_line(values[0].rstrip(), values[1].rstrip())
|
||||||
line_mvt += 1
|
line_mvt += 1
|
||||||
|
|
||||||
def quit(self):
|
def quit(self):
|
||||||
self.tmux_session.type_keys("escape", ":q", "enter")
|
self.set_mode("command")
|
||||||
|
self.tmux_session.type_keys(":qa!", "enter")
|
||||||
|
|
Loading…
Reference in a new issue