Commits

Georg Brandl  committed 9f75dd9

CTags feature:
* rename parameter "urlformat" to "tagurlformat" to make clear it belongs to the tags feature
* change NameError to RuntimeError and tone down the message
* document that python-ctags must be installed to use the tagsfile feature
* move test tags file from examplefiles/, where it will be attempted as a test file
* fix test to work if python-ctags is not installed
* changelog/attribution

  • Participants
  • Parent commits e0d12b0

Comments (0)

Files changed (6)

 * Erick Tryzelaar -- Felix lexer
 * Daniele Varrazzo -- PostgreSQL lexers
 * Abe Voelker -- OpenEdge ABL lexer
+* Pepijn de Vos -- HTML formatter CTags support
 * Whitney Young -- ObjectiveC lexer
 * Matthias Vallentin -- Bro lexer
 * Nathan Weizenbaum -- Haml and Sass lexers
   * Windows Registry (#819)
   * Xtend (PR#68)
 
+- The HTML formatter now supports linking to tags using CTags files, when the
+  python-ctags package is installed (PR#87)
+
 - When deriving a lexer from another lexer with token definitions, definitions
   for states not in the child lexer are now inherited.  If you override a state
   in the child lexer, an "inherit" keyword has been added to insert the base

File pygments/formatters/html.py

 """
 
 import os
+import sys
 import os.path
-import sys
 import StringIO
 
 from pygments.formatter import Formatter
 from pygments.util import get_bool_opt, get_int_opt, get_list_opt, bytes
 
 try:
-    from ctags import CTags, TagEntry
+    import ctags
 except ImportError:
-    CTags = None
+    ctags = None
 
 __all__ = ['HtmlFormatter']
 
     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.
+    generate hyperlinks from names to their definition.  You must enable
+    `anchorlines` and run ctags with the `-n` option for this to work.  The
+    `python-ctags` module from PyPI must be installed to use this feature;
+    otherwise a `RuntimeError` will be raised.
 
     The `get_style_defs(arg='')` method of a `HtmlFormatter` returns a string
     containing CSS rules for the CSS classes used by the formatter. The
     `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}`.
+        tags file should specify line numbers (see the `-n` option to ctags).
+
+    `tagurlformat`
+        A string formatting pattern used to generate links to ctags definitions.
+        Avaliabe variable are `%(path)s`, `%(fname)s` and `%(fext)s`.
         Defaults to an empty string, resulting in just `#prefix-number` links.
 
+
     **Subclassing the HTML formatter**
 
     *New in Pygments 0.7.*
         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', ''))
+        self.tagurlformat = self._decodeifneeded(options.get('tagurlformat', ''))
 
         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\'.')
+            if not ctags:
+                raise RuntimeError('The "ctags" package must to be installed '
+                                   'to be able to use the "tagsfile" feature.')
+            self._ctags = ctags.CTags(self.tagsfile)
 
         linenos = options.get('linenos', False)
         if linenos == 'inline':
         getcls = self.ttype2class.get
         c2s = self.class2style
         escape_table = _escape_html_table
-
         tagsfile = self.tagsfile
-        urlformat = self.urlformat
 
         lspan = ''
         line = ''
                 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])
+                    url = self.tagurlformat % {'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
             yield 1, line + (lspan and '</span>') + lsep
 
     def _lookup_ctag(self, token):
-        entry = TagEntry()
-        if self.ct.find(entry, token, 0):
+        entry = ctags.TagEntry()
+        if self._ctags.find(entry, token, 0):
             return entry['file'], entry['lineNumber']
         else:
             return None, None

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/support/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

         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())
+    def test_ctags(self):
+        try:
+            import ctags
+        except ImportError:
+            # we can't check without the ctags module, but at least check the exception
+            self.assertRaises(RuntimeError, HtmlFormatter, tagsfile='support/tags')
+        else:
+            # this tagfile says that test_ctags() is on line 165, even if it isn't
+            # anymore in the actual source
+            fmt = HtmlFormatter(tagsfile='support/tags', lineanchors='L',
+                                tagurlformat='%(fname)s%(fext)s')
+            outfile = StringIO.StringIO()
+            fmt.format(tokensource, outfile)
+            self.assertTrue('<a href="test_html_formatter.py#L-165">test_ctags</a>'
+                            in outfile.getvalue())