D5146: histedit: import chistedit curses UI from hg-experimental
This revision was automatically updated to reflect the committed changes. Closed by commit rHGc36175456350: histedit: import chistedit curses UI from hg-experimental (authored by durin42, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D5146?vs=12322=12513 REVISION DETAIL https://phab.mercurial-scm.org/D5146 AFFECTED FILES hgext/histedit.py mercurial/ui.py CHANGE DETAILS diff --git a/mercurial/ui.py b/mercurial/ui.py --- a/mercurial/ui.py +++ b/mercurial/ui.py @@ -1245,7 +1245,11 @@ "chunkselector": [ "text", "curses", -] +], +"histedit": [ +"text", +"curses", +], } # Feature-specific interface diff --git a/hgext/histedit.py b/hgext/histedit.py --- a/hgext/histedit.py +++ b/hgext/histedit.py @@ -183,7 +183,11 @@ from __future__ import absolute_import +import fcntl +import functools import os +import struct +import termios from mercurial.i18n import _ from mercurial import ( @@ -198,6 +202,7 @@ extensions, hg, lock, +logcmdutil, merge as mergemod, mergeutil, node, @@ -235,6 +240,9 @@ configitem('histedit', 'singletransaction', default=False, ) +configitem('ui', 'interface.histedit', +default=None, +) # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should @@ -915,6 +923,562 @@ raise error.Abort(msg, hint=hint) return repo[roots[0]].node() +# Curses Support +try: +import curses +except ImportError: +curses = None + +KEY_LIST = ['pick', 'edit', 'fold', 'drop', 'mess', 'roll'] +ACTION_LABELS = { +'fold': '^fold', +'roll': '^roll', +} + +COLOR_HELP, COLOR_SELECTED, COLOR_OK, COLOR_WARN = 1, 2, 3, 4 + +E_QUIT, E_HISTEDIT = 1, 2 +E_PAGEDOWN, E_PAGEUP, E_LINEUP, E_LINEDOWN, E_RESIZE = 3, 4, 5, 6, 7 +MODE_INIT, MODE_PATCH, MODE_RULES, MODE_HELP = 0, 1, 2, 3 + +KEYTABLE = { +'global': { +'h': 'next-action', +'KEY_RIGHT': 'next-action', +'l': 'prev-action', +'KEY_LEFT': 'prev-action', +'q': 'quit', +'c': 'histedit', +'C': 'histedit', +'v': 'showpatch', +'?': 'help', +}, +MODE_RULES: { +'d': 'action-drop', +'e': 'action-edit', +'f': 'action-fold', +'m': 'action-mess', +'p': 'action-pick', +'r': 'action-roll', +' ': 'select', +'j': 'down', +'k': 'up', +'KEY_DOWN': 'down', +'KEY_UP':'up', +'J': 'move-down', +'K': 'move-up', +'KEY_NPAGE': 'move-down', +'KEY_PPAGE': 'move-up', +'0': 'goto', # Used for 0..9 +}, +MODE_PATCH: { +' ': 'page-down', +'KEY_NPAGE': 'page-down', +'KEY_PPAGE': 'page-up', +'j': 'line-down', +'k': 'line-up', +'KEY_DOWN': 'line-down', +'KEY_UP':'line-up', +'J': 'down', +'K': 'up', +}, +MODE_HELP: { +}, +} + +def screen_size(): +return struct.unpack('hh', fcntl.ioctl(1, termios.TIOCGWINSZ, '')) + +class histeditrule(object): +def __init__(self, ctx, pos, action='pick'): +self.ctx = ctx +self.action = action +self.origpos = pos +self.pos = pos +self.conflicts = [] + +def __str__(self): +# Some actions ('fold' and 'roll') combine a patch with a previous one. +# Add a marker showing which patch they apply to, and also omit the +# description for 'roll' (since it will get discarded). Example display: +# +# #10 pick 316392:06a16c25c053 add option to skip tests +# #11 ^roll 316393:71313c964cc5 +# #12 pick 316394:ab31f3973b0d include mfbt for mozilla-config.h +# #13 ^fold 316395:14ce5803f4c3 fix warnings +# +# The carets point to the changeset being folded into ("roll this +# changeset into the changeset above"). +action = ACTION_LABELS.get(self.action, self.action) +h = self.ctx.hex()[0:12] +r = self.ctx.rev() +desc = self.ctx.description().splitlines()[0].strip() +if self.action == 'roll': +desc = '' +return "#{0:<2} {1:<6} {2}:{3} {4}".format( +self.origpos, action, r, h, desc) + +def checkconflicts(self, other): +if other.pos > self.pos and other.origpos <= self.origpos: +if set(other.ctx.files()) & set(self.ctx.files()) != set(): +self.conflicts.append(other) +return self.conflicts + +if other in self.conflicts:
D5146: histedit: import chistedit curses UI from hg-experimental
pulkit accepted this revision. pulkit added a comment. Queueing this, many thanks for importing this in core. Can you add some documentation about the new config option to enable curses interface as followup? Also, how do you feel about histedit using curses interface if `ui.interface=curses` is set? REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D5146 To: durin42, #hg-reviewers, martinvonz, pulkit Cc: martinvonz, pulkit, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D5146: histedit: import chistedit curses UI from hg-experimental
pulkit added a comment. In https://phab.mercurial-scm.org/D5146#77493, @durin42 wrote: > In https://phab.mercurial-scm.org/D5146#77492, @pulkit wrote: > > > I remember using chistedit and finding that the `.hg/chisedit` file is not deleted after chistedit is completed. Looking at code, I am unable to find code which deletes that file. Can you please have a look and delete that file after a successfull or aborted chistedit? > > > That file is an implementation detail, so that histedit can pass rules to itself. It's a little gross, but not harmful. > > Could I persuade you to let us clean that up later as a follow-up? Yeah sure! REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D5146 To: durin42, #hg-reviewers, martinvonz Cc: martinvonz, pulkit, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D5146: histedit: import chistedit curses UI from hg-experimental
durin42 added a comment. In https://phab.mercurial-scm.org/D5146#77492, @pulkit wrote: > I remember using chistedit and finding that the `.hg/chisedit` file is not deleted after chistedit is completed. Looking at code, I am unable to find code which deletes that file. Can you please have a look and delete that file after a successfull or aborted chistedit? That file is an implementation detail, so that histedit can pass rules to itself. It's a little gross, but not harmful. Could I persuade you to let us clean that up later as a follow-up? REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D5146 To: durin42, #hg-reviewers, martinvonz Cc: martinvonz, pulkit, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D5146: histedit: import chistedit curses UI from hg-experimental
pulkit added a comment. I remember using chistedit and finding that the `.hg/chisedit` file is not deleted after chistedit is completed. Looking at code, I am unable to find code which deletes that file. Can you please have a look and delete that file after a successfull or aborted chistedit? REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D5146 To: durin42, #hg-reviewers, martinvonz Cc: martinvonz, pulkit, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D5146: histedit: import chistedit curses UI from hg-experimental
durin42 updated this revision to Diff 12322. REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D5146?vs=12221=12322 REVISION DETAIL https://phab.mercurial-scm.org/D5146 AFFECTED FILES hgext/histedit.py mercurial/ui.py CHANGE DETAILS diff --git a/mercurial/ui.py b/mercurial/ui.py --- a/mercurial/ui.py +++ b/mercurial/ui.py @@ -1203,7 +1203,11 @@ "chunkselector": [ "text", "curses", -] +], +"histedit": [ +"text", +"curses", +], } # Feature-specific interface diff --git a/hgext/histedit.py b/hgext/histedit.py --- a/hgext/histedit.py +++ b/hgext/histedit.py @@ -183,7 +183,11 @@ from __future__ import absolute_import +import fcntl +import functools import os +import struct +import termios from mercurial.i18n import _ from mercurial import ( @@ -198,6 +202,7 @@ extensions, hg, lock, +logcmdutil, merge as mergemod, mergeutil, node, @@ -235,6 +240,9 @@ configitem('histedit', 'singletransaction', default=False, ) +configitem('ui', 'interface.histedit', +default=None, +) # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should @@ -915,6 +923,562 @@ raise error.Abort(msg, hint=hint) return repo[roots[0]].node() +# Curses Support +try: +import curses +except ImportError: +curses = None + +KEY_LIST = ['pick', 'edit', 'fold', 'drop', 'mess', 'roll'] +ACTION_LABELS = { +'fold': '^fold', +'roll': '^roll', +} + +COLOR_HELP, COLOR_SELECTED, COLOR_OK, COLOR_WARN = 1, 2, 3, 4 + +E_QUIT, E_HISTEDIT = 1, 2 +E_PAGEDOWN, E_PAGEUP, E_LINEUP, E_LINEDOWN, E_RESIZE = 3, 4, 5, 6, 7 +MODE_INIT, MODE_PATCH, MODE_RULES, MODE_HELP = 0, 1, 2, 3 + +KEYTABLE = { +'global': { +'h': 'next-action', +'KEY_RIGHT': 'next-action', +'l': 'prev-action', +'KEY_LEFT': 'prev-action', +'q': 'quit', +'c': 'histedit', +'C': 'histedit', +'v': 'showpatch', +'?': 'help', +}, +MODE_RULES: { +'d': 'action-drop', +'e': 'action-edit', +'f': 'action-fold', +'m': 'action-mess', +'p': 'action-pick', +'r': 'action-roll', +' ': 'select', +'j': 'down', +'k': 'up', +'KEY_DOWN': 'down', +'KEY_UP':'up', +'J': 'move-down', +'K': 'move-up', +'KEY_NPAGE': 'move-down', +'KEY_PPAGE': 'move-up', +'0': 'goto', # Used for 0..9 +}, +MODE_PATCH: { +' ': 'page-down', +'KEY_NPAGE': 'page-down', +'KEY_PPAGE': 'page-up', +'j': 'line-down', +'k': 'line-up', +'KEY_DOWN': 'line-down', +'KEY_UP':'line-up', +'J': 'down', +'K': 'up', +}, +MODE_HELP: { +}, +} + +def screen_size(): +return struct.unpack('hh', fcntl.ioctl(1, termios.TIOCGWINSZ, '')) + +class histeditrule(object): +def __init__(self, ctx, pos, action='pick'): +self.ctx = ctx +self.action = action +self.origpos = pos +self.pos = pos +self.conflicts = [] + +def __str__(self): +# Some actions ('fold' and 'roll') combine a patch with a previous one. +# Add a marker showing which patch they apply to, and also omit the +# description for 'roll' (since it will get discarded). Example display: +# +# #10 pick 316392:06a16c25c053 add option to skip tests +# #11 ^roll 316393:71313c964cc5 +# #12 pick 316394:ab31f3973b0d include mfbt for mozilla-config.h +# #13 ^fold 316395:14ce5803f4c3 fix warnings +# +# The carets point to the changeset being folded into ("roll this +# changeset into the changeset above"). +action = ACTION_LABELS.get(self.action, self.action) +h = self.ctx.hex()[0:12] +r = self.ctx.rev() +desc = self.ctx.description().splitlines()[0].strip() +if self.action == 'roll': +desc = '' +return "#{0:<2} {1:<6} {2}:{3} {4}".format( +self.origpos, action, r, h, desc) + +def checkconflicts(self, other): +if other.pos > self.pos and other.origpos <= self.origpos: +if set(other.ctx.files()) & set(self.ctx.files()) != set(): +self.conflicts.append(other) +return self.conflicts + +if other in self.conflicts: +self.conflicts.remove(other) +return self.conflicts + +# EVENTS === +def movecursor(state, oldpos, newpos): +
D5146: histedit: import chistedit curses UI from hg-experimental
durin42 added a comment. Correct, no tests in hg-experimental. I did a basic sniff test, but I don't really know how we do curses tests in general. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D5146 To: durin42, #hg-reviewers, martinvonz Cc: martinvonz, pulkit, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D5146: histedit: import chistedit curses UI from hg-experimental
martinvonz accepted this revision. martinvonz added a comment. This revision is now accepted and ready to land. Would be good to have tests, but I guess there are no tests in hg-experimental either? I'm not queuing because I'm biased. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D5146 To: durin42, #hg-reviewers, martinvonz Cc: martinvonz, pulkit, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D5146: histedit: import chistedit curses UI from hg-experimental
durin42 added a comment. > pulkit added a comment. > > Is the goal is to include this in 4.8? Yes if possible. > REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D5146 To: durin42, #hg-reviewers Cc: pulkit, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D5146: histedit: import chistedit curses UI from hg-experimental
pulkit added a comment. Is the goal is to include this in 4.8? REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D5146 To: durin42, #hg-reviewers Cc: pulkit, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D5146: histedit: import chistedit curses UI from hg-experimental
durin42 updated this revision to Diff 12221. REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D5146?vs=12220=12221 REVISION DETAIL https://phab.mercurial-scm.org/D5146 AFFECTED FILES hgext/histedit.py mercurial/ui.py CHANGE DETAILS diff --git a/mercurial/ui.py b/mercurial/ui.py --- a/mercurial/ui.py +++ b/mercurial/ui.py @@ -1203,7 +1203,11 @@ "chunkselector": [ "text", "curses", -] +], +"histedit": [ +"text", +"curses", +], } # Feature-specific interface diff --git a/hgext/histedit.py b/hgext/histedit.py --- a/hgext/histedit.py +++ b/hgext/histedit.py @@ -183,7 +183,11 @@ from __future__ import absolute_import +import fcntl +import functools import os +import struct +import termios from mercurial.i18n import _ from mercurial import ( @@ -198,6 +202,7 @@ extensions, hg, lock, +logcmdutil, merge as mergemod, mergeutil, node, @@ -235,6 +240,9 @@ configitem('histedit', 'singletransaction', default=False, ) +configitem('ui', 'interface.histedit', +default=None, +) # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should @@ -916,6 +924,562 @@ raise error.Abort(msg, hint=hint) return repo[roots[0]].node() +# Curses Support +try: +import curses +except ImportError: +curses = None + +KEY_LIST = ['pick', 'edit', 'fold', 'drop', 'mess', 'roll'] +ACTION_LABELS = { +'fold': '^fold', +'roll': '^roll', +} + +COLOR_HELP, COLOR_SELECTED, COLOR_OK, COLOR_WARN = 1, 2, 3, 4 + +E_QUIT, E_HISTEDIT = 1, 2 +E_PAGEDOWN, E_PAGEUP, E_LINEUP, E_LINEDOWN, E_RESIZE = 3, 4, 5, 6, 7 +MODE_INIT, MODE_PATCH, MODE_RULES, MODE_HELP = 0, 1, 2, 3 + +KEYTABLE = { +'global': { +'h': 'next-action', +'KEY_RIGHT': 'next-action', +'l': 'prev-action', +'KEY_LEFT': 'prev-action', +'q': 'quit', +'c': 'histedit', +'C': 'histedit', +'v': 'showpatch', +'?': 'help', +}, +MODE_RULES: { +'d': 'action-drop', +'e': 'action-edit', +'f': 'action-fold', +'m': 'action-mess', +'p': 'action-pick', +'r': 'action-roll', +' ': 'select', +'j': 'down', +'k': 'up', +'KEY_DOWN': 'down', +'KEY_UP':'up', +'J': 'move-down', +'K': 'move-up', +'KEY_NPAGE': 'move-down', +'KEY_PPAGE': 'move-up', +'0': 'goto', # Used for 0..9 +}, +MODE_PATCH: { +' ': 'page-down', +'KEY_NPAGE': 'page-down', +'KEY_PPAGE': 'page-up', +'j': 'line-down', +'k': 'line-up', +'KEY_DOWN': 'line-down', +'KEY_UP':'line-up', +'J': 'down', +'K': 'up', +}, +MODE_HELP: { +}, +} + +def screen_size(): +return struct.unpack('hh', fcntl.ioctl(1, termios.TIOCGWINSZ, '')) + +class histeditrule(object): +def __init__(self, ctx, pos, action='pick'): +self.ctx = ctx +self.action = action +self.origpos = pos +self.pos = pos +self.conflicts = [] + +def __str__(self): +# Some actions ('fold' and 'roll') combine a patch with a previous one. +# Add a marker showing which patch they apply to, and also omit the +# description for 'roll' (since it will get discarded). Example display: +# +# #10 pick 316392:06a16c25c053 add option to skip tests +# #11 ^roll 316393:71313c964cc5 +# #12 pick 316394:ab31f3973b0d include mfbt for mozilla-config.h +# #13 ^fold 316395:14ce5803f4c3 fix warnings +# +# The carets point to the changeset being folded into ("roll this +# changeset into the changeset above"). +action = ACTION_LABELS.get(self.action, self.action) +h = self.ctx.hex()[0:12] +r = self.ctx.rev() +desc = self.ctx.description().splitlines()[0].strip() +if self.action == 'roll': +desc = '' +return "#{0:<2} {1:<6} {2}:{3} {4}".format( +self.origpos, action, r, h, desc) + +def checkconflicts(self, other): +if other.pos > self.pos and other.origpos <= self.origpos: +if set(other.ctx.files()) & set(self.ctx.files()) != set(): +self.conflicts.append(other) +return self.conflicts + +if other in self.conflicts: +self.conflicts.remove(other) +return self.conflicts + +# EVENTS === +def movecursor(state, oldpos, newpos): +
D5146: histedit: import chistedit curses UI from hg-experimental
durin42 created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY I don't tend to like curses interfaces, but this gets enough use at work that it seems like it's worth bringing into core. This is a minimal import from hg-experimental revision https://phab.mercurial-scm.org/rFBHGX4c7f33bf5f00b91c2caf28f095c320873584dd34, in that I've done the smallest amount of code movement and editing in order to import the functionality. .. feature:: `hg histedit` will now present a curses UI if curses is available and `ui.interface` or `ui.interface.histedit` is set to `curses`. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D5146 AFFECTED FILES hgext/histedit.py mercurial/ui.py CHANGE DETAILS diff --git a/mercurial/ui.py b/mercurial/ui.py --- a/mercurial/ui.py +++ b/mercurial/ui.py @@ -1203,7 +1203,11 @@ "chunkselector": [ "text", "curses", -] +], +"histedit": [ +"text", +"curses", +], } # Feature-specific interface diff --git a/hgext/histedit.py b/hgext/histedit.py --- a/hgext/histedit.py +++ b/hgext/histedit.py @@ -183,6 +183,7 @@ from __future__ import absolute_import +import functools import os from mercurial.i18n import _ @@ -916,6 +917,565 @@ raise error.Abort(msg, hint=hint) return repo[roots[0]].node() +# Curses Support +try: +import curses +except ImportError: +curses = None + +KEY_LIST = ['pick', 'edit', 'fold', 'drop', 'mess', 'roll'] +ACTION_LABELS = { +'fold': '^fold', +'roll': '^roll', +} + +COLOR_HELP, COLOR_SELECTED, COLOR_OK, COLOR_WARN = 1, 2, 3, 4 + +E_QUIT, E_HISTEDIT = 1, 2 +E_PAGEDOWN, E_PAGEUP, E_LINEUP, E_LINEDOWN, E_RESIZE = 3, 4, 5, 6, 7 +MODE_INIT, MODE_PATCH, MODE_RULES, MODE_HELP = 0, 1, 2, 3 + +KEYTABLE = { +'global': { +'h': 'next-action', +'KEY_RIGHT': 'next-action', +'l': 'prev-action', +'KEY_LEFT': 'prev-action', +'q': 'quit', +'c': 'histedit', +'C': 'histedit', +'v': 'showpatch', +'?': 'help', +}, +MODE_RULES: { +'d': 'action-drop', +'e': 'action-edit', +'f': 'action-fold', +'m': 'action-mess', +'p': 'action-pick', +'r': 'action-roll', +' ': 'select', +'j': 'down', +'k': 'up', +'KEY_DOWN': 'down', +'KEY_UP':'up', +'J': 'move-down', +'K': 'move-up', +'KEY_NPAGE': 'move-down', +'KEY_PPAGE': 'move-up', +'0': 'goto', # Used for 0..9 +}, +MODE_PATCH: { +' ': 'page-down', +'KEY_NPAGE': 'page-down', +'KEY_PPAGE': 'page-up', +'j': 'line-down', +'k': 'line-up', +'KEY_DOWN': 'line-down', +'KEY_UP':'line-up', +'J': 'down', +'K': 'up', +}, +MODE_HELP: { +}, +} + +def screen_size(): +import termios +from fcntl import ioctl +from struct import unpack +return unpack('hh', ioctl(1, termios.TIOCGWINSZ, '')) + +class histeditrule(object): +def __init__(self, ctx, pos, action='pick'): +self.ctx = ctx +self.action = action +self.origpos = pos +self.pos = pos +self.conflicts = [] + +def __str__(self): +# Some actions ('fold' and 'roll') combine a patch with a previous one. +# Add a marker showing which patch they apply to, and also omit the +# description for 'roll' (since it will get discarded). Example display: +# +# #10 pick 316392:06a16c25c053 add option to skip tests +# #11 ^roll 316393:71313c964cc5 +# #12 pick 316394:ab31f3973b0d include mfbt for mozilla-config.h +# #13 ^fold 316395:14ce5803f4c3 fix warnings +# +# The carets point to the changeset being folded into ("roll this +# changeset into the changeset above"). +action = ACTION_LABELS.get(self.action, self.action) +h = self.ctx.hex()[0:12] +r = self.ctx.rev() +desc = self.ctx.description().splitlines()[0].strip() +if self.action == 'roll': +desc = '' +return "#{0:<2} {1:<6} {2}:{3} {4}".format( +self.origpos, action, r, h, desc) + +def checkconflicts(self, other): +if other.pos > self.pos and other.origpos <= self.origpos: +if set(other.ctx.files()) & set(self.ctx.files()) != set(): +self.conflicts.append(other) +return self.conflicts + +if other in self.conflicts: +self.conflicts.remove(other) +