sapling/hgext3rd/edrecord.py
TJ 9b91e50f77 edrecord: allow specifying a different editor for chunk selection
Summary:
a user may want to use a special-purpose 'editor' to do chunk selection, but not
have to use that same 'editor' to write commit messages and resolve merge conflicts

This is hacked together to make it work.  I'm not super familiar with the correct way to do this in python.

Test Plan: added test case

Reviewers: rmcelroy, simonfar, most, quark

Reviewed By: quark

Subscribers: mjpieters

Differential Revision: https://phabricator.intern.facebook.com/D4832207

Tasks: 15728734

Signature: t1:4832207:1491519433:82a3e62d5ae03f456abe97caf1bd352a02fe35c3
2017-04-06 16:49:27 -07:00

112 lines
3.7 KiB
Python

# edrecord.py
#
# Copyright 2017 Facebook, Inc.
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
from mercurial import (
cmdutil,
crecord as crecordmod,
patch as patchmod,
util,
)
from mercurial.i18n import _
testedwith = 'ships-with-fb-hgext'
stringio = util.stringio
originaldorecord = cmdutil.dorecord
originalrecordfilter = cmdutil.recordfilter
def uisetup(ui):
# "editor" is otherwise not allowed as a valid option for "ui.interface"
class edrecordui(ui.__class__):
def interface(self, feature):
if feature == "chunkselector":
if (self.config("ui", "interface.%s" % feature, None) ==
"editor"):
return "editor"
elif self.config("ui", "interface.%s" % feature, None) is None:
if self.config("ui", "interface", None) == "editor":
return "editor"
return super(edrecordui, self).interface(feature)
ui.__class__ = edrecordui
cmdutil.recordfilter = recordfilter
cmdutil.dorecord = dorecord
def dorecord(ui, repo, commitfunc, cmdsuggest, backupall,
filterfn, *pats, **opts):
if ui.interface("chunkselector") != "editor":
return originaldorecord(ui, repo, commitfunc, cmdsuggest, backupall,
filterfn, *pats, **opts)
overrides = {('ui', 'interactive'): True}
with ui.configoverride(overrides, 'edrecord'):
originaldorecord(ui, repo, commitfunc, cmdsuggest, backupall, filterfn,
*pats, **opts)
def recordfilter(ui, headers, operation=None):
if ui.interface("chunkselector") != "editor":
return originalrecordfilter(ui, headers, operation)
# construct diff string from headers
if len(headers) == 0:
return [], {}
patch = stringio()
patch.write(crecordmod.diffhelptext)
specials = {}
for header in headers:
patch.write('#\n')
if header.special():
# this is useful for special changes, we are able to get away with
# only including the parts of headers that offer useful info
specials[header.filename()] = header
for h in header.header:
if h.startswith('index '):
# starting at 'index', the headers for binary files tend to
# stop offering useful info for the viewer
patch.write(_("""\
# this modifies a binary file (all or nothing)
"""))
break
if not h.startswith('diff '):
# For specials, we only care about the filename header.
# The rest can be displayed as comments
patch.write('# ')
patch.write(h)
else:
header.write(patch)
for hunk in header.hunks:
hunk.write(patch)
patcheditor = ui.config('ui', 'editor.chunkselector', None)
if patcheditor is not None:
override = {('ui', 'editor'): patcheditor}
else:
override = {}
with ui.configoverride(override):
patch = ui.edit(patch.getvalue(), "", extra={'suffix': '.diff'})
# remove comments from patch
# if there's an empty line, add a space to it
patch = [(line if len(line) > 0 else ' ') + '\n'
for line in patch.splitlines() if not line.startswith('#')]
headers = patchmod.parsepatch(patch)
applied = {}
for h in headers:
if h.filename() in specials:
h = specials[h.filename()]
applied[h.filename()] = [h] + h.hunks
return (sum([i for i in applied.itervalues()
if i[0].special() or len(i) > 1], []), {})