# HG changeset patch
# User Sune Foldager <sune.folda...@edlund.dk>
# Date 1259164839 -3600
# Node ID 19efd1f442c48132e941a735628ecddd982652b4
# Parent  5c9682fb8cd7f5b6e44b3243a92800c8df28d4d4
changeset: add changeset and issue tracker links (#315)

This is also related to #369

Every word-boundary delimited string of 12 or 40 characters from the
range [0-9a-f] is considered a changeset link. Clicking on it in the
repository explorer, will jump to the given changeset if possible.

Issue tracker links are enabled when configured in the tortoisehg
section of the configuration files. There are two keys: issue.regex
and issue.link. The first defines the regex to match when picking up
issue numbers, while the second defines the command to run when an
issue number is recognized.

You may include groups in issue.regex, and corresponding {n} tokens
in issue.link (where n is a non-negative integer). {0} refers to the
entire string matched by issue.regex, while {1} refers to the first
group and so on. If no {n} tokens are found in issue.link, the entire
matched string is appended instead.

The examples below assume that os.system will open URLs in a browser.

BitBucket:
issue.regex = #(\d+)\b
issue.link = http://bitbucket.org/<your project and repo>/issue/{1}/

Mercurial:
issue.regex = \bissue\d+\b
issue.link = http://mercurial.selenic.com/bts/

diff --git a/tortoisehg/hgtk/changeset.py b/tortoisehg/hgtk/changeset.py
--- a/tortoisehg/hgtk/changeset.py
+++ b/tortoisehg/hgtk/changeset.py
@@ -6,6 +6,7 @@
 # GNU General Public License version 2, incorporated herein by reference.
 
 import os
+import re
 import gtk
 import gobject
 import pango
@@ -26,6 +27,14 @@
         self.glog_parent = None
         self.bfile = None
 
+        # initialize changeset/issue tracker link regex and dict
+        match = r'(\b[0-9a-f]{12}(?:[0-9a-f]{28})?\b)'
+        issue = self.ui.config('tortoisehg', 'issue.regex')
+        if issue:
+            match = r'%s|(%s)' % (match, issue)
+        self.bodyre = re.compile(match)
+        self.issuedict = dict()
+
     def get_title(self):
         title = _('%s changeset ') % self.get_reponame()
         rev = self.opts['rev']
@@ -261,7 +270,26 @@
         buf = self._buffer
         buf.set_text('')
         eob = buf.get_end_iter()
-        buf.insert(eob, desc + '\n\n')
+
+        pos = 0
+        self.issuedict.clear()
+        for m in self.bodyre.finditer(desc):
+            a, b = m.span()
+            if a > pos:
+                buf.insert(eob, desc[pos:a])
+                pos = b
+            groups = m.groups()
+            link = groups[0]
+            if link:
+                buf.insert_with_tags_by_name(eob, link, 'csetlink')
+            else:
+                link = groups[1]
+                if len(groups) > 2:
+                    self.issuedict[link] = groups[1:]
+                buf.insert_with_tags_by_name(eob, link, 'issuelink')
+        if pos < len(desc):
+            buf.insert(eob, desc[pos:])
+        buf.insert(eob, '\n\n')
 
     def append_diff(self, wfile):
         if not wfile:
@@ -726,6 +754,56 @@
                                    weight=pango.WEIGHT_BOLD))
         tag_table.add(make_texttag('yellowbg', background='yellow'))
 
+        issuelink_tag = make_texttag('issuelink', foreground='blue',
+                                     underline=pango.UNDERLINE_SINGLE)
+        issuelink_tag.connect('event', self.issuelink_event)
+        tag_table.add(issuelink_tag)
+        csetlink_tag = make_texttag('csetlink', foreground='blue',
+                                    underline=pango.UNDERLINE_SINGLE)
+        csetlink_tag.connect('event', self.csetlink_event)
+        tag_table.add(csetlink_tag)
+
+    def issuelink_event(self, tag, widget, event, liter):
+        if event.type != gtk.gdk.BUTTON_RELEASE:
+            return
+        text = self.get_link_text(tag, widget, liter)
+        if not text:
+            return
+        link = self.ui.config('tortoisehg', 'issue.link')
+        if link:
+            groups = self.issuedict.get(text, [text])
+            link, num = re.subn(r'\{(\d+)\}', lambda m:
+                groups[int(m.group(1))], link)
+            if not num:
+                link += text
+            os.startfile(link)
+
+    def csetlink_event(self, tag, widget, event, liter):
+        if event.type != gtk.gdk.BUTTON_RELEASE:
+            return
+        text = self.get_link_text(tag, widget, liter)
+        if not text:
+            return
+        try:
+            rev = self.repo[text].rev()
+            if self.graphview:
+                self.graphview.set_revision_id(rev, load=True)
+            else:
+                self.load_details(rev)
+        except RepoError:
+            pass
+
+    def get_link_text(self, tag, widget, liter):
+        text_buffer = widget.get_buffer()
+        beg = liter.copy()
+        while not beg.begins_tag(tag):
+            beg.backward_char()
+        end = liter.copy()
+        while not end.ends_tag(tag):
+            end.forward_char()
+        text = text_buffer.get_text(beg, end)
+        return text
+
     def file_button_release(self, widget, event):
         if event.button == 3 and not (event.state & (gtk.gdk.SHIFT_MASK |
             gtk.gdk.CONTROL_MASK)):

------------------------------------------------------------------------------
Let Crystal Reports handle the reporting - Free Crystal Reports 2008 30-Day 
trial. Simplify your report design, integration and deployment - and focus on 
what you do best, core application coding. Discover what's new with
Crystal Reports now.  http://p.sf.net/sfu/bobj-july
_______________________________________________
Tortoisehg-develop mailing list
Tortoisehg-develop@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/tortoisehg-develop

Reply via email to