# 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