Patchwork hgext: add smb extension

login
register
mail settings
Submitter Sean Farley
Date April 1, 2015, 6:28 p.m.
Message ID <35cf765458f06a7651d0.1427912935@1.0.0.127.in-addr.arpa>
Download mbox | patch
Permalink /patch/8424/
State Accepted
Headers show

Comments

Sean Farley - April 1, 2015, 6:28 p.m.
# HG changeset patch
# User Sean Farley <sean@farley.io>
# Date 1427912211 25200
#      Wed Apr 01 11:16:51 2015 -0700
# Node ID 35cf765458f06a7651d0e9ba9340d46369741616
# Parent  1b97cc5d2272c272961cc3e1d738e521af012a40
hgext: add smb extension

This extension brings the missing component of DVCS to Mercurial: sound
effects. Who doesn't want the sound of a vine growing when they 'hg log' or the
sound of a warping down a pipe when updating to an ancestor? I'll tell you.
Nobody.

For reference and a screencast, the extension is hosted at:

https://bitbucket.org/seanfarley/smb
Kyle Lippincott - April 1, 2015, 7:59 p.m.
:( I thought that this was going to implement hg-vss, a DVCS on top of
Visual Source Safe (which is on top of CIFS/SMB).

On Wed, Apr 1, 2015 at 11:28 AM, Sean Farley <sean@farley.io> wrote:

> # HG changeset patch
> # User Sean Farley <sean@farley.io>
> # Date 1427912211 25200
> #      Wed Apr 01 11:16:51 2015 -0700
> # Node ID 35cf765458f06a7651d0e9ba9340d46369741616
> # Parent  1b97cc5d2272c272961cc3e1d738e521af012a40
> hgext: add smb extension
>
> This extension brings the missing component of DVCS to Mercurial: sound
> effects. Who doesn't want the sound of a vine growing when they 'hg log'
> or the
> sound of a warping down a pipe when updating to an ancestor? I'll tell you.
> Nobody.
>
> For reference and a screencast, the extension is hosted at:
>
> https://bitbucket.org/seanfarley/smb
>
> diff --git a/hgext/smb.py b/hgext/smb.py
> new file mode 100644
> --- /dev/null
> +++ b/hgext/smb.py
> @@ -0,0 +1,209 @@
> +import inspect
> +import os
> +import subprocess
> +
> +from mercurial import _
> +from mercurial import commands
> +from mercurial import error
> +from mercurial import extensions
> +from mercurial import dispatch
> +from mercurial import hg
> +from mercurial import obsolete
> +from mercurial import scmutil
> +from mercurial import util
> +
> +######################
> +# audio player methods
> +######################
> +
> +def is_exe(fpath):
> +    return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
> +
> +def which(program):
> +    fpath, fname = os.path.split(program)
> +    if fpath:
> +        if is_exe(program):
> +            return program
> +    else:
> +        for path in os.environ["PATH"].split(os.pathsep):
> +            path = path.strip('"')
> +            exe_file = os.path.join(path, program)
> +            if is_exe(exe_file):
> +                return exe_file
> +
> +    return None
> +
> +def audioplayer():
> +    for p in ('afplay', 'aplay', 'mpg123'):
> +        found = which(p)
> +        if found is not None:
> +            return found
> +    return None
> +
> +def play(repo, sound):
> +    if repo is None or not repo.ui.interactive():
> +        return
> +    player = audioplayer()
> +    hasplayed = False
> +    if os.path.exists(repo.join('smbrebasehack')):
> +        os.remove(repo.join('smbrebasehack'))
> +        hasplayed = True
> +
> +    if player and not hasplayed:
> +        datapath = os.path.dirname(os.path.realpath(__file__))
> +        datapath = os.path.join(datapath, 'data')
> +        if not sound.endswith('.wav'):
> +            sound = sound + '.wav'
> +        subprocess.Popen([player, os.path.join(datapath, sound)],
> +                         stdout=open(os.devnull, 'w'),
> +                         stderr=subprocess.STDOUT)
> +
> +##############################
> +# mercurial extension wrappers
> +##############################
> +
> +def extsetup(ui):
> +    extensions.wrapfunction(dispatch, 'runcommand', wrapruncommand)
> +    extensions.wrapfunction(hg, 'updaterepo', wrapupdate)
> +
> +    extensions.wrapcommand(commands.table, 'log', wraplog)
> +    extensions.wrapcommand(commands.table, 'commit', wrapcommit)
> +
> +    try:
> +        rebase = extensions.find('rebase')
> +        if rebase:
> +            extensions.wrapcommand(rebase.cmdtable, 'rebase', wraprebase)
> +    except:
> +        pass
> +
> +    try:
> +        histedit = extensions.find('histedit')
> +        if histedit:
> +            extensions.wrapfunction(histedit, 'finishfold', wrapfold)
> +    except:
> +        pass
> +
> +    try:
> +        purge = extensions.find('purge')
> +        if purge:
> +            extensions.wrapcommand(purge.cmdtable, 'purge', wrappurge)
> +    except:
> +        pass
> +
> +    try:
> +        evolve = extensions.find('evolve')
> +        if evolve:
> +            extensions.wrapcommand(evolve.cmdtable, 'prune', wrapprune)
> +            extensions.wrapcommand(evolve.cmdtable, 'amend', wrapamend)
> +    except:
> +        pass
> +
> +def wrapruncommand(orig, lui, repo, cmd, fullargs, ui, options, d,
> cmdpats,
> +                   cmdoptions):
> +    try:
> +        ret = orig(lui, repo, cmd, fullargs, ui, options, d, cmdpats,
> +                   cmdoptions)
> +        return ret
> +    except Exception as e:
> +        # things to skip:
> +        # - skip if broken pipe (such as quiting from a pager)
> +        # - histedit in progress
> +        if ((not util.safehasattr(e, 'errno') or e.errno != 32)
> +            and e.message != 'histedit in progress'
> +            and e.message != 'rebase in progress'):
> +            if isinstance(e, error.InterventionRequired):
> +                play(repo, 'smb_pause')
> +            else:
> +                play(repo, 'smb_gameover')
> +        raise
> +
> +def wrapupdate(orig, repo, node, overwrite):
> +    # copied from merge.update which should be extracted
> +    wc = repo[None]
> +    p1 = wc.parents()[0]
> +    if node is None:
> +            try:
> +                node = repo.branchtip(wc.branch())
> +            except error.RepoLookupError:
> +                if wc.branch() == 'default':
> +                    node = repo.lookup('tip')
> +                else:
> +                    raise util.Abort(_("branch %s not found") %
> wc.branch())
> +
> +            if p1.obsolete() and not p1.children():
> +                # allow updating to successors
> +                successors = obsolete.successorssets(repo, p1.node())
> +                if successors:
> +                    successors = [n for sub in successors for n in sub]
> +                    node = repo.revs('max(%ln)', successors).first()
> +
> +    current = repo['.']
> +    dest = repo[node]
> +
> +    # things to skip:
> +    # - if current == dest
> +    # - if we are in histedit
> +    # - if we are in a rebase
> +    skip = False
> +    for frame in inspect.stack():
> +        for s in ['rebase', 'hist', 'prune']:
> +            if s in frame[3]:
> +                skip = s
> +        if skip:
> +            break
> +
> +    if current != dest and not skip:
> +        # are we moving from an ancestor to a descendant?
> +        if current.descendant(dest):
> +            play(repo, 'smb_flagpole')
> +        # or from a descendat to ancestor?
> +        elif dest.descendant(current):
> +            play(repo, 'smb_pipe')
> +        # or jumping across branches?
> +        else:
> +            play(repo, 'smb_jump')
> +    return orig(repo, node, overwrite)
> +
> +def wraprebase(orig, ui, repo, **opts):
> +    ret = orig(ui, repo, **opts)
> +    play(repo, 'smb_die')
> +    f = repo.vfs('smbrebasehack', 'w')
> +    f.write("True")
> +    f.close()
> +    return ret
> +
> +def wraplog(orig, ui, repo, *args, **opts):
> +    # double check rev exists so we don't play two sounds
> +    rev = opts.get('rev')
> +    skip = False
> +    if rev and not scmutil.revrange(repo, opts.get('rev')):
> +        skip = True
> +    if not skip:
> +        play(repo, 'smb_vine')
> +    return orig(ui, repo, *args, **opts)
> +
> +def wrapcommit(orig, ui, repo, *args, **opts):
> +    if opts.get('amend'):
> +        play(repo, 'smb_kick')
> +    else:
> +        play(repo, 'smb_1up')
> +    return orig(ui, repo, *args, **opts)
> +
> +def wrapfold(orig, ui, repo, ctx, oldctx, newnode, opts, internalchanges):
> +    play(repo, 'smb_stomp')
> +    return orig(ui, repo, ctx, oldctx, newnode, opts, internalchanges)
> +
> +def wrappurge(orig, ui, repo, *dirs, **opts):
> +    play(repo, 'smb_bowserfire')
> +    return orig(ui, repo, *dirs, **opts)
> +
> +def wrapprune(orig, ui, repo, *revs, **opts):
> +    if opts.get('succ'):
> +        play(repo, 'smb_kick')
> +    else:
> +        play(repo, 'smb_fireball')
> +    return orig(ui, repo, *revs, **opts)
> +
> +def wrapamend(orig, ui, repo, *pats, **opts):
> +    play(repo, 'smb_kick')
> +    return orig(ui, repo, *pats, **opts)
> _______________________________________________
> Mercurial-devel mailing list
> Mercurial-devel@selenic.com
> http://selenic.com/mailman/listinfo/mercurial-devel
>
Matt Mackall - April 1, 2015, 9:57 p.m.
On Wed, 2015-04-01 at 11:28 -0700, Sean Farley wrote:
> # HG changeset patch
> # User Sean Farley <sean@farley.io>
> # Date 1427912211 25200
> #      Wed Apr 01 11:16:51 2015 -0700
> # Node ID 35cf765458f06a7651d0e9ba9340d46369741616
> # Parent  1b97cc5d2272c272961cc3e1d738e521af012a40
> hgext: add smb extension

Queued for stable, thanks.

Patch

diff --git a/hgext/smb.py b/hgext/smb.py
new file mode 100644
--- /dev/null
+++ b/hgext/smb.py
@@ -0,0 +1,209 @@ 
+import inspect
+import os
+import subprocess
+
+from mercurial import _
+from mercurial import commands
+from mercurial import error
+from mercurial import extensions
+from mercurial import dispatch
+from mercurial import hg
+from mercurial import obsolete
+from mercurial import scmutil
+from mercurial import util
+
+######################
+# audio player methods
+######################
+
+def is_exe(fpath):
+    return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
+
+def which(program):
+    fpath, fname = os.path.split(program)
+    if fpath:
+        if is_exe(program):
+            return program
+    else:
+        for path in os.environ["PATH"].split(os.pathsep):
+            path = path.strip('"')
+            exe_file = os.path.join(path, program)
+            if is_exe(exe_file):
+                return exe_file
+
+    return None
+
+def audioplayer():
+    for p in ('afplay', 'aplay', 'mpg123'):
+        found = which(p)
+        if found is not None:
+            return found
+    return None
+
+def play(repo, sound):
+    if repo is None or not repo.ui.interactive():
+        return
+    player = audioplayer()
+    hasplayed = False
+    if os.path.exists(repo.join('smbrebasehack')):
+        os.remove(repo.join('smbrebasehack'))
+        hasplayed = True
+
+    if player and not hasplayed:
+        datapath = os.path.dirname(os.path.realpath(__file__))
+        datapath = os.path.join(datapath, 'data')
+        if not sound.endswith('.wav'):
+            sound = sound + '.wav'
+        subprocess.Popen([player, os.path.join(datapath, sound)],
+                         stdout=open(os.devnull, 'w'),
+                         stderr=subprocess.STDOUT)
+
+##############################
+# mercurial extension wrappers
+##############################
+
+def extsetup(ui):
+    extensions.wrapfunction(dispatch, 'runcommand', wrapruncommand)
+    extensions.wrapfunction(hg, 'updaterepo', wrapupdate)
+
+    extensions.wrapcommand(commands.table, 'log', wraplog)
+    extensions.wrapcommand(commands.table, 'commit', wrapcommit)
+
+    try:
+        rebase = extensions.find('rebase')
+        if rebase:
+            extensions.wrapcommand(rebase.cmdtable, 'rebase', wraprebase)
+    except:
+        pass
+
+    try:
+        histedit = extensions.find('histedit')
+        if histedit:
+            extensions.wrapfunction(histedit, 'finishfold', wrapfold)
+    except:
+        pass
+
+    try:
+        purge = extensions.find('purge')
+        if purge:
+            extensions.wrapcommand(purge.cmdtable, 'purge', wrappurge)
+    except:
+        pass
+
+    try:
+        evolve = extensions.find('evolve')
+        if evolve:
+            extensions.wrapcommand(evolve.cmdtable, 'prune', wrapprune)
+            extensions.wrapcommand(evolve.cmdtable, 'amend', wrapamend)
+    except:
+        pass
+
+def wrapruncommand(orig, lui, repo, cmd, fullargs, ui, options, d, cmdpats,
+                   cmdoptions):
+    try:
+        ret = orig(lui, repo, cmd, fullargs, ui, options, d, cmdpats,
+                   cmdoptions)
+        return ret
+    except Exception as e:
+        # things to skip:
+        # - skip if broken pipe (such as quiting from a pager)
+        # - histedit in progress
+        if ((not util.safehasattr(e, 'errno') or e.errno != 32)
+            and e.message != 'histedit in progress'
+            and e.message != 'rebase in progress'):
+            if isinstance(e, error.InterventionRequired):
+                play(repo, 'smb_pause')
+            else:
+                play(repo, 'smb_gameover')
+        raise
+
+def wrapupdate(orig, repo, node, overwrite):
+    # copied from merge.update which should be extracted
+    wc = repo[None]
+    p1 = wc.parents()[0]
+    if node is None:
+            try:
+                node = repo.branchtip(wc.branch())
+            except error.RepoLookupError:
+                if wc.branch() == 'default':
+                    node = repo.lookup('tip')
+                else:
+                    raise util.Abort(_("branch %s not found") % wc.branch())
+
+            if p1.obsolete() and not p1.children():
+                # allow updating to successors
+                successors = obsolete.successorssets(repo, p1.node())
+                if successors:
+                    successors = [n for sub in successors for n in sub]
+                    node = repo.revs('max(%ln)', successors).first()
+
+    current = repo['.']
+    dest = repo[node]
+
+    # things to skip:
+    # - if current == dest
+    # - if we are in histedit
+    # - if we are in a rebase
+    skip = False
+    for frame in inspect.stack():
+        for s in ['rebase', 'hist', 'prune']:
+            if s in frame[3]:
+                skip = s
+        if skip:
+            break
+
+    if current != dest and not skip:
+        # are we moving from an ancestor to a descendant?
+        if current.descendant(dest):
+            play(repo, 'smb_flagpole')
+        # or from a descendat to ancestor?
+        elif dest.descendant(current):
+            play(repo, 'smb_pipe')
+        # or jumping across branches?
+        else:
+            play(repo, 'smb_jump')
+    return orig(repo, node, overwrite)
+
+def wraprebase(orig, ui, repo, **opts):
+    ret = orig(ui, repo, **opts)
+    play(repo, 'smb_die')
+    f = repo.vfs('smbrebasehack', 'w')
+    f.write("True")
+    f.close()
+    return ret
+
+def wraplog(orig, ui, repo, *args, **opts):
+    # double check rev exists so we don't play two sounds
+    rev = opts.get('rev')
+    skip = False
+    if rev and not scmutil.revrange(repo, opts.get('rev')):
+        skip = True
+    if not skip:
+        play(repo, 'smb_vine')
+    return orig(ui, repo, *args, **opts)
+
+def wrapcommit(orig, ui, repo, *args, **opts):
+    if opts.get('amend'):
+        play(repo, 'smb_kick')
+    else:
+        play(repo, 'smb_1up')
+    return orig(ui, repo, *args, **opts)
+
+def wrapfold(orig, ui, repo, ctx, oldctx, newnode, opts, internalchanges):
+    play(repo, 'smb_stomp')
+    return orig(ui, repo, ctx, oldctx, newnode, opts, internalchanges)
+
+def wrappurge(orig, ui, repo, *dirs, **opts):
+    play(repo, 'smb_bowserfire')
+    return orig(ui, repo, *dirs, **opts)
+
+def wrapprune(orig, ui, repo, *revs, **opts):
+    if opts.get('succ'):
+        play(repo, 'smb_kick')
+    else:
+        play(repo, 'smb_fireball')
+    return orig(ui, repo, *revs, **opts)
+
+def wrapamend(orig, ui, repo, *pats, **opts):
+    play(repo, 'smb_kick')
+    return orig(ui, repo, *pats, **opts)