{"action":"created","comment":{"url":"https://api.github.com/repos/jupyter/nbconvert/pulls/comments/51631619","id":51631619,"diff_hunk":"@@ -1,155 +1,264 @@\n-\"\"\"Filters for processing ANSI colors within Jinja templates.\n-\"\"\"\n+\"\"\"Filters for processing ANSI colors within Jinja templates.\"\"\"\n \n # Copyright (c) IPython Development Team.\n # Distributed under the terms of the Modified BSD License.\n \n import re\n-from nbconvert.utils import coloransi\n+import jinja2\n from ipython_genutils.text import strip_ansi\n \n-\n __all__ = [\n     'strip_ansi',\n     'ansi2html',\n-    'single_ansi2latex',\n     'ansi2latex'\n ]\n \n-ansi_colormap = {\n-    '30': 'ansiblack',\n-    '31': 'ansired',\n-    '32': 'ansigreen',\n-    '33': 'ansiyellow',\n-    '34': 'ansiblue',\n-    '35': 'ansipurple',\n-    '36': 'ansicyan',\n-    '37': 'ansigrey',\n-    '01': 'ansibold',\n-}\n-\n-html_escapes = {\n-    '<': '&lt;',\n-    '>': '&gt;',\n-    \"'\": '&apos;',\n-    '\"': '&quot;',\n-    '`': '&#96;',\n-}\n-ansi_re = re.compile('\\x1b' + r'\\[([\\dA-Fa-f;]*?)m')\n+_FG_HTML = (\n+    'ansiblack',\n+    'ansired',\n+    'ansigreen',\n+    'ansiyellow',\n+    'ansiblue',\n+    'ansipurple',\n+    'ansicyan',\n+    'ansigray',\n+)\n+\n+_BG_HTML = (\n+    'ansibgblack',\n+    'ansibgred',\n+    'ansibggreen',\n+    'ansibgyellow',\n+    'ansibgblue',\n+    'ansibgpurple',\n+    'ansibgcyan',\n+    'ansibggray',\n+)\n+\n+_FG_LATEX = (\n+    'black',\n+    'red',\n+    'green',\n+    'brown',\n+    'blue',\n+    'purple',\n+    'cyan',\n+    'lightgray',\n+)\n+\n+_BG_LATEX = (\n+    'darkgray',\n+    'lightred',\n+    'lightgreen',\n+    'yellow',\n+    'lightblue',\n+    'lightpurple',\n+    'lightcyan',\n+    'white',\n+)\n+\n \n def ansi2html(text):\n     \"\"\"\n-    Convert ansi colors to html colors.\n-    \n+    Convert ANSI colors to HTML colors.\n+\n     Parameters\n     ----------\n     text : str\n-        Text containing ansi colors to convert to html\n+        Text containing ANSI colors to convert to HTML\n+\n     \"\"\"\n+    text = str(jinja2.utils.escape(text))\n+    return _ansi2anything(text, _htmlconverter)\n \n-    # do ampersand first\n-    text = text.replace('&', '&amp;')\n-    \n-    for c, escape in html_escapes.items():\n-        text = text.replace(c, escape)\n-\n-    m = ansi_re.search(text)\n-    opened = False\n-    cmds = []\n-    opener = ''\n-    closer = ''\n-    while m:\n-        cmds = m.groups()[0].split(';')\n-        closer = '</span>' if opened else ''\n-        \n-        # True if there is there more than one element in cmds, *or*\n-        # if there is only one but it is not equal to a string of zeroes.\n-        opened = len(cmds) > 1 or cmds[0] != '0' * len(cmds[0])\n-        classes = []\n-        for cmd in cmds:\n-            if cmd in ansi_colormap:\n-                classes.append(ansi_colormap[cmd])\n-\n-        if classes:\n-            opener = '<span class=\"%s\">' % (' '.join(classes))\n-        else:\n-            opener = ''\n-        text = re.sub(ansi_re, closer + opener, text, 1)\n \n-        m = ansi_re.search(text)\n+def ansi2latex(text):\n+    \"\"\"\n+    Convert ANSI colors to LaTeX colors.\n+\n+    Parameters\n+    ----------\n+    text : str\n+        Text containing ANSI colors to convert to LaTeX\n+\n+    \"\"\"\n+    return _ansi2anything(text, _latexconverter)\n+\n+\n+def _htmlconverter(fg, bg, bold):\n+    \"\"\"\n+    Return start and end tags for given foreground/background/bold.\n+\n+    \"\"\"\n+    if (fg, bg, bold) == (None, None, False):\n+        return '', ''\n+\n+    classes = []\n+    styles = []\n \n-    if opened:\n-        text += '</span>'\n-    return text\n+    if isinstance(fg, int):\n+        classes.append(_FG_HTML[fg])","path":"nbconvert/filters/ansi.py","position":160,"original_position":160,"commit_id":"6be45f6cd2d2022a32d1ea40c09bbbad86061db1","original_commit_id":"6be45f6cd2d2022a32d1ea40c09bbbad86061db1","user":{"login":"minrk","id":151929,"avatar_url":"https://avatars.githubusercontent.com/u/151929?v=3","gravatar_id":"","url":"https://api.github.com/users/minrk","html_url":"https://github.com/minrk","followers_url":"https://api.github.com/users/minrk/followers","following_url":"https://api.github.com/users/minrk/following{/other_user}","gists_url":"https://api.github.com/users/minrk/gists{/gist_id}","starred_url":"https://api.github.com/users/minrk/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/minrk/subscriptions","organizations_url":"https://api.github.com/users/minrk/orgs","repos_url":"https://api.github.com/users/minrk/repos","events_url":"https://api.github.com/users/minrk/events{/privacy}","received_events_url":"https://api.github.com/users/minrk/received_events","type":"User","site_admin":false},"body":"Are KeyErrors impossible on these lookups?","created_at":"2016-02-02T20:41:41Z","updated_at":"2016-02-02T20:41:41Z","html_url":"https://github.com/jupyter/nbconvert/pull/225#discussion_r51631619","pull_request_url":"https://api.github.com/repos/jupyter/nbconvert/pulls/225","_links":{"self":{"href":"https://api.github.com/repos/jupyter/nbconvert/pulls/comments/51631619"},"html":{"href":"https://github.com/jupyter/nbconvert/pull/225#discussion_r51631619"},"pull_request":{"href":"https://api.github.com/repos/jupyter/nbconvert/pulls/225"}}},"pull_request":{"url":"https://api.github.com/repos/jupyter/nbconvert/pulls/225","id":58025092,"html_url":"https://github.com/jupyter/nbconvert/pull/225","diff_url":"https://github.com/jupyter/nbconvert/pull/225.diff","patch_url":"https://github.com/jupyter/nbconvert/pull/225.patch","issue_url":"https://api.github.com/repos/jupyter/nbconvert/issues/225","number":225,"state":"open","locked":false,"title":"Re-write ANSI color handling","user":{"login":"mgeier","id":705404,"avatar_url":"https://avatars.githubusercontent.com/u/705404?v=3","gravatar_id":"","url":"https://api.github.com/users/mgeier","html_url":"https://github.com/mgeier","followers_url":"https://api.github.com/users/mgeier/followers","following_url":"https://api.github.com/users/mgeier/following{/other_user}","gists_url":"https://api.github.com/users/mgeier/gists{/gist_id}","starred_url":"https://api.github.com/users/mgeier/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mgeier/subscriptions","organizations_url":"https://api.github.com/users/mgeier/orgs","repos_url":"https://api.github.com/users/mgeier/repos","events_url":"https://api.github.com/users/mgeier/events{/privacy}","received_events_url":"https://api.github.com/users/mgeier/received_events","type":"User","site_admin":false},"body":"Initially, I only wanted to have less ugly colors for Python exceptions shown in LaTeX output.\r\nBut then I found out that the whole ANSI-handling code is quite a mess (which I already hinted on in #214).\r\n\r\nThe code handling HTML and LaTeX was completely separate and showed quite different behavior.\r\n\r\nI re-implemented the whole thing, now most of the code is shared between HTML and LaTeX output. I also implemented the 256 indexed colors and 24-bit RGB colors which are supported by the live notebook. And I fixed several bugs along the way (e.g. #174).\r\n\r\nThere are a few caveats, though:\r\n\r\n* The behavior is still different from the live notebook, but I consider my implementation more correct in several points. I'll probably list the bugs in a separate issue there, once I got some feedback here.\r\n\r\n* The 8 (resp. 16) default colors are still different between HTML and LaTeX because I didn't dare to change the color definitions. But probably this should be adjusted?\r\n\r\n* I had to add 2 `\\newcommand`s in the LaTeX template because there were errors when I tried to use something like `\\textcolor[RGB]{255,0,0}{some text}` within a `Verbatim` environment. I think it may have something to do with the commas (I read somewhere that they might be \"active\", whatever that means ...), but I don't know enough TeX-fu to find, let alone fix the error. Can somebody help with that?","created_at":"2016-02-02T17:58:51Z","updated_at":"2016-02-02T20:41:42Z","closed_at":null,"merged_at":null,"merge_commit_sha":"aefd94171fbe90725334dd34f1cf6a133e9d94b7","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/jupyter/nbconvert/pulls/225/commits","review_comments_url":"https://api.github.com/repos/jupyter/nbconvert/pulls/225/comments","review_comment_url":"https://api.github.com/repos/jupyter/nbconvert/pulls/comments{/number}","comments_url":"https://api.github.com/repos/jupyter/nbconvert/issues/225/comments","statuses_url":"https://api.github.com/repos/jupyter/nbconvert/statuses/6be45f6cd2d2022a32d1ea40c09bbbad86061db1","head":{"label":"mgeier:ansi","ref":"ansi","sha":"6be45f6cd2d2022a32d1ea40c09bbbad86061db1","user":{"login":"mgeier","id":705404,"avatar_url":"https://avatars.githubusercontent.com/u/705404?v=3","gravatar_id":"","url":"https://api.github.com/users/mgeier","html_url":"https://github.com/mgeier","followers_url":"https://api.github.com/users/mgeier/followers","following_url":"https://api.github.com/users/mgeier/following{/other_user}","gists_url":"https://api.github.com/users/mgeier/gists{/gist_id}","starred_url":"https://api.github.com/users/mgeier/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mgeier/subscriptions","organizations_url":"https://api.github.com/users/mgeier/orgs","repos_url":"https://api.github.com/users/mgeier/repos","events_url":"https://api.github.com/users/mgeier/events{/privacy}","received_events_url":"https://api.github.com/users/mgeier/received_events","type":"User","site_admin":false},"repo":{"id":49829768,"name":"nbconvert","full_name":"mgeier/nbconvert","owner":{"login":"mgeier","id":705404,"avatar_url":"https://avatars.githubusercontent.com/u/705404?v=3","gravatar_id":"","url":"https://api.github.com/users/mgeier","html_url":"https://github.com/mgeier","followers_url":"https://api.github.com/users/mgeier/followers","following_url":"https://api.github.com/users/mgeier/following{/other_user}","gists_url":"https://api.github.com/users/mgeier/gists{/gist_id}","starred_url":"https://api.github.com/users/mgeier/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mgeier/subscriptions","organizations_url":"https://api.github.com/users/mgeier/orgs","repos_url":"https://api.github.com/users/mgeier/repos","events_url":"https://api.github.com/users/mgeier/events{/privacy}","received_events_url":"https://api.github.com/users/mgeier/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/mgeier/nbconvert","description":"Jupyter Notebook Conversion","fork":true,"url":"https://api.github.com/repos/mgeier/nbconvert","forks_url":"https://api.github.com/repos/mgeier/nbconvert/forks","keys_url":"https://api.github.com/repos/mgeier/nbconvert/keys{/key_id}","collaborators_url":"https://api.github.com/repos/mgeier/nbconvert/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/mgeier/nbconvert/teams","hooks_url":"https://api.github.com/repos/mgeier/nbconvert/hooks","issue_events_url":"https://api.github.com/repos/mgeier/nbconvert/issues/events{/number}","events_url":"https://api.github.com/repos/mgeier/nbconvert/events","assignees_url":"https://api.github.com/repos/mgeier/nbconvert/assignees{/user}","branches_url":"https://api.github.com/repos/mgeier/nbconvert/branches{/branch}","tags_url":"https://api.github.com/repos/mgeier/nbconvert/tags","blobs_url":"https://api.github.com/repos/mgeier/nbconvert/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/mgeier/nbconvert/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/mgeier/nbconvert/git/refs{/sha}","trees_url":"https://api.github.com/repos/mgeier/nbconvert/git/trees{/sha}","statuses_url":"https://api.github.com/repos/mgeier/nbconvert/statuses/{sha}","languages_url":"https://api.github.com/repos/mgeier/nbconvert/languages","stargazers_url":"https://api.github.com/repos/mgeier/nbconvert/stargazers","contributors_url":"https://api.github.com/repos/mgeier/nbconvert/contributors","subscribers_url":"https://api.github.com/repos/mgeier/nbconvert/subscribers","subscription_url":"https://api.github.com/repos/mgeier/nbconvert/subscription","commits_url":"https://api.github.com/repos/mgeier/nbconvert/commits{/sha}","git_commits_url":"https://api.github.com/repos/mgeier/nbconvert/git/commits{/sha}","comments_url":"https://api.github.com/repos/mgeier/nbconvert/comments{/number}","issue_comment_url":"https://api.github.com/repos/mgeier/nbconvert/issues/comments{/number}","contents_url":"https://api.github.com/repos/mgeier/nbconvert/contents/{+path}","compare_url":"https://api.github.com/repos/mgeier/nbconvert/compare/{base}...{head}","merges_url":"https://api.github.com/repos/mgeier/nbconvert/merges","archive_url":"https://api.github.com/repos/mgeier/nbconvert/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/mgeier/nbconvert/downloads","issues_url":"https://api.github.com/repos/mgeier/nbconvert/issues{/number}","pulls_url":"https://api.github.com/repos/mgeier/nbconvert/pulls{/number}","milestones_url":"https://api.github.com/repos/mgeier/nbconvert/milestones{/number}","notifications_url":"https://api.github.com/repos/mgeier/nbconvert/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/mgeier/nbconvert/labels{/name}","releases_url":"https://api.github.com/repos/mgeier/nbconvert/releases{/id}","deployments_url":"https://api.github.com/repos/mgeier/nbconvert/deployments","created_at":"2016-01-17T18:29:48Z","updated_at":"2016-01-17T18:29:49Z","pushed_at":"2016-02-02T17:32:19Z","git_url":"git://github.com/mgeier/nbconvert.git","ssh_url":"git@github.com:mgeier/nbconvert.git","clone_url":"https://github.com/mgeier/nbconvert.git","svn_url":"https://github.com/mgeier/nbconvert","homepage":"http://nbconvert.readthedocs.org/","size":1642,"stargazers_count":0,"watchers_count":0,"language":"Python","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"jupyter:master","ref":"master","sha":"4f9ac70ceb14f1ce1e042d6ad6b612e86900cb3f","user":{"login":"jupyter","id":7388996,"avatar_url":"https://avatars.githubusercontent.com/u/7388996?v=3","gravatar_id":"","url":"https://api.github.com/users/jupyter","html_url":"https://github.com/jupyter","followers_url":"https://api.github.com/users/jupyter/followers","following_url":"https://api.github.com/users/jupyter/following{/other_user}","gists_url":"https://api.github.com/users/jupyter/gists{/gist_id}","starred_url":"https://api.github.com/users/jupyter/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jupyter/subscriptions","organizations_url":"https://api.github.com/users/jupyter/orgs","repos_url":"https://api.github.com/users/jupyter/repos","events_url":"https://api.github.com/users/jupyter/events{/privacy}","received_events_url":"https://api.github.com/users/jupyter/received_events","type":"Organization","site_admin":false},"repo":{"id":33653617,"name":"nbconvert","full_name":"jupyter/nbconvert","owner":{"login":"jupyter","id":7388996,"avatar_url":"https://avatars.githubusercontent.com/u/7388996?v=3","gravatar_id":"","url":"https://api.github.com/users/jupyter","html_url":"https://github.com/jupyter","followers_url":"https://api.github.com/users/jupyter/followers","following_url":"https://api.github.com/users/jupyter/following{/other_user}","gists_url":"https://api.github.com/users/jupyter/gists{/gist_id}","starred_url":"https://api.github.com/users/jupyter/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jupyter/subscriptions","organizations_url":"https://api.github.com/users/jupyter/orgs","repos_url":"https://api.github.com/users/jupyter/repos","events_url":"https://api.github.com/users/jupyter/events{/privacy}","received_events_url":"https://api.github.com/users/jupyter/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/jupyter/nbconvert","description":"Jupyter Notebook Conversion","fork":false,"url":"https://api.github.com/repos/jupyter/nbconvert","forks_url":"https://api.github.com/repos/jupyter/nbconvert/forks","keys_url":"https://api.github.com/repos/jupyter/nbconvert/keys{/key_id}","collaborators_url":"https://api.github.com/repos/jupyter/nbconvert/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/jupyter/nbconvert/teams","hooks_url":"https://api.github.com/repos/jupyter/nbconvert/hooks","issue_events_url":"https://api.github.com/repos/jupyter/nbconvert/issues/events{/number}","events_url":"https://api.github.com/repos/jupyter/nbconvert/events","assignees_url":"https://api.github.com/repos/jupyter/nbconvert/assignees{/user}","branches_url":"https://api.github.com/repos/jupyter/nbconvert/branches{/branch}","tags_url":"https://api.github.com/repos/jupyter/nbconvert/tags","blobs_url":"https://api.github.com/repos/jupyter/nbconvert/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/jupyter/nbconvert/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/jupyter/nbconvert/git/refs{/sha}","trees_url":"https://api.github.com/repos/jupyter/nbconvert/git/trees{/sha}","statuses_url":"https://api.github.com/repos/jupyter/nbconvert/statuses/{sha}","languages_url":"https://api.github.com/repos/jupyter/nbconvert/languages","stargazers_url":"https://api.github.com/repos/jupyter/nbconvert/stargazers","contributors_url":"https://api.github.com/repos/jupyter/nbconvert/contributors","subscribers_url":"https://api.github.com/repos/jupyter/nbconvert/subscribers","subscription_url":"https://api.github.com/repos/jupyter/nbconvert/subscription","commits_url":"https://api.github.com/repos/jupyter/nbconvert/commits{/sha}","git_commits_url":"https://api.github.com/repos/jupyter/nbconvert/git/commits{/sha}","comments_url":"https://api.github.com/repos/jupyter/nbconvert/comments{/number}","issue_comment_url":"https://api.github.com/repos/jupyter/nbconvert/issues/comments{/number}","contents_url":"https://api.github.com/repos/jupyter/nbconvert/contents/{+path}","compare_url":"https://api.github.com/repos/jupyter/nbconvert/compare/{base}...{head}","merges_url":"https://api.github.com/repos/jupyter/nbconvert/merges","archive_url":"https://api.github.com/repos/jupyter/nbconvert/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/jupyter/nbconvert/downloads","issues_url":"https://api.github.com/repos/jupyter/nbconvert/issues{/number}","pulls_url":"https://api.github.com/repos/jupyter/nbconvert/pulls{/number}","milestones_url":"https://api.github.com/repos/jupyter/nbconvert/milestones{/number}","notifications_url":"https://api.github.com/repos/jupyter/nbconvert/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/jupyter/nbconvert/labels{/name}","releases_url":"https://api.github.com/repos/jupyter/nbconvert/releases{/id}","deployments_url":"https://api.github.com/repos/jupyter/nbconvert/deployments","created_at":"2015-04-09T06:58:23Z","updated_at":"2016-02-02T18:00:01Z","pushed_at":"2016-02-02T18:18:58Z","git_url":"git://github.com/jupyter/nbconvert.git","ssh_url":"git@github.com:jupyter/nbconvert.git","clone_url":"https://github.com/jupyter/nbconvert.git","svn_url":"https://github.com/jupyter/nbconvert","homepage":"http://nbconvert.readthedocs.org/","size":3562,"stargazers_count":51,"watchers_count":51,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":62,"mirror_url":null,"open_issues_count":54,"forks":62,"open_issues":54,"watchers":51,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/jupyter/nbconvert/pulls/225"},"html":{"href":"https://github.com/jupyter/nbconvert/pull/225"},"issue":{"href":"https://api.github.com/repos/jupyter/nbconvert/issues/225"},"comments":{"href":"https://api.github.com/repos/jupyter/nbconvert/issues/225/comments"},"review_comments":{"href":"https://api.github.com/repos/jupyter/nbconvert/pulls/225/comments"},"review_comment":{"href":"https://api.github.com/repos/jupyter/nbconvert/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/jupyter/nbconvert/pulls/225/commits"},"statuses":{"href":"https://api.github.com/repos/jupyter/nbconvert/statuses/6be45f6cd2d2022a32d1ea40c09bbbad86061db1"}}}}