# Pygments HTML Template --  pygments.formatter plugin

You can write a template for pygments HtmlFormatter with this package.

## Template Example

``` xml
<Pygmentshtmltemplate>  <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -(1)-->
  <h1>${title}</h1> <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -(2)-->
  <ol class="${cssclass}">  <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -(3)-->
    <Line>  <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -(4)-->
      <li _="${highlighted}" id="line-${lineno}" class="hll"><Tokens/></li> <!-- - - - - - - - - - - -(5)-->
      <li id="line-${lineno}"><Tokens/></li>  <!-- - - - - - - - - - - - - - - - - - - - - - - - - - -(6)-->
    </Line>
    <Tokens>  <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -(7)-->
      <code class="${token_class} init"><Token type="Name.Function" fullmatch="__init__"/></code> <!--(8)-->
      <code class="${token_class}" title="${token_type}"><Token/></code>  <!-- - - - - - - - - - - - -(9)-->
    </Tokens>
  </ol> <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - (10)-->
  <p>${filename}</p>  <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - (11)-->
</Pygmentshtmltemplate>
```

- (1): Must have a root node `Pygmentshtmltemplate`
  - The `.parsers.parse` function uses parser `xml.sax.make_parser()` in the Python standard library. So the template must be able to read by the the SAX parser. You should not parse untrusted data.
- (2), (3), (10), (11): Wrapping html parts
  - `<Line>` (4) and `<Tokens>` (7) block represent line formats and each token format in each code line.
  - In (2), `${title}` represents `title` option which is set to the formatter class. And that is replaced with its value.
- (5), (6): Select a line wrapper
  - Each of `<Line>` (4) children represents a line format. Html elements can have options value for testing. If all values non-nil, then that line will be rendered.
    - `highlight` and `lineno` are not the formatter class options, but implicitly generated from `linenostart` and `hl_lines` option.
	- `False`, `None`, and empty Sequence are failed, but `0` (zero) is success.
	- Properties which name starts with `_` are only used for testing, but they are not rendered.
	- The formatter tries the tests in textual order. The last line of the `<Line>` block should be always successful.
- (8), (9): Select a token wrapper
  - Html properties are the same as line’s.
    - `token_class` and `token_type` are not the formatter options.
	- `token_class` is generated by `formatter._get_css_classes(ttype: pygments.token._TokenType)`.
	- `token_type` is generated by `'.'.join(ttype: pygments.token._TokenType)`
  - `<Token>` (8) element can have `type`, `match`, and `fullmatch` properties for testing.
    - When Pygments gives the formatter a ‘tokentype’ and a ‘tokenvalue’, the formatter will test follows:
	  - `pygments.token.is_token_subtype(tokentype, pytments.token.string_to_tokentype(${type}))`
	  - `re.fullmatch(${fullmatch}, tokenvalue)` or `re.match(${match}, tokenvalue)`
  - When all tests are failed, a ‘tokenvalue’ is rendered as it is.

## Install

```
% pip install pygmentshtmltemplate
```

You can confirm pygments.formatters plugins:

``` python
>>> import importlib
>>> importlib.metadata.entry_points().select(group='pygments.formatters')
[EntryPoint(name='pygmentshtmltemplate', value='pygmentshtmltemplate:FormatterWithTemplate', group='pygments.formatters')]
```

## Usage

`FormatterWithTemplate` is the subclass of `pygments.formatters.HtmlFormatter`. `FormatterWithTemplate.name` is `FormatterWithTemplate`, and `FormatterWithTemplate.aliases` is `['fmtr_tmpl']`

### Formatter options

The options `filename`, `cssclass`, `linenostart`, `hl_lines`, `classprefix`, and `title` are in common spec with the `HtmlFormatter`. Other options of the `HtmlFormatter` are suppressed by the `FormatterWithTemplate`. Almost other options may get alternatives by wrapping parts of a template.

There is the original option `template` which is set to the path of a template file. If the `template` isn’t set, the default template (the following code) is used.

``` xml
<Pygmentshtmltemplate>
  <ol class="${cssclass}">
    <Line>
      <li _="${highlighted}" class="hll"><Tokens/></li>
      <li><Tokens/></li>
    </Line>
    <Tokens>
      <code class="${token_class}"><Token/></code>
    </Tokens>
  </ol>
</Pygmentshtmltemplate>
```

### Command line

The command line interface is provided by the Pygments. For example:

```
$ pygmentize -f fmtr_tmpl -O template=template.xml -o output.html want_to_highlight.py

$ pygmentize -f fmtr_tmpl -S default -a .highlight
```

### In reStructuredText

You can use this formatter in the reStructuredText with docutils.

First, register the reST directive `pygmentshtmltemplate.docutis.PygHtmlTmplRstDirective`. The target source code can be provided at that directive content block. Or using `:file:` option, you can provide the source file.

``` python
from docutils.core import publish_string
from docutils.writers import html4css1
from docutils.parsers.rst import directives

from pygmentshtmltemplate.docutils import PygHtmlTmplRstDirective

directives.register_directive('fmtr-tmpl', PygHtmlTmplRstDirective)

ReST = f'''\
PygHtmlTmplRstDirective test
============================

pygmentize a file

.. fmtr-tmpl:: python :file: {__file__}

pygmentize a content block

.. fmtr-tmpl:: python
   :hl_lines: 1

   """highlight this line"""
   def hello(foo):
       print(f'hello, {{foo}}!')
'''

if __name__ == '__main__':
    formatted = publish_string(ReST, writer=html4css1.Writer())
    with open(output, 'wb') as out:
        out.write(formatted)
```

## Future considered

- Template for the `full` option
- Now, the `formatter.get_style_defs()` method corresponds to the only default template.
