zsh-workers
 help / color / mirror / code / Atom feed
* PATCH: (0/3) vcs_info: hg fixes, enhancements and hooks for vcs_info
@ 2010-02-15 23:52 Frank Terbeck
  2010-02-15 23:52 ` PATCH: (1/3) vcs_info: hg fixes and enhancements Frank Terbeck
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Frank Terbeck @ 2010-02-15 23:52 UTC (permalink / raw)
  To: zsh-workers

This is a series of three patches (two by Seth House, one by myself),
which fixes bugs in the `hg' backend, adds a new feature to it and adds
the concept of `hooks' to vcs_info.

Seth is the author of all the purely mercurial (hg) related changes.
Including a bug fix: if you're on a mercurial branch, but check out an
older commit, the hash returned by the `get-revision' style if still
the one of the branch's top commit.

The other change Seth made, is support for mercurial's "bookmarks",
which - according to Seth - are mercurial's way of cheap branches. They
are similar to a tag, but they move along as commits are made.

The changes I made, introduce "hooks" into vcs_info. I did that, because
Seth told me, that at the same time more than one bookmark could be
active. So, when you're on a commit that has 123 bookmarks on it, you're
getting an awfully long string in return.

Vcs_info's formats could only strim entries to a certain size. E.g.: if
you'd want the %m item to be eight characters wide, you'd use "%8.8m".
With that bookmarks business, I didn't feel that was quite enough.

Also up to now, a backend could only hand over *one* "misc" parameter to
VCS_INFO_formats(). And with the support for bookmarks, the hg backend
needed more, because the misc parameter is already in use by the
mq-top-patch support. Now, every backend may hand over one or more
misc parameters to VCS_INFO_formats(). The default is, to insert a
comma separated list of all misc parameters as the `%m' item. (Fine-
grained access to what is really inserted is possible through the
`set-message' hook.)

A short example of a hook to control how the hg-bookmarks string is
generated could look like this (included in the documentation too):

Register a function:

% zstyle ':vcs_info:hg+gen-hg-bookmark-string:*' hooks hgbookmarks

