Commits

Georg Brandl committed e0d12b0 Merge

Merge pull request #87 (ctags link support for HTML formatter) from https://bitbucket.org/pepijndevos/pygments-main/overview

  • Participants
  • Parent commits 03134df, ce2f619

Comments (0)

Files changed (3)

File pygments/formatters/html.py

 """
 
 import os
+import os.path
 import sys
 import StringIO
 
 from pygments.token import Token, Text, STANDARD_TYPES
 from pygments.util import get_bool_opt, get_int_opt, get_list_opt, bytes
 
+try:
+    from ctags import CTags, TagEntry
+except ImportError:
+    CTags = None
 
 __all__ = ['HtmlFormatter']
 
     the style definitions inside a ``<style>`` tag, or in a separate file if
     the `cssfile` option is given.
 
+    When `tagsfile` is set to the path of a ctags index file, it is used to
+    generate hyperlinks from names to their definition. You must enable
+    `anchorlines` and run ctags with the `-n` option for this to work.
+
     The `get_style_defs(arg='')` method of a `HtmlFormatter` returns a string
     containing CSS rules for the CSS classes used by the formatter. The
     argument `arg` can be used to specify additional CSS selectors that
         If set to `True`, will wrap line numbers in <a> tags. Used in
         combination with `linenos` and `lineanchors`.
 
+    `tagsfile`
+        If set to the path of a ctags file, wrap names in anchor tags that
+        link to their definitions. `lineanchors` should be used, and the
+        tags file should specify line numbers(see the `-n` option to ctags)
+    
+    `urlformat`
+        A `String.format` pattern used to generate links to ctags definitions.
+        Avaliabe variable are `{path}`, `{fname}` and `{fext}`.
+        Defaults to an empty string, resulting in just `#prefix-number` links.
 
     **Subclassing the HTML formatter**
 
         self.prestyles = self._decodeifneeded(options.get('prestyles', ''))
         self.cssfile = self._decodeifneeded(options.get('cssfile', ''))
         self.noclobber_cssfile = get_bool_opt(options, 'noclobber_cssfile', False)
+        self.tagsfile = self._decodeifneeded(options.get('tagsfile', ''))
+        self.urlformat = self._decodeifneeded(options.get('urlformat', ''))
+
+        if self.tagsfile:
+            if CTags:
+                self.ct = CTags(self.tagsfile)
+            else:
+                raise NameError('Hey! ctags doesn\'t seem to be installed. Try \'pip install python-ctags\'.')
 
         linenos = options.get('linenos', False)
         if linenos == 'inline':
         c2s = self.class2style
         escape_table = _escape_html_table
 
+        tagsfile = self.tagsfile
+        urlformat = self.urlformat
+
         lspan = ''
         line = ''
         for ttype, value in tokensource:
 
             parts = value.translate(escape_table).split('\n')
 
+            if tagsfile and ttype in Token.Name:
+                filename, linenumber = self._lookup_ctag(value)
+                if linenumber:
+                    base, filename = os.path.split(filename)
+                    filename, extension = os.path.splitext(filename)
+                    url = urlformat.format(path=base, fname=filename, fext=extension)
+                    parts[0] = "<a href=\"%s#%s-%d\">%s" % (url, self.lineanchors, linenumber, parts[0])
+                    parts[-1] = parts[-1] + "</a>"
+
             # for all but the last line
             for part in parts[:-1]:
                 if line:
         if line:
             yield 1, line + (lspan and '</span>') + lsep
 
+    def _lookup_ctag(self, token):
+        entry = TagEntry()
+        if self.ct.find(entry, token, 0):
+            return entry['file'], entry['lineNumber']
+        else:
+            return None, None
+
     def _highlight_lines(self, tokensource):
         """
         Highlighted the lines specified in the `hl_lines` option by

File tests/examplefiles/tags

+!_TAG_FILE_FORMAT	2	/extended format; --format=1 will not append ;" to lines/
+!_TAG_FILE_SORTED	1	/0=unsorted, 1=sorted, 2=foldcase/
+!_TAG_PROGRAM_AUTHOR	Darren Hiebert	/dhiebert@users.sourceforge.net/
+!_TAG_PROGRAM_NAME	Exuberant Ctags	//
+!_TAG_PROGRAM_URL	http://ctags.sourceforge.net	/official site/
+!_TAG_PROGRAM_VERSION	5.8	//
+HtmlFormatter	test_html_formatter.py	19;"	i
+HtmlFormatterTest	test_html_formatter.py	34;"	c
+NullFormatter	test_html_formatter.py	19;"	i
+PythonLexer	test_html_formatter.py	18;"	i
+StringIO	test_html_formatter.py	13;"	i
+dirname	test_html_formatter.py	16;"	i
+escape_html	test_html_formatter.py	20;"	i
+fp	test_html_formatter.py	27;"	v
+inspect	test_html_formatter.py	15;"	i
+isfile	test_html_formatter.py	16;"	i
+join	test_html_formatter.py	16;"	i
+os	test_html_formatter.py	10;"	i
+re	test_html_formatter.py	11;"	i
+subprocess	test_html_formatter.py	125;"	i
+support	test_html_formatter.py	23;"	i
+tempfile	test_html_formatter.py	14;"	i
+test_all_options	test_html_formatter.py	72;"	m	class:HtmlFormatterTest
+test_correct_output	test_html_formatter.py	35;"	m	class:HtmlFormatterTest
+test_ctags	test_html_formatter.py	165;"	m	class:HtmlFormatterTest
+test_external_css	test_html_formatter.py	48;"	m	class:HtmlFormatterTest
+test_get_style_defs	test_html_formatter.py	141;"	m	class:HtmlFormatterTest
+test_lineanchors	test_html_formatter.py	98;"	m	class:HtmlFormatterTest
+test_lineanchors_with_startnum	test_html_formatter.py	106;"	m	class:HtmlFormatterTest
+test_linenos	test_html_formatter.py	82;"	m	class:HtmlFormatterTest
+test_linenos_with_startnum	test_html_formatter.py	90;"	m	class:HtmlFormatterTest
+test_unicode_options	test_html_formatter.py	155;"	m	class:HtmlFormatterTest
+test_valid_output	test_html_formatter.py	114;"	m	class:HtmlFormatterTest
+tokensource	test_html_formatter.py	29;"	v
+uni_open	test_html_formatter.py	21;"	i
+unittest	test_html_formatter.py	12;"	i

File tests/test_html_formatter.py

         tfile = os.fdopen(handle, 'w+b')
         fmt.format(tokensource, tfile)
         tfile.close()
+
+    def test_ctags(self): # make sure this is in fact line 165 and the tags file says so
+        fmt = HtmlFormatter(tagsfile='examplefiles/tags', lineanchors="L")
+        outfile = StringIO.StringIO()
+        fmt.format(tokensource, outfile)
+        self.assertTrue('<a href="#L-165">test_ctags</a>' in outfile.getvalue())