Define the function (the `+vi-' prefix is to avoid namespace issues,
the manual describes this, too):

function +vi-hgbookmarks() {
    # The default is to connect all bookmark names by semicolons. This
    # mixes things up a little.
    # Imagine, there's one type of bookmarks that is special to you.
    # Say, because it's *your* work. Those bookmarks look always like
    # this: "sh/*". And you only want to see those.
    local s i

    # The bookmarks returned by `hg' are available in the functions
    # positional parameters.
    (( $# == 0 )) && return 0
    for i in "$@"; do
        if [[ $i == sh/* ]]; then
            [[ -n $s ]] && s=$s,
            s=${s}$i
        fi
    done

    # Now, the communication with the code that calls the hook functions
    # is done via the hook_com[] hash. The key, at which the
    # `gen-hg-bookmark-string' hook looks at is `hg-bookmark-string'.
    # So:
    hook_com[hg-bookmark-string]=$s

    # And to signal, that we want to use the sting we just generated,
    # set the special variable `ret' to something other than the default
    # zero:
    ret=1
    return 0
}

There are currently five hooks available:
    set-message
    set-branch-format
    set-hgrev-format
    gen-hg-bookmark-string
    gen-mq-patch-string
    gen-stgit-patch-string
    gen-stgit-unapplied-string
    set-stgit-format

Those give the user full control over what is being output. Adding hooks
is trivial, but I didn't come up with useful cases yet. :)

Regards, Frank

Frank Terbeck (1):
  vcs_info: Introduce the concept of hooks

Seth House (2):
  vcs_info: hg fixes and enhancements
  vcs_info: hg bookmarks support

 Doc/Zsh/contrib.yo                                |  290 ++++++++++++++++++++-
 Functions/VCS_Info/Backends/VCS_INFO_get_data_bzr |    9 +-
 Functions/VCS_Info/Backends/VCS_INFO_get_data_git |   65 +++---
 Functions/VCS_Info/Backends/VCS_INFO_get_data_hg  |  117 ++++++---
 Functions/VCS_Info/Backends/VCS_INFO_get_data_p4  |   15 +-
 Functions/VCS_Info/Backends/VCS_INFO_get_data_svk |    9 +-
 Functions/VCS_Info/Backends/VCS_INFO_get_data_svn |    9 +-
 Functions/VCS_Info/VCS_INFO_formats               |   83 ++++--
 Functions/VCS_Info/VCS_INFO_hook                  |   42 +++
 Functions/VCS_Info/vcs_info                       |    1 +
 10 files changed, 530 insertions(+), 110 deletions(-)


^ permalink raw reply	[flat|nested] 4+ messages in thread

* PATCH: (1/3) vcs_info: hg fixes and enhancements
  2010-02-15 23:52 PATCH: (0/3) vcs_info: hg fixes, enhancements and hooks for vcs_info Frank Terbeck
@ 2010-02-15 23:52 ` Frank Terbeck
  2010-02-15 23:52 ` PATCH: (2/3) vcs_info: hg bookmarks support Frank Terbeck
  2010-02-15 23:52 ` PATCH: (3/3) vcs_info: Introduce the concept of hooks Frank Terbeck
  2 siblings, 0 replies; 4+ messages in thread
From: Frank Terbeck @ 2010-02-15 23:52 UTC (permalink / raw)
  To: zsh-workers; +Cc: Seth House

From: Seth House <seth@eseth.com>

---
 Doc/Zsh/contrib.yo                               |   13 +++++---
 Functions/VCS_Info/Backends/VCS_INFO_get_data_hg |   36 ++++++++++++++--------
 2 files changed, 31 insertions(+), 18 deletions(-)

diff --git a/Doc/Zsh/contrib.yo b/Doc/Zsh/contrib.yo
index b815e01..2c3fc39 100644
--- a/Doc/Zsh/contrib.yo
+++ b/Doc/Zsh/contrib.yo
@@ -532,14 +532,17 @@ example(zstyle ':vcs_info:*' disable-patterns "$HOME/.zsh+LPAR()|/*+RPAR()")
 )
 kindex(check-for-changes)
 item(tt(check-for-changes))(
-If enabled, this style (currently only used by the tt(git) backend) causes the
-tt(%c) and tt(%u) format escapes to be filled with information. The strings
-filled into these escapes can be controlled via the var(stagedstr) and
-var(unstagedstr) styles.
+If enabled, this style causes the tt(%c) and tt(%u) format escapes to be filled
+with information. The strings filled into these escapes can be controlled via
+the var(stagedstr) and var(unstagedstr) styles. The only backends that
+currently support this option are tt(git) and tt(hg) (tt(hg) only supports
+unstaged).
 
 Note, that the actions taken if this style is enabled are potentially expensive
 (read: they take time, depending on how big the current repository is).
-Therefore, it is disabled by default.
+Therefore, it is disabled by default. In order to use this style with
+the tt(hg) backend you must also use the var(get-revision) style to avoid
+having to start the interpretrer more than once.
 )
 kindex(stagedstr)
 item(tt(stagedstr))(
diff --git a/Functions/VCS_Info/Backends/VCS_INFO_get_data_hg b/Functions/VCS_Info/Backends/VCS_INFO_get_data_hg
index 0b66463..6689015 100644
--- a/Functions/VCS_Info/Backends/VCS_INFO_get_data_hg
+++ b/Functions/VCS_Info/Backends/VCS_INFO_get_data_hg
@@ -3,7 +3,8 @@
 ## Distributed under the same BSD-ish license as zsh itself.
 
 setopt localoptions NO_shwordsplit
-local file hgbranch hgbranch_name hgbase hghash hglrev hgmisc r_branch r_info revformat
+local file hgbranch hgbranch_name hgbase hglhash hgshash hglrev hgmisc \
+    r_branch hgchanges revformat
 
 VCS_INFO_hg_get_mq_top_patch () {
     local patchdir=$1
@@ -29,27 +30,36 @@ else
     hgbranch_name="default"
 fi
 
-hghash=''
+hglhash=''
 hglrev=''
 if zstyle -t ":vcs_info:${vcs}:${usercontext}:${rrn}" get-revision ; then
     # Calling the 'hg' program is quite a bit too slow for prompts.
     # If there's a way around that, I'd be interested.
     # Disabled by default anyway, so no harm done.
 
-    HGRCPATH="/dev/null" ${vcs_comm[cmd]} branches \
-    | while read -r r_branch r_info ; do
-        if [[ ${r_branch} == ${hgbranch_name} ]] ; then
-            match=()
-            : ${r_info/(#b)([^:]##):(*)}
-            hglrev=${match[1]}
-            hghash=${match[2]}
-            break
+    if zstyle -t ":vcs_info:${vcs}:${usercontext}:${rrn}" \
+            "check-for-changes" ; then
+
+        HGRCPATH="/dev/null" ${vcs_comm[cmd]} id -i -n -b \
+        | read -r hglhash hglrev r_branch
+
+        # Are there uncommitted-changes?
+        if [[ $hglrev[-1] == + ]] ; then
+            hgchanges=1
         fi
-    done
+
+        # Remove uncommitted-changes marker, if any
+        hglrev=${hglrev/+/}
+        hglhash=${hglhash/+/}
+    else
+        HGRCPATH="/dev/null" ${vcs_comm[cmd]} \
+        parents --template="{node} {node|short} {rev} {branches}\n" \
+        | read -r hglhash hgshash hglrev r_branch
+    fi
 
     if [[ -n ${hglrev} ]] ; then
         zstyle -s ":vcs_info:${vcs}:${usercontext}:${rrn}" hgrevformat revformat || revformat="%r:%h"
-        zformat -f hglrev "${revformat}" "r:${hglrev}" "h:${hghash}"
+        zformat -f hglrev "${revformat}" "r:${hglrev}" "h:${hglhash}"
         zstyle -s ":vcs_info:${vcs}:${usercontext}:${rrn}" branchformat hgbranch || hgbranch="%b:%r"
         zformat -f hgbranch "${hgbranch}" "b:${hgbranch_name}" "r:${hglrev}"
     fi
@@ -67,5 +77,5 @@ else
     hgmisc=''
 fi
 
-VCS_INFO_formats '' "${hgbranch}" "${hgbase}" '' '' "${hglrev}" "${hgmisc}"
+VCS_INFO_formats '' "${hgbranch}" "${hgbase}" '' "${hgchanges}" "${hglrev}" "${hgmisc}"
 return 0
-- 
1.7.0


^ permalink raw reply	[flat|nested] 4+ messages in thread

* PATCH: (2/3) vcs_info: hg bookmarks support
  2010-02-15 23:52 PATCH: (0/3) vcs_info: hg fixes, enhancements and hooks for vcs_info Frank Terbeck
  2010-02-15 23:52 ` PATCH: (1/3) vcs_info: hg fixes and enhancements Frank Terbeck
@ 2010-02-15 23:52 ` Frank Terbeck
  2010-02-15 23:52 ` PATCH: (3/3) vcs_info: Introduce the concept of hooks Frank Terbeck
  2 siblings, 0 replies; 4+ messages in thread
From: Frank Terbeck @ 2010-02-15 23:52 UTC (permalink / raw)
  To: zsh-workers; +Cc: Seth House

From: Seth House <seth@eseth.com>

---
 Doc/Zsh/contrib.yo                               |    6 ++++--
 Functions/VCS_Info/Backends/VCS_INFO_get_data_hg |   19 +++++++++++++++----
 2 files changed, 19 insertions(+), 6 deletions(-)

diff --git a/Doc/Zsh/contrib.yo b/Doc/Zsh/contrib.yo
index 2c3fc39..6d8d93f 100644
--- a/Doc/Zsh/contrib.yo
+++ b/Doc/Zsh/contrib.yo
@@ -602,8 +602,9 @@ If set to true, vcs_info goes the extra mile to figure out the revision of
 a repository's work tree (currently for the tt(git) and tt(hg) backends,
 where this kind of information is not always vital). For tt(git), the
 hash value of the currently checked out commit is available via the tt(%i)
-expansion. With tt(hg), the local revision number is available via tt(%i)
-and the corresponding global hash is available via tt(%m).
+expansion. With tt(hg), the local revision number is available via tt(%i),
+the corresponding global hash is available via tt(%m), and bookmarks are
+available via tt(%k).
 If this style is set in the tt(hg) context, the backend supports the
 branchformat style.
 )
@@ -669,6 +670,7 @@ In tt(branchformat) these replacements are done:
 startsitem()
 sitem(tt(%b))(the branch name)
 sitem(tt(%r))(the current revision number or the var(hgrevformat) style for tt(hg))
+sitem(tt(%k))(any current bookmarks for tt(hg))
 endsitem()
 
 In tt(stgitformat) these replacements are done:
diff --git a/Functions/VCS_Info/Backends/VCS_INFO_get_data_hg b/Functions/VCS_Info/Backends/VCS_INFO_get_data_hg
index 6689015..7c854b2 100644
--- a/Functions/VCS_Info/Backends/VCS_INFO_get_data_hg
+++ b/Functions/VCS_Info/Backends/VCS_INFO_get_data_hg
@@ -4,7 +4,7 @@
 
 setopt localoptions NO_shwordsplit
 local file hgbranch hgbranch_name hgbase hglhash hgshash hglrev hgmisc \
-    r_branch hgchanges revformat
+    r_branch hgchanges revformat bookmarks r_bmhash r_bmname hgbm
 
 VCS_INFO_hg_get_mq_top_patch () {
     local patchdir=$1
@@ -32,6 +32,9 @@ fi
 
 hglhash=''
 hglrev=''
+hgbm=''
+bookmarks="${hgbase}/.hg/bookmarks"
+
 if zstyle -t ":vcs_info:${vcs}:${usercontext}:${rrn}" get-revision ; then
     # Calling the 'hg' program is quite a bit too slow for prompts.
     # If there's a way around that, I'd be interested.
@@ -41,7 +44,7 @@ if zstyle -t ":vcs_info:${vcs}:${usercontext}:${rrn}" get-revision ; then
             "check-for-changes" ; then
 
         HGRCPATH="/dev/null" ${vcs_comm[cmd]} id -i -n -b \
-        | read -r hglhash hglrev r_branch
+        | read -r hgshash hglrev r_branch
 
         # Are there uncommitted-changes?
         if [[ $hglrev[-1] == + ]] ; then
@@ -50,18 +53,26 @@ if zstyle -t ":vcs_info:${vcs}:${usercontext}:${rrn}" get-revision ; then
 
         # Remove uncommitted-changes marker, if any
         hglrev=${hglrev/+/}
-        hglhash=${hglhash/+/}
+        hgshash=${hgshash/+/}
     else
         HGRCPATH="/dev/null" ${vcs_comm[cmd]} \
         parents --template="{node} {node|short} {rev} {branches}\n" \
         | read -r hglhash hgshash hglrev r_branch
     fi
 
+    if [[ -r "${bookmarks}" ]] ; then
+        while read -r r_bmhash r_bmname ; do
+            if [[ $hglhash == $r_bmhash || $r_bmhash == $hgshash* ]] ; then
+                hgbm="$r_bmname;${hgbm}"
+            fi
+        done < ${bookmarks}
+    fi
+
     if [[ -n ${hglrev} ]] ; then
         zstyle -s ":vcs_info:${vcs}:${usercontext}:${rrn}" hgrevformat revformat || revformat="%r:%h"
         zformat -f hglrev "${revformat}" "r:${hglrev}" "h:${hglhash}"
         zstyle -s ":vcs_info:${vcs}:${usercontext}:${rrn}" branchformat hgbranch || hgbranch="%b:%r"
-        zformat -f hgbranch "${hgbranch}" "b:${hgbranch_name}" "r:${hglrev}"
+        zformat -f hgbranch "${hgbranch}" "b:${hgbranch_name}" "r:${hglrev}" "k:${hgbm}"
     fi
 else
     hgbranch="${hgbranch_name}"
-- 
1.7.0


^ permalink raw reply	[flat|nested] 4+ messages in thread

* PATCH: (3/3) vcs_info: Introduce the concept of hooks
  2010-02-15 23:52 PATCH: (0/3) vcs_info: hg fixes, enhancements and hooks for vcs_info Frank Terbeck
  2010-02-15 23:52 ` PATCH: (1/3) vcs_info: hg fixes and enhancements Frank Terbeck
  2010-02-15 23:52 ` PATCH: (2/3) vcs_info: hg bookmarks support Frank Terbeck
@ 2010-02-15 23:52 ` Frank Terbeck
  2 siblings, 0 replies; 4+ messages in thread
From: Frank Terbeck @ 2010-02-15 23:52 UTC (permalink / raw)
  To: zsh-workers

This introduces the basic system, documentation updates and a number
of hooks:
    set-message
    set-branch-format
    set-hgrev-format
    gen-hg-bookmark-string
    gen-mq-patch-string
    gen-stgit-patch-string
    gen-stgit-unapplied-string
    set-stgit-format
---
 Doc/Zsh/contrib.yo                                |  281 ++++++++++++++++++++-
 Functions/VCS_Info/Backends/VCS_INFO_get_data_bzr |    9 +-
 Functions/VCS_Info/Backends/VCS_INFO_get_data_git |   65 +++---
 Functions/VCS_Info/Backends/VCS_INFO_get_data_hg  |  102 +++++---
 Functions/VCS_Info/Backends/VCS_INFO_get_data_p4  |   15 +-
 Functions/VCS_Info/Backends/VCS_INFO_get_data_svk |    9 +-
 Functions/VCS_Info/Backends/VCS_INFO_get_data_svn |    9 +-
 Functions/VCS_Info/VCS_INFO_formats               |   83 +++++--
 Functions/VCS_Info/VCS_INFO_hook                  |   42 +++
 Functions/VCS_Info/vcs_info                       |    1 +
 10 files changed, 505 insertions(+), 111 deletions(-)
 create mode 100644 Functions/VCS_Info/VCS_INFO_hook

diff --git a/Doc/Zsh/contrib.yo b/Doc/Zsh/contrib.yo
index 6d8d93f..e77fc13 100644
--- a/Doc/Zsh/contrib.yo
+++ b/Doc/Zsh/contrib.yo
@@ -324,7 +324,7 @@ cindex(version control utility)
 In a lot of cases, it is nice to automatically retrieve information from
 version control systems (VCSs), such as subversion, CVS or git, to be able
 to provide it to the user; possibly in the user's prompt. So that you can
-instantly tell on which branch you are currently on,  for example.
+instantly tell which branch you are currently on, for example.
 
 In order to do that, you may use the tt(vcs_info) function.
 
@@ -419,7 +419,8 @@ example(:vcs_info:<vcs-string>:<user-context>:<repo-root-name>)
 startitem()
 item(tt(<vcs-string>))(
 is one of: git, git-svn, git-p4, hg, darcs, bzr,
-cdv, mtn, svn, cvs, svk, tla or p4.
+cdv, mtn, svn, cvs, svk, tla or p4. When hooks are active the hooks name
+is added after a `+'. (See tt(Hooks in vcs_info) below.)
 )
 item(tt(<user-context>))(
 is a freely configurable string, assignable by
@@ -542,7 +543,7 @@ Note, that the actions taken if this style is enabled are potentially expensive
 (read: they take time, depending on how big the current repository is).
 Therefore, it is disabled by default. In order to use this style with
 the tt(hg) backend you must also use the var(get-revision) style to avoid
-having to start the interpretrer more than once.
+having to start the interpreter more than once.
 )
 kindex(stagedstr)
 item(tt(stagedstr))(
@@ -602,11 +603,16 @@ If set to true, vcs_info goes the extra mile to figure out the revision of
 a repository's work tree (currently for the tt(git) and tt(hg) backends,
 where this kind of information is not always vital). For tt(git), the
 hash value of the currently checked out commit is available via the tt(%i)
-expansion. With tt(hg), the local revision number is available via tt(%i),
-the corresponding global hash is available via tt(%m), and bookmarks are
-available via tt(%k).
+expansion. With tt(hg), the local revision number and the corresponding
+global hash are available via tt(%i); in addition, the topmost
+applied tt(mq) patch and bookmarks are available via tt(%m).
 If this style is set in the tt(hg) context, the backend supports the
-branchformat style.
+var(branchformat) style.
+)
+kindex(get-bookmarks)
+item(tt(get-bookmarks))(
+If set to true, the tt(hg) backend will try to get a list of current
+bookmarks. They will be available in via the `tt(%m)' replacement.
 )
 kindex(use-prompt-escapes)
 item(tt(use-prompt-escapes))(
@@ -614,6 +620,16 @@ Determines if we assume that the assembled
 string from var(vcs_info) includes prompt escapes. (Used by
 tt(vcs_info_lastmsg).)
 )
+kindex(debug)
+item(tt(debug))(
+Enable debugging output, to track possible problems. Currently this style
+is only used by tt(vcs_info)'s hooks system.
+)
+kindex(hooks)
+item(tt(hooks))(
+A list style, that defines hook-function names. See tt(Hooks in vcs_info)
+below for details.
+)
 enditem()
 
 The default values for these styles in all contexts are:
@@ -636,7 +652,10 @@ sitem(tt(command))((empty string))
 sitem(tt(use-server))(false)
 sitem(tt(use-simple))(false)
 sitem(tt(get-revision))(false)
+sitem(tt(get-bookmarks))(false)
 sitem(tt(use-prompt-escapes))(true)
+sitem(tt(debug))(false)
+sitem(tt(hooks))((empty list))
 endsitem()
 
 In normal tt(formats) and tt(actionformats), the following replacements are
@@ -661,8 +680,9 @@ var(/foo/bar/reposXY/beer/tasty), tt(%S) is var(beer/tasty).)
 sitem(tt(%m))(A "misc" replacement. It is at the discretion of the backend
 to decide what this replacement expands to. It is currently used by
 the tt(hg) and tt(git) backends. The tt(hg) backend replaces tt(%m) with the
-topmost Mq patch applied (qtop) and the tt(git) backend replaces it
-with the string from the var(stgitformat) style.)
+topmost tt(mq) patch applied (qtop) and a list of any current bookmarks. The
+tt(git) backend replaces it with the string from the var(stgitformat)
+style.)
 endsitem()
 
 In tt(branchformat) these replacements are done:
@@ -670,7 +690,6 @@ In tt(branchformat) these replacements are done:
 startsitem()
 sitem(tt(%b))(the branch name)
 sitem(tt(%r))(the current revision number or the var(hgrevformat) style for tt(hg))
-sitem(tt(%k))(any current bookmarks for tt(hg))
 endsitem()
 
 In tt(stgitformat) these replacements are done:
@@ -752,6 +771,168 @@ enditem()
 
 All variables named VCS_INFO_* are for internal use only.
 
+subsect(Hooks in vcs_info)
+
+Hooks are places in tt(vcs_info) where you can run your own code. That
+code can communicate with the code that called it and through that,
+change the system's behaviour.
+
+For configuration, hooks change the style context:
+example(:vcs_info:<vcs-string>+<hook-name>:<user-context>:<repo-root-name>)
+
+To register functions to a hook, you need to list them in the tt(hooks)
+style in the appropriate context.
+
+Example:
+example(zstyle ':vcs_info:*+foo:*' hooks bar baz)
+
+This registers functions to the hook `foo' for all backends. In order to
+avoid namespace problems, all registered function names are prepended by
+a `+vi-', so the actual functions called for the `foo' hook are
+`tt(+vi-bar)' and `tt(+vi-baz)'.
+
+If something seems weird, you can enable the `debug' boolean style in
+the proper context and the hook-calling code will print what it tried
+to execute and whether the function in question existed.
+
+When you register more than one function to a hook, all functions are
+executed one after another until one function returns non-zero or until
+all functions have been called.
+
+There are a number of variables, that are special in hook contexts:
+
+startitem()
+item(tt(ret))(
+The return value, that the hooks system will return to the caller. The
+default is an integer `zero'. If and how a changed tt(ret) value changes
+the execution of the caller depends on the specific hook. See the hook's
+documentation below for details.
+)
+item(tt(hook_com))(
+An associated array, which is used for bidirectional communication from
+the caller to hook functions. The used keys depend on the specific hook.
+)
+item(tt(context))(
+The active context of the hook. Functions that wish to change this
+variable should make it local scope first.
+)
+enditem()
+
+Finally, the full list of currently available hooks:
+
+startitem()
+item(tt(gen-hg-bookmark-string))(
+Called in the Mercurial backend (the tt(get-revision) and tt(get-bookmarks)
+styles must be active) when a bookmark string is generated.
+
+This hook gets the names of the Mercurial bookmarks, that
+tt(vcs_info) collected from `hg'.
+
+When setting tt(ret) to non-zero, the string in
+tt(${hook_com[hg-bookmark-string]}) will be used as the
+`tt(misc1)' replacement in the variables set by tt(vcs_info).
+)
+item(tt(gen-mq-patch-string))(
+Called in the Mercurial backend when a mq-patch string is generated. That
+only happens if a tt(.hg/patches) directory exists in the repository.
+
+This hook gets the names of all applied mq patches which tt(vcs_info)
+collected so far in the opposite order, which mean that the first argument
+is the top-most patch and so forth.
+
+When setting tt(ret) to non-zero, the string in
+tt(${hook_com[hg-mqpatch-string]}) will be used as the
+`tt(misc0)' replacement in the variables set by tt(vcs_info).
+)
+item(tt(gen-stgit-patch-string))(
+Called in the git backend when a stgit-patch string is generated. That
+only happens if stgit is in use in the repository.
+
+This hook gets the names of all applied stgit patches which tt(vcs_info)
+collected so far in the opposite order, which mean that the first argument
+is the top-most patch and so forth.
+
+When setting tt(ret) to non-zero, the string in
+tt(${hook_com[stgit-patch-string]}) will be used as the
+`tt(misc0)' replacement in the variables set by tt(vcs_info).
+)
+item(tt(gen-stgit-unapplied-string))(
+Called in the git backend when a stgit-unapplied string is generated. That
+only happens if stgit is in use in the repository.
+
+This hook gets the names of all unapplied stgit patches which tt(vcs_info)
+collected so far.
+
+When setting tt(ret) to non-zero, the string in
+tt(${hook_com[stgit-unapplied-string]}) will be used as the
+`tt(misc0)' replacement in the variables set by tt(vcs_info).
+)
+item(tt(set-branch-format))(
+Called before a `tt(branchformat)' is set. The only argument to the
+hook is the format that is configured at this point.
+
+The `tt(hook_com)' keys considered are `tt(branch)' and `tt(revision)'.
+They are set to the values figured out so far by tt(vcs_info) and any
+change will be used directly when the actual replacement is done.
+
+If tt(ret) is set to to non-zero, the string in
+tt(${hook_com[branch-replace]}) will be used unchanged as the
+`tt(%b)' replacement in the variables set by tt(vcs_info).
+)
+item(tt(set-hgrev-format))(
+Called before a `tt(hgrevformat)' is set. The only argument to the
+hook is the format that is configured at this point.
+
+The `tt(hook_com)' keys considered are `tt(hash)' and `tt(localref)'.
+They are set to the values figured out so far by tt(vcs_info) and any
+change will be used directly when the actual replacement is done.
+
+If tt(ret) is set to to non-zero, the string in
+tt(${hook_com[rev-replace]}) will be used unchanged as the
+`tt(%i)' replacement in the variables set by tt(vcs_info).
+)
+item(tt(set-message))(
+Called each time before a `tt(vcs_info_msg_N_)' message is set.
+It takes two arguments; the first being the `N' in the message
+variable name, the second is the currently configured format or
+actionformat.
+
+There are a number of `tt(hook_com)' keys, that are used here:
+`tt(action)', `tt(branch)', `tt(base)', `tt(base-name)', `tt(subdir)',
+`tt(staged)', `tt(unstaged)', `tt(revision)', `tt(misc)', `tt(vcs)'
+and one `tt(miscN)' entry for each backend-specific data field (tt(N)
+starting at zero). They are set to the values figured out so far by
+tt(vcs_info) and any change will be used directly when the actual
+replacement is done.
+
+Since this hook is triggered multiple times (once for each configured
+format or actionformat), each of the `tt(hook_com)' keys mentioned
+above (except for the tt(miscN) entries) has an `tt(_orig)' counterpart,
+so even if you changed a value to your liking you can still get the
+original value in the next run. Changing the `tt(_orig)' values is
+probably not a good idea.
+
+If tt(ret) is set to to non-zero, the string in
+tt(${hook_com[message]}) will be used unchanged as the message by
+tt(vcs_info).
+)
+item(tt(set-stgit-format))(
+Called before a `tt(stgitformat)' is set. The only argument to the
+hook is the format that is configured at this point.
+
+The `tt(hook_com)' keys considered are `tt(patch)' and `tt(unapplied)'.
+They are set to the values figured out so far by tt(vcs_info) and any
+change will be used directly when the actual replacement is done.
+
+If tt(ret) is set to to non-zero, the string in
+tt(${hook_com[stgit-replace]}) will be used unchanged as the
+`tt(misc0)' replacement in the variables set by tt(vcs_info).
+)
+enditem()
+
+If all of this sounds rather confusing, take a look at the tt(Examples)
+section below. It contains some explanatory code.
+
 subsect(Examples)
 
 Don't use tt(vcs_info) at all (even though it's in your prompt):
@@ -784,6 +965,86 @@ example(alias vcsi='vcs_info command; vcs_info_lastmsg')
 This way, you can even define different formats for output via
 tt(vcs_info_lastmsg) in the ':vcs_info:*:command:*' namespace.
 
+Now as promised, some code that uses hooks:
+say, you'd like to replace the string `svn' by `subversion' in
+tt(vcs_info)'s tt(%s) format-replacement.
+
+First, we will tell tt(vcs_info) to call a function when populating
+the message variables with the gathered information:
+example(zstyle ':vcs_info:*+set-message:*' hooks svn2subversion)
+
+Nothing happens. Which is reasonable, since there we didn't define
+the actual function yet. To see what the hooks subsystem is trying to
+do, enable the `tt(debug)' style:
+example(zstyle ':vcs_info:*+*:*' debug true)
+
+That should give you an idea what is going on. Specifically, the function
+that we are looking for is `tt(+vi-svn2subversion)'. Note, the `tt(+vi-)'
+prefix. So, everything is in order, just as documented. When you are done
+checking out the debugging output, disable it again:
+example(zstyle ':vcs_info:*+*:*' debug false)
+
+Now, let's define the function:
+example(
+function +vi-svn2subversion+LPAR()RPAR() {
+    [[ ${hook_com[vcs_orig]} == svn ]] && hook_com[vcs]=subversion
+})
+
+Simple enough. And it could have even been simpler, if only we had
+registered our function in a less generic context. If we do it only in
+the `tt(svn)' backend's context, we don't need to test which the active
+backend is:
+example(zstyle ':vcs_info:svn+set-message:*' hooks svn2subversion)
+example(
+function +vi-svn2subversion+LPAR()RPAR() {
+    hook_com[vcs]=subversion
+})
+
+And finally a little more elaborate example, that uses a hook to create
+a customised bookmark string for the tt(hg) backend.
+
+Again, we start off by registering a function:
+example(zstyle ':vcs_info:hg+gen-hg-bookmark-string:*' hooks hgbookmarks)
+
+And then we define the `tt(+vi-hgbookmarks) function:
+example(
+function +vi-hgbookmarks+LPAR()RPAR() {
+    # The default is to connect all bookmark names by
+    # semicolons. This mixes things up a little.
+    # Imagine, there's one type of bookmarks that is
+    # special to you. Say, because it's *your* work.
+    # Those bookmarks look always like this: "sh/*"
+    # (because your initials are sh, for example).
+    # This makes the bookmarks string use only those
+    # bookmarks. If there's more than one, it
+    # concatenates them using commas.
+    local s i)
+example(
+    # The bookmarks returned by `hg' are available in
+    # the functions positional parameters.
+    (( $# == 0 )) && return 0
+    for i in "$@"; do
+        if [[ $i == sh/* ]]; then
+            [[ -n $s ]] && s=$s,
+            s=${s}$i
+        fi
+    done)
+example(
+    # Now, the communication with the code that calls
+    # the hook functions is done via the hook_com[]
+    # hash. The key, at which the `gen-hg-bookmark-string'
+    # hook looks at is `hg-bookmark-string'. So:
+    hook_com[hg-bookmark-string]=$s)
+example(
+    # And to signal, that we want to use the sting we
+    # just generated, set the special variable `ret' to
+    # something other than the default zero:
+    ret=1
+    return 0
+})
+
+This concludes our guided tour through zsh's tt(vcs_info).
+
 
 texinode(Prompt Themes)(ZLE Functions)(Version Control Information)(User Contributions)
 sect(Prompt Themes)
diff --git a/Functions/VCS_Info/Backends/VCS_INFO_get_data_bzr b/Functions/VCS_Info/Backends/VCS_INFO_get_data_bzr
index e85de31..5d4deaa 100644
--- a/Functions/VCS_Info/Backends/VCS_INFO_get_data_bzr
+++ b/Functions/VCS_Info/Backends/VCS_INFO_get_data_bzr
@@ -5,6 +5,7 @@
 setopt localoptions noksharrays extendedglob NO_shwordsplit
 local bzrbase bzrbr
 local -a bzrinfo
+local -xA hook_com
 
 if zstyle -t ":vcs_info:${vcs}:${usercontext}:${rrn}" "use-simple" ; then
     bzrbase=${vcs_comm[basedir]}
@@ -21,6 +22,12 @@ fi
 
 rrn=${bzrbase:t}
 zstyle -s ":vcs_info:${vcs}:${usercontext}:${rrn}" branchformat bzrbr || bzrbr="%b:%r"
-zformat -f bzrbr "${bzrbr}" "b:${bzrinfo[2]}" "r:${bzrinfo[1]}"
+hook_com=( branch "${bzrinfo[2]}" revision "${bzrinfo[1]}" )
+if VCS_INFO_hook 'set-branch-format' "${bzrbr}"; then
+    zformat -f bzrbr "${bzrbr}" "b:${hook_com[branch]}" "r:${hook_com[revision]}"
+else
+    bzrbr=${hook_com[branch-replace]}
+fi
+hook_com=()
 VCS_INFO_formats '' "${bzrbr}" "${bzrbase}" '' '' "${bzrinfo[1]}" ''
 return 0
diff --git a/Functions/VCS_Info/Backends/VCS_INFO_get_data_git b/Functions/VCS_Info/Backends/VCS_INFO_get_data_git
index bf7c479..4018b5d 100644
--- a/Functions/VCS_Info/Backends/VCS_INFO_get_data_git
+++ b/Functions/VCS_Info/Backends/VCS_INFO_get_data_git
@@ -4,6 +4,8 @@
 
 setopt localoptions extendedglob NO_shwordsplit
 local gitdir gitbase gitbranch gitaction gitunstaged gitstaged gitsha1 gitmisc
+local stgitpatch stgitunapplied
+local -xA hook_com
 
 VCS_INFO_git_getaction () {
     local gitaction='' gitdir=$1
@@ -97,36 +99,6 @@ VCS_INFO_git_getbranch () {
     return 0
 }
 
-VCS_INFO_git_get_stgit_top_patch () {
-    local patchdir=$1
-
-    if [[ -d "$patchdir" ]]; then
-        local -a patches
-        patches=(${(f)"$(< "${patchdir}/applied")"})
-        printf '%s' $patches[-1]
-        return 0
-    fi
-
-    return 1
-}
-
-VCS_INFO_git_get_stgit_unapplied() {
-    local patchdir=$1
-
-    if [[ -d "$patchdir" ]]; then
-        local -a patches
-        patches=(${(f)"$(< "${patchdir}/unapplied")"})
-        if [[ -z $patches[@] ]]; then
-            printf 0
-        else
-            printf '%d' $#patches
-        fi
-        return 0
-    fi
-
-    return 1
-}
-
 gitdir=${vcs_comm[gitdir]}
 gitbranch="$(VCS_INFO_git_getbranch ${gitdir})"
 if zstyle -t ":vcs_info:${vcs}:${usercontext}:${rrn}" get-revision && \
@@ -160,14 +132,37 @@ rrn=${gitbase:t}
 
 local patchdir=${gitdir}/patches/${gitbranch}
 if [[ -d $patchdir ]] ; then
-    stgitpatch=$(VCS_INFO_git_get_stgit_top_patch "${patchdir}")
-    stgitunapplied=$(VCS_INFO_git_get_stgit_unapplied "${patchdir}")
+    local -a stgit_applied stgit_unapplied
 
-    stgitpatch=${stgitpatch:-"no patch applied"}
+    stgit_applied=(${(f)"$(< "${patchdir}/applied")"})
+    stgit_applied=( ${(Oa)stgit_applied} )
+    stgit_unapplied=(${(f)"$(< "${patchdir}/unapplied")"})
+    stgit_unapplied=( ${(oa)stgit_applied} )
+
+    if VCS_INFO_hook 'gen-stgit-patch-string' "${stgit_applied[@]}"; then
+        if (( ${#stgit_applied} )); then
+            stgitpatch=${stgit_applied[1]}
+        else
+            stgitpatch="no patch applied"
+        fi
+    else
+        stgitpatch=${hook_com[stgit-patch-string]}
+    fi
+    if VCS_INFO_hook 'gen-stgit-unapplied-string' "${stgit_unapplied[@]}"; then
+        stgitunapplied=${#stgit_unapplied}
+    else
+        stgitunapplied=${hook_com[stgit-unapplied-string]}
+    fi
 
     zstyle -s ":vcs_info:${vcs}:${usercontext}:${rrn}" stgitformat stgitmsg || stgitmsg=" %p (%c)"
-    zformat -f stgitmsg "${stgitmsg}" "p:${stgitpatch}" "c:${stgitunapplied}"
-    gitmisc=${stgitmsg}
+    hook_com=( patch "${stgitpatch}" unapplied "${stgitunapplied}" )
+    if VCS_INFO_hook 'set-stgit-format' "${stgitformat}"; then
+        zformat -f stgitmsg "${stgitmsg}" "p:${hook_com[patch]}" "c:${hook_com[unapplied]}"
+        gitmisc=${stgitmsg}
+    else
+        gitmisc=${hook_com[stgit-replace]}
+    fi
+    hook_com=()
 else
     gitmisc=''
 fi
diff --git a/Functions/VCS_Info/Backends/VCS_INFO_get_data_hg b/Functions/VCS_Info/Backends/VCS_INFO_get_data_hg
index 7c854b2..1c103a5 100644
--- a/Functions/VCS_Info/Backends/VCS_INFO_get_data_hg
+++ b/Functions/VCS_Info/Backends/VCS_INFO_get_data_hg
@@ -3,22 +3,11 @@
 ## Distributed under the same BSD-ish license as zsh itself.
 
 setopt localoptions NO_shwordsplit
-local file hgbranch hgbranch_name hgbase hglhash hgshash hglrev hgmisc \
-    r_branch hgchanges revformat bookmarks r_bmhash r_bmname hgbm
-
-VCS_INFO_hg_get_mq_top_patch () {
-    local patchdir=$1
-
-    if [[ -e "${patchdir}/status" ]]; then
-        local -a patches
-        patches=(${(f)"$(< "${patchdir}/status")"})
-        printf "%s" "${patches[-1]/[^:]*:/}"
-        return 0
-    fi
-
-    return 1
-}
-
+local file hgbranch hgbranch_name hgbase hghash hglrev hgmqstring \
+    r_branch hgchanges revformat bookmarks r_bmhash r_bmname hgbmstring
+local -i getbookmarks
+local -a hgbm mqpatches hgmisc_args
+local -xA hook_com
 
 hgbase=${vcs_comm[basedir]}
 rrn=${hgbase:t}
@@ -30,21 +19,21 @@ else
     hgbranch_name="default"
 fi
 
-hglhash=''
+hghash=''
 hglrev=''
-hgbm=''
+hgbm=()
 bookmarks="${hgbase}/.hg/bookmarks"
-
 if zstyle -t ":vcs_info:${vcs}:${usercontext}:${rrn}" get-revision ; then
     # Calling the 'hg' program is quite a bit too slow for prompts.
     # If there's a way around that, I'd be interested.
     # Disabled by default anyway, so no harm done.
+    local HGRCPATH
 
     if zstyle -t ":vcs_info:${vcs}:${usercontext}:${rrn}" \
             "check-for-changes" ; then
 
-        HGRCPATH="/dev/null" ${vcs_comm[cmd]} id -i -n -b \
-        | read -r hgshash hglrev r_branch
+        HGRCPATH="/dev/null" ${vcs_comm[cmd]} id --debug -i -n -b \
+        | read -r hghash hglrev r_branch
 
         # Are there uncommitted-changes?
         if [[ $hglrev[-1] == + ]] ; then
@@ -53,26 +42,49 @@ if zstyle -t ":vcs_info:${vcs}:${usercontext}:${rrn}" get-revision ; then
 
         # Remove uncommitted-changes marker, if any
         hglrev=${hglrev/+/}
-        hgshash=${hgshash/+/}
+        hghash=${hghash/+/}
     else
         HGRCPATH="/dev/null" ${vcs_comm[cmd]} \
-        parents --template="{node} {node|short} {rev} {branches}\n" \
-        | read -r hglhash hgshash hglrev r_branch
+        parents --template="{node} {rev} {branches}\n" \
+        | read -r hghash hglrev r_branch
     fi
 
-    if [[ -r "${bookmarks}" ]] ; then
+    if zstyle -t ":vcs_info:${vcs}:${usercontext}:${rrn}" "get-bookmarks" \
+            && getbookmarks=1 || getbookmarks=0
+
+    if (( getbookmarks )) && [[ -r "${bookmarks}" ]] ; then
         while read -r r_bmhash r_bmname ; do
-            if [[ $hglhash == $r_bmhash || $r_bmhash == $hgshash* ]] ; then
-                hgbm="$r_bmname;${hgbm}"
+            if [[ $hghash == $r_bmhash ]] ; then
+                hgbm=( "$r_bmname" ${hgbm} )
             fi
         done < ${bookmarks}
     fi
 
     if [[ -n ${hglrev} ]] ; then
         zstyle -s ":vcs_info:${vcs}:${usercontext}:${rrn}" hgrevformat revformat || revformat="%r:%h"
-        zformat -f hglrev "${revformat}" "r:${hglrev}" "h:${hglhash}"
+        hook_com=( localrev "${hglrev}" "hash" "${hghash}" )
+        if VCS_INFO_hook 'set-hgrev-format' "${revformat}"; then
+            zformat -f hglrev "${revformat}" "r:${hook_com[localrev]}" "h:${hook_com[hash]}"
+        else
+            hglrev=${hook_com[rev-replace]}
+        fi
+        hook_com=()
+        if (( getbookmarks )) ; then
+            if VCS_INFO_hook 'gen-hg-bookmark-string' "${hgbm[@]}"; then
+                hgbmstring=${(j.;.)hgbm}
+            else
+                hgbmstring=${hook_com[hg-bookmark-string]}
+            fi
+            hook_com=()
+        fi
         zstyle -s ":vcs_info:${vcs}:${usercontext}:${rrn}" branchformat hgbranch || hgbranch="%b:%r"
-        zformat -f hgbranch "${hgbranch}" "b:${hgbranch_name}" "r:${hglrev}" "k:${hgbm}"
+        hook_com=( branch "${hgbranch_name}" revision "${hglrev}" )
+        if VCS_INFO_hook 'set-branch-format' "${hgbranch}"; then
+            zformat -f hgbranch "${hgbranch}" "b:${hook_com[branch]}" "r:${hook_com[revision]}"
+        else
+            hgbranch=${hook_com[branch-replace]}
+        fi
+        hook_com=()
     fi
 else
     hgbranch="${hgbranch_name}"
@@ -81,12 +93,36 @@ fi
 local patchdir=${hgbase}/.hg/patches/
 
 if [[ -d $patchdir ]] ; then
-    hgmisc=$(VCS_INFO_hg_get_mq_top_patch "${patchdir}")
+    local -a mqpatches
+    if [[ -e "${patchdir}/status" ]]; then
+        mqpatches=( ${${(f)"$(< "${patchdir}/status")"}/(#s)[a-f0-9]##:/} )
+        mqpatches=( ${(Oa)mqpatches} )
+    else
+        mqpatches=( )
+    fi
 
-    hgmisc=${hgmisc:-"no patch applied"}
+    if VCS_INFO_hook 'gen-mq-patch-string' "${mqpatches[@]}"; then
+        if (( ${#mqpatches} )); then
+            hgmqstring=${mqpatches[1]}
+        else
+            hgmqstring="no patch applied"
+        fi
+    else
+        hgbmstring=${hook_com[hg-mqpatch-string]}
+    fi
+    hook_com=()
 else
-    hgmisc=''
+    hgmqstring=''
 fi
 
-VCS_INFO_formats '' "${hgbranch}" "${hgbase}" '' "${hgchanges}" "${hglrev}" "${hgmisc}"
+if [[ -z "${hgmqstring}" ]] && [[ -z "${hgbmstring}" ]]; then
+    hgmisc_args=( '' ) # make sure there's at least *one* misc argument
+elif [[ -z "${hgmqstring}" ]]; then
+    hgmisc_args=( "${hgmqstring}" )
+elif [[ -z "${hgbmstring}" ]]; then
+    hgmisc_args=( "${hgbmstring}" )
+else
+    hgmisc_args=( "${hgmqstring}" "${hgbmstring}" )
+fi
+VCS_INFO_formats '' "${hgbranch}" "${hgbase}" '' "${hgchanges}" "${hglrev}" "${hgmisc_args[@]}"
 return 0
diff --git a/Functions/VCS_Info/Backends/VCS_INFO_get_data_p4 b/Functions/VCS_Info/Backends/VCS_INFO_get_data_p4
index e4bbb06..430cfa6 100644
--- a/Functions/VCS_Info/Backends/VCS_INFO_get_data_p4
+++ b/Functions/VCS_Info/Backends/VCS_INFO_get_data_p4
@@ -6,6 +6,7 @@
 setopt localoptions extendedglob
 local p4base a b
 local -A p4info
+local -xA hook_com
 
 ${vcs_comm[cmd]} info | while IFS=: read a b; do p4info[${a// /_}]="${b## #}"; done
 p4base=${vcs_comm[basedir]}
@@ -16,9 +17,13 @@ local p4branch change
 # here down is synced as the revision.
 # I suppose the following might be slow on a tortuous client view.
 change="${${$(${vcs_comm[cmd]} changes -m 1 ...\#have)##Change }%% *}"
-zstyle -s ":vcs_info:${vcs}:${usercontext}:${rrn}" branchformat p4branch ||
-p4branch="%b:%r"
-zformat -f p4branch "${p4branch}" "b:${p4info[Client_name]}" \
-"r:$change"
-
+zstyle -s ":vcs_info:${vcs}:${usercontext}:${rrn}" branchformat p4branch || p4branch="%b:%r"
+hook_com=( branch "${p4info[Client_name]}" revision "${change}" )
+if VCS_INFO_hook 'set-branch-format' "${p4branch}"; then
+    zformat -f p4branch "${p4branch}" "b:${hook_com[branch]}" "r:${hook_com[revision]}"
+else
+    p4branch=${hook_com[branch-replace]}
+fi
+hook_com=()
 VCS_INFO_formats '' "${p4branch}" "${p4base}" '' '' "$change" ''
+return 0
diff --git a/Functions/VCS_Info/Backends/VCS_INFO_get_data_svk b/Functions/VCS_Info/Backends/VCS_INFO_get_data_svk
index 3df9a73..6107a14 100644
--- a/Functions/VCS_Info/Backends/VCS_INFO_get_data_svk
+++ b/Functions/VCS_Info/Backends/VCS_INFO_get_data_svk
@@ -4,10 +4,17 @@
 
 setopt localoptions NO_shwordsplit
 local svkbranch svkbase
+local -xA hook_com
 
 svkbase=${vcs_comm[basedir]}
 rrn=${svkbase:t}
 zstyle -s ":vcs_info:${vcs}:${usercontext}:${rrn}" branchformat svkbranch || svkbranch="%b:%r"
-zformat -f svkbranch "${svkbranch}" "b:${vcs_comm[branch]}" "r:${vcs_comm[revision]}"
+hook_com=( branch "${vcs_comm[branch]}" revision "${vcs_comm[revision]}" )
+if VCS_INFO_hook 'set-branch-format' "${svkbranch}"; then
+    zformat -f svkbranch "${svkbranch}" "b:${hook_com[branch]}" "r:${hook_com[revision]}"
+else
+    svkbranch=${hook_com[branch-replace]}
+fi
+hook_com=()
 VCS_INFO_formats '' "${svkbranch}" "${svkbase}" '' '' "${vcs_comm[revision]}" ''
 return 0
diff --git a/Functions/VCS_Info/Backends/VCS_INFO_get_data_svn b/Functions/VCS_Info/Backends/VCS_INFO_get_data_svn
index 75da22b..b1cb730 100644
--- a/Functions/VCS_Info/Backends/VCS_INFO_get_data_svn
+++ b/Functions/VCS_Info/Backends/VCS_INFO_get_data_svn
@@ -7,6 +7,7 @@
 setopt localoptions noksharrays extendedglob NO_shwordsplit
 local svnbase svnbranch a b rrn
 local -A svninfo parentinfo
+local -xA hook_com
 
 svnbase=".";
 svninfo=()
@@ -23,6 +24,12 @@ svnbase="$(VCS_INFO_realpath ${svnbase})"
 
 rrn=${svnbase:t}
 zstyle -s ":vcs_info:${vcs}:${usercontext}:${rrn}" branchformat svnbranch || svnbranch="%b:%r"
-zformat -f svnbranch "${svnbranch}" "b:${svninfo[URL]##*/}" "r:${svninfo[Revision]}"
+hook_com=( branch "${svninfo[URL]##*/}" revision "${svninfo[Revision]}" )
+if VCS_INFO_hook 'set-branch-format' "${svnbranch}"; then
+    zformat -f svnbranch "${svnbranch}" "b:${hook_com[branch]}" "r:${hook_com[revision]}"
+else
+    svnbranch=${hook_com[branch-replace]}
+fi
+hook_com=()
 VCS_INFO_formats '' "${svnbranch}" "${svnbase}" '' '' "${svninfo[Revision]}" ''
 return 0
diff --git a/Functions/VCS_Info/VCS_INFO_formats b/Functions/VCS_Info/VCS_INFO_formats
index 35b3b96..db7a8dd 100644
--- a/Functions/VCS_Info/VCS_INFO_formats
+++ b/Functions/VCS_Info/VCS_INFO_formats
@@ -3,7 +3,39 @@
 ## Distributed under the same BSD-ish license as zsh itself.
 
 setopt localoptions noksharrays NO_shwordsplit
-local action=$1 branch=$2 base=$3 staged=$4 unstaged=$5 rev=$6 misc=$7
+local msg tmp
+local -i i
+local -xA hook_com
+# The _origs are needed because hooks can change values and there would
+# be no way to get the originals back for later hooks (a hook is run for
+# each message, that's being created).
+hook_com=(
+    action        "$1"
+    action_orig   "$1"
+    branch        "$2"
+    branch_orig   "$2"
+    base          "$3"
+    base_orig     "$3"
+    staged        "$4"
+    staged_orig   "$4"
+    unstaged      "$5"
+    unstaged_orig "$5"
+    revision      "$6"
+    revision_orig "$6"
+    vcs           "${vcs}"
+    vcs_orig      "${vcs}"
+)
+shift 6
+i=0
+for tmp in "$@"; do
+    hook_com[misc$((i++))]="${tmp}"
+done
+hook_com[misc]=${(j:,:)argv}
+hook_com[misc_orig]=${hook_com[misc]}
+hook_com[base-name]="${${hook_com[base]}:t}"
+hook_com[base-name_orig]="${hook_com[base_name]}"
+hook_com[subdir]="$(VCS_INFO_reposub ${hook_com[base]})"
+hook_com[subdir_orig]="${hook_com[subdir]}"
 
 ## description:
 #   action:   a string that signals a certain non-default condition in the
@@ -13,18 +45,15 @@ local action=$1 branch=$2 base=$3 staged=$4 unstaged=$5 rev=$6 misc=$7
 #   base:     the full name of the repository's root directory.
 #   staged:   non-empty if the repository contains staged changes.
 #   unstaged: non-empty if the repository contains unstaged changes.
-#   rev:      an identifier of the currently checked out revision.
-#   misc:     a string that may contain anything the author likes.
+#   revision: an identifier of the currently checked out revision.
+#   misc0..N: a set of strings that may contain anything the author likes.
 #             the backends should document what they put in it and when.
 #
 # If an argument has no valid value for a given backend, an empty value
 # should be provided. eg:
 #   VCS_INFO_formats '' "${foobranch}" "${foobase}" '' '' '' "${foomisc}"
 
-local msg
-local -i i j
-
-if [[ -n ${action} ]] ; then
+if [[ -n ${hook_com[action]} ]] ; then
     zstyle -a ":vcs_info:${vcs}:${usercontext}:${rrn}" actionformats msgs
     (( ${#msgs} < 1 )) && msgs[1]=' (%s)-[%b|%a]-'
 else
@@ -32,29 +61,33 @@ else
     (( ${#msgs} < 1 )) && msgs[1]=' (%s)-[%b]-'
 fi
 
-if [[ -n ${staged} ]] ; then
-    zstyle -s ":vcs_info:${vcs}:${usercontext}:${rrn}" stagedstr staged
-    [[ -z ${staged} ]] && staged='S'
+if [[ -n ${hook_com[staged]} ]] ; then
+    zstyle -s ":vcs_info:${vcs}:${usercontext}:${rrn}" stagedstr tmp
+    [[ -z ${tmp} ]] && hook_com[staged]='S' || hook_com[staged]=${tmp}
 fi
 
-if [[ -n ${unstaged} ]] ; then
-    zstyle -s ":vcs_info:${vcs}:${usercontext}:${rrn}" unstagedstr unstaged
-    [[ -z ${unstaged} ]] && unstaged='U'
+if [[ -n ${hook_com[unstaged]} ]] ; then
+    zstyle -s ":vcs_info:${vcs}:${usercontext}:${rrn}" unstagedstr tmp
+    [[ -z ${tmp} ]] && hook_com[unstaged]='U' || hook_com[unstaged]=${tmp}
 fi
 
 (( ${#msgs} > maxexports )) && msgs[$(( maxexports + 1 )),-1]=()
 for i in {1..${#msgs}} ; do
-    zformat -f msg ${msgs[$i]}                      \
-                    a:${action}                     \
-                    b:${branch}                     \
-                    c:${staged}                     \
-                    i:${rev}                        \
-                    m:${misc}                       \
-                    r:${base:t}                     \
-                    s:${vcs}                        \
-                    u:${unstaged}                   \
-                    R:${base}                       \
-                    S:"$(VCS_INFO_reposub ${base})"
-    msgs[$i]=${msg}
+    if VCS_INFO_hook "set-message" $(( $i - 1 )) "${msgs[$i]}"; then
+        zformat -f msg ${msgs[$i]}                      \
+                        a:${hook_com[action]}           \
+                        b:${hook_com[branch]}           \
+                        c:${hook_com[staged]}           \
+                        i:${hook_com[revision]}         \
+                        m:${hook_com[misc]}             \
+                        r:${hook_com[base-name]}        \
+                        s:${hook_com[vcs]}              \
+                        u:${hook_com[unstaged]}         \
+                        R:${hook_com[base]}             \
+                        S:${hook_com[subdir]}
+        msgs[$i]=${msg}
+    else
+        msgs[$i]=${hook_com[message]}
+    fi
 done
 return 0
diff --git a/Functions/VCS_Info/VCS_INFO_hook b/Functions/VCS_Info/VCS_INFO_hook
new file mode 100644
index 0000000..71845d8
--- /dev/null
+++ b/Functions/VCS_Info/VCS_INFO_hook
@@ -0,0 +1,42 @@
+### vim:ft=zsh:foldmethod=marker
+## Written by Frank Terbeck <ft@bewatermyfriend.org>
+## Distributed under the same BSD-ish license as zsh itself.
+
+local hook func
+local -x context hook_name
+local -xi ret
+local -a hooks
+local -i debug
+
+ret=0
+hook_name="$1"
+shift
+context=":vcs_info:${vcs}+${hook_name}:${usercontext}:${rrn}"
+
+zstyle -t "${context}" debug && debug=1 || debug=0
+if (( debug )); then
+    printf 'VCS_INFO_hook: running hook: "%s"\n' "${hook_name}"
+    printf 'VCS_INFO_hook: current context: "%s"\n' "${context}"
+fi
+
+zstyle -a "${context}" hooks hooks || return 0
+# protect some internal variables in hooks
+typeset -r vcs rrn usercontext maxexports msgs vcs_comm
+for hook in ${hooks} ; do
+    func="+vi-${hook}"
+    if (( ${+functions[$func]} == 0 )); then
+        (( debug )) && printf '  + Unknown function: "%s"\n' "${func}"
+        continue
+    fi
+    (( debug )) && printf '  + Running function: "%s"\n' "${func}"
+    ${func} "$@"
+    case $? in
+        (0)
+            ;;
+        (*)
+            break
+            ;;
+    esac
+done
+typeset +r vcs rrn usercontext maxexports msgs vcs_comm
+return $ret
diff --git a/Functions/VCS_Info/vcs_info b/Functions/VCS_Info/vcs_info
index 4344d0b..906d984 100644
--- a/Functions/VCS_Info/vcs_info
+++ b/Functions/VCS_Info/vcs_info
@@ -18,6 +18,7 @@ static_functions=(
     VCS_INFO_check_com
     VCS_INFO_formats
     VCS_INFO_get_cmd
+    VCS_INFO_hook
     VCS_INFO_maxexports
     VCS_INFO_nvcsformats
     VCS_INFO_realpath
-- 
1.7.0


^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2010-02-15 23:58 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-02-15 23:52 PATCH: (0/3) vcs_info: hg fixes, enhancements and hooks for vcs_info Frank Terbeck
2010-02-15 23:52 ` PATCH: (1/3) vcs_info: hg fixes and enhancements Frank Terbeck
2010-02-15 23:52 ` PATCH: (2/3) vcs_info: hg bookmarks support Frank Terbeck
2010-02-15 23:52 ` PATCH: (3/3) vcs_info: Introduce the concept of hooks Frank Terbeck

Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/zsh/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).