From: Vladimír Vondruš Date: Sun, 26 Nov 2017 01:56:01 +0000 (+0100) Subject: Support for colored terminal output highlighting. X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~cjwatson/git?a=commitdiff_plain;h=8ea9e525ef6c66ade23a8a89ca90377611300032;p=blog.git Support for colored terminal output highlighting. --- diff --git a/css/m-components.css b/css/m-components.css index 54a6267f..fbe7689c 100644 --- a/css/m-components.css +++ b/css/m-components.css @@ -87,6 +87,9 @@ pre { overflow-x: auto; margin-top: 0; /* stupid defaults */ } +pre.m-console { + background-color: var(--console-background-color); +} /* Inline elements */ abbr { @@ -120,6 +123,9 @@ code { color: var(--code-color); background-color: var(--code-background-color); } +code.m-console { + background-color: var(--console-background-color); +} /* Header navigation */ body > header > nav { @@ -1057,14 +1063,14 @@ figure.m-figure.m-fullwidth:after { } /* Code figure */ -figure.m-code-figure { +figure.m-code-figure, figure.m-console-figure { margin-top: 0; margin-left: 0; margin-right: 0; position: relative; padding: 1rem; } -figure.m-code-figure:before { +figure.m-code-figure:before, figure.m-console-figure:before { position: absolute; content: ' '; top: 0; @@ -1075,18 +1081,23 @@ figure.m-code-figure:before { border-style: solid; border-width: 0.125rem; border-radius: var(--border-radius); - border-color: var(--code-background-color); /* to match the
 background */
 }
-figure.m-code-figure.m-flat:before {
+figure.m-code-figure:before {
+  border-color: var(--code-background-color);
+}
+figure.m-console-figure:before {
+  border-color: var(--console-background-color);
+}
+figure.m-code-figure.m-flat:before, figure.m-console-figure.m-flat:before {
   border-color: transparent;
 }
-figure.m-code-figure > pre:first-child {
+figure.m-code-figure > pre:first-child, figure.m-console-figure > pre:first-child {
   position: relative; /* so it's above the border */
   margin: -1rem -1rem 1rem -1rem;
   border-bottom-left-radius: 0;
   border-bottom-right-radius: 0;
 }
-article section:target figure.m-code-figure {
+article section:target figure.m-code-figure, article section:target figure.m-console-figure {
   z-index: 1; /* so the selection border isn't above figure border */
 }
 
@@ -1181,12 +1192,14 @@ article section:target figure.m-code-figure {
 .m-container-inflatable > .m-row > [class*='m-col-'] > .m-imagegrid,
 .m-container-inflatable > .m-row > [class*='m-col-'] > pre,
 .m-container-inflatable > .m-row > [class*='m-col-'] > figure.m-code-figure,
+.m-container-inflatable > .m-row > [class*='m-col-'] > figure.m-console-figure,
 .m-container-inflatable > .m-row > [class*='m-col-'] section > .m-note,
 .m-container-inflatable > .m-row > [class*='m-col-'] section > .m-frame,
 .m-container-inflatable > .m-row > [class*='m-col-'] section > .m-block,
 .m-container-inflatable > .m-row > [class*='m-col-'] section > .m-imagegrid,
 .m-container-inflatable > .m-row > [class*='m-col-'] section > pre,
-.m-container-inflatable > .m-row > [class*='m-col-'] section > figure.m-code-figure {
+.m-container-inflatable > .m-row > [class*='m-col-'] section > figure.m-code-figure,
+.m-container-inflatable > .m-row > [class*='m-col-'] section > figure.m-console-figure {
   margin-left: -1rem;
   margin-right: -1rem;
 }
@@ -1219,11 +1232,13 @@ article section:target figure.m-code-figure {
 .m-container-inflatable section:target > .m-block,
 .m-container-inflatable section:target > pre,
 .m-container-inflatable section:target > figure.m-code-figure > pre:first-child,
+.m-container-inflatable section:target > figure.m-console-figure > pre:first-child,
 .m-container-inflatable section:target section > .m-note,
 .m-container-inflatable section:target section > .m-frame,
 .m-container-inflatable section:target section > .m-block,
 .m-container-inflatable section:target section > pre,
-.m-container-inflatable section:target section > figure.m-code-figure > pre:first-child {
+.m-container-inflatable section:target section > figure.m-code-figure > pre:first-child,
+.m-container-inflatable section:target section > figure.m-console-figure > pre:first-child {
   margin-left: -1.0rem;
   border-left-style: solid;
   border-left-width: 0.25rem;
@@ -1232,20 +1247,26 @@ article section:target figure.m-code-figure {
   padding-left: 0.75rem;
 }
 .m-container-inflatable section:target > figure.m-code-figure::before,
-.m-container-inflatable section:target section > figure.m-code-figure::before {
+.m-container-inflatable section:target > figure.m-console-figure::before,
+.m-container-inflatable section:target section > figure.m-code-figure::before,
+.m-container-inflatable section:target section > figure.m-console-figure::before {
   border-top-left-radius: 0;
   border-bottom-left-radius: 0;
   border-left-width: 0.25rem;
 }
 .m-container-inflatable section:target > pre,
 .m-container-inflatable section:target > figure.m-code-figure > pre:first-child,
+.m-container-inflatable section:target > figure.m-console-figure > pre:first-child,
 .m-container-inflatable section:target section > pre,
-.m-container-inflatable section:target section > figure.m-code-figure > pre:first-child {
+.m-container-inflatable section:target section > figure.m-code-figure > pre:first-child,
+.m-container-inflatable section:target section > figure.m-console-figure > pre:first-child {
   border-color: var(--line-color);
 }
 .m-container-inflatable section:target > figure.m-code-figure::before,
-.m-container-inflatable section:target section > figure.m-code-figure::before,
+.m-container-inflatable section:target > figure.m-console-figure::before,
 .m-container-inflatable section:target > .m-note.m-default,
+.m-container-inflatable section:target section > figure.m-code-figure::before,
+.m-container-inflatable section:target section > figure.m-console-figure::before,
 .m-container-inflatable section:target section > .m-note.m-default {
   border-left-color: var(--line-color);
 }
@@ -1310,16 +1331,16 @@ div.m-math.m-info svg, svg.m-math.m-info { fill: var(--info-color); }
 div.m-math.m-dim svg, svg.m-math.m-dim { fill: var(--dim-color); }
 
 /* Spacing after every block element, but not after the last */
-p, ul, ol, dl, blockquote, hr, pre, figure.m-code-figure,
-article, article > header, article section,
+p, ul, ol, dl, blockquote, pre, figure.m-code-figure, figure.m-console-figure,
+hr, article, article > header, article section,
 .m-note, .m-frame, .m-block, .m-button,
 div.m-scroll, table.m-table, div.m-image, img.m-image,
 figure.m-figure, .m-imagegrid, div.m-math {
   margin-bottom: 1rem;
 }
 p:last-child, ul:last-child, ol:last-child, dl:last-child, blockquote:last-child,
-hr:last-child, pre:last-child, figure.m-code-figure:last-child,
-article:last-child, article section:last-child,
+pre:last-child, figure.m-code-figure:last-child, figure.m-console-figure:last-child,
+hr:last-child, article:last-child, article section:last-child,
 .m-note:last-child, .m-frame:last-child, .m-block:last-child, .m-button:last-child,
 table.m-table:last-child, img.m-image:last-child, div.m-image:last-child,
 figure.m-figure:last-child, .m-imagegrid:last-child, div.m-math:last-child {
diff --git a/css/m-dark.css b/css/m-dark.css
index 1711056d..b7309e96 100644
--- a/css/m-dark.css
+++ b/css/m-dark.css
@@ -25,6 +25,7 @@
 @import url('m-grid.css');
 @import url('m-components.css');
 @import url('pygments-dark.css');
+@import url('pygments-console.css');
 
 :root {
   /* Text properties */
@@ -56,6 +57,7 @@
   --code-color: #e6e6e6;
   --code-inverted-color: rgba(230, 230, 230, 0.33);
   --code-background-color: #22272e;
+  --console-background-color: #000000;
 
   /* Header */
   --header-border-width: 0 0 0.25rem 0;
diff --git a/css/m-light.css b/css/m-light.css
index 1d96657c..eb388f8f 100644
--- a/css/m-light.css
+++ b/css/m-light.css
@@ -24,6 +24,8 @@
 
 @import url('m-grid.css');
 @import url('m-components.css');
+/* TODO: pygments-light.css */
+@import url('pygments-console.css');
 
 :root {
   /* Text properties */
@@ -53,6 +55,7 @@
   --code-color: #5b5b5b;
   --code-inverted-color: rgba(91, 91, 91, 0.33);
   --code-background-color: #fbf0ec;
+  --console-background-color: #000000;
 
   /* Header */
   --header-border-width: 0.25rem 0 0 0;
diff --git a/css/pygments-console.css b/css/pygments-console.css
new file mode 100644
index 00000000..06895f1e
--- /dev/null
+++ b/css/pygments-console.css
@@ -0,0 +1,22 @@
+.m-console .hll { background-color: #ffffcc }
+.m-console .g-AnsiBlack { color: #000000 } /* Generic.AnsiBlack */
+.m-console .g-AnsiBlue { color: #3f3fd1 } /* Generic.AnsiBlue */
+.m-console .g-AnsiBrightBlack { color: #686868; font-weight: bold } /* Generic.AnsiBrightBlack */
+.m-console .g-AnsiBrightBlue { color: #5454ff; font-weight: bold } /* Generic.AnsiBrightBlue */
+.m-console .g-AnsiBrightCyan { color: #54ffff; font-weight: bold } /* Generic.AnsiBrightCyan */
+.m-console .g-AnsiBrightDefault { color: #ffffff; font-weight: bold } /* Generic.AnsiBrightDefault */
+.m-console .g-AnsiBrightGreen { color: #54ff54; font-weight: bold } /* Generic.AnsiBrightGreen */
+.m-console .g-AnsiBrightMagenta { color: #ff54ff; font-weight: bold } /* Generic.AnsiBrightMagenta */
+.m-console .g-AnsiBrightRed { color: #ff5454; font-weight: bold } /* Generic.AnsiBrightRed */
+.m-console .g-AnsiBrightWhite { color: #ffffff; font-weight: bold } /* Generic.AnsiBrightWhite */
+.m-console .g-AnsiBrightYellow { color: #ffff54; font-weight: bold } /* Generic.AnsiBrightYellow */
+.m-console .g-AnsiCyan { color: #18b2b2 } /* Generic.AnsiCyan */
+.m-console .g-AnsiDefault { color: #b2b2b2 } /* Generic.AnsiDefault */
+.m-console .g-AnsiGreen { color: #18b218 } /* Generic.AnsiGreen */
+.m-console .g-AnsiMagenta { color: #b218b2 } /* Generic.AnsiMagenta */
+.m-console .g-AnsiRed { color: #b21818 } /* Generic.AnsiRed */
+.m-console .g-AnsiWhite { color: #b2b2b2 } /* Generic.AnsiWhite */
+.m-console .g-AnsiYellow { color: #b26818 } /* Generic.AnsiYellow */
+.m-console .go { color: #b2b2b2 } /* Generic.Output */
+.m-console .gp { color: #54ffff; font-weight: bold } /* Generic.Prompt */
+.m-console .w { color: #b2b2b2 } /* Text.Whitespace */
diff --git a/css/pygments-console.py b/css/pygments-console.py
new file mode 100644
index 00000000..5f4e8355
--- /dev/null
+++ b/css/pygments-console.py
@@ -0,0 +1,59 @@
+#
+#   This file is part of m.css.
+#
+#   Copyright © 2017 Vladimír Vondruš 
+#
+#   Permission is hereby granted, free of charge, to any person obtaining a
+#   copy of this software and associated documentation files (the "Software"),
+#   to deal in the Software without restriction, including without limitation
+#   the rights to use, copy, modify, merge, publish, distribute, sublicense,
+#   and/or sell copies of the Software, and to permit persons to whom the
+#   Software is furnished to do so, subject to the following conditions:
+#
+#   The above copyright notice and this permission notice shall be included
+#   in all copies or substantial portions of the Software.
+#
+#   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+#   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+#   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+#   THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+#   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+#   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+#   DEALINGS IN THE SOFTWARE.
+#
+
+from pygments.style import Style
+from pygments.token import Text, Generic
+
+class ConsoleStyle(Style):
+
+    background_color = None
+    default_style = ''
+
+    styles = {
+        Text:                       '#b2b2b2',
+
+        # Shell session
+        Generic.Prompt:             'bold #54ffff',
+        Generic.Output:             '#b2b2b2',
+
+        # ANSI highlighting
+        Generic.AnsiBlack:          '#000000',
+        Generic.AnsiRed:            '#b21818',
+        Generic.AnsiGreen:          '#18b218',
+        Generic.AnsiYellow:         '#b26818',
+        Generic.AnsiBlue:           '#3f3fd1',
+        Generic.AnsiMagenta:        '#b218b2',
+        Generic.AnsiCyan:           '#18b2b2',
+        Generic.AnsiWhite:          '#b2b2b2',
+        Generic.AnsiDefault:        '#b2b2b2',
+        Generic.AnsiBrightBlack:    'bold #686868',
+        Generic.AnsiBrightRed:      'bold #ff5454',
+        Generic.AnsiBrightGreen:    'bold #54ff54',
+        Generic.AnsiBrightYellow:   'bold #ffff54',
+        Generic.AnsiBrightBlue:     'bold #5454ff',
+        Generic.AnsiBrightMagenta:  'bold #ff54ff',
+        Generic.AnsiBrightCyan:     'bold #54ffff',
+        Generic.AnsiBrightWhite:    'bold #ffffff',
+        Generic.AnsiBrightDefault:  'bold #ffffff'
+    }
diff --git a/doc/css.rst b/doc/css.rst
index 674e68d4..a2264fac 100644
--- a/doc/css.rst
+++ b/doc/css.rst
@@ -49,12 +49,14 @@ CSS. Download them below or :gh:`grab the whole Git repository `:
 -   :gh:`m-dark.css ` or
     :gh:`m-light.css `
 
-In addition to the above, if you want to present highlighted code snippets on
-your website, there's also a builtin style for `Pygments `_,
-matching m.css themes:
+In addition to the above, if you want to present highlighted code snippets (or
+colored terminal output) on your website, there's also a builtin style for
+`Pygments `_, matching m.css themes:
 
 -   :gh:`pygments-dark.css `,
     generated from :gh:`pygments-dark.py `
+-   :gh:`pygments-console.css `,
+    generated from :gh:`pygments-dark.py `
 
 Once you have the files, include them in your HTML markup. The top-level
 ``m-dark.css`` / ``m-light.css`` file includes the other via CSS :css:`@import`
@@ -87,7 +89,7 @@ to include a proper :html:`` tag. The HTML5 DOCTYPE is also required.
     :css:`@import` statements. Because of that, the builtin themes provide
     a ``*.compiled.css`` versions that are *post*\ processed without CSS
     variables or :css:`@import` statements. The compiled version includes also
-    the Pygments style, all combined in one file:
+    the code and console Pygments style, all combined in one file:
 
     -   :gh:`m-dark.compiled.css `
         (:filesize:`{filename}/../css/m-dark.compiled.css`,
diff --git a/doc/css/components-test.rst b/doc/css/components-test.rst
index c6ce4206..1c19be0d 100644
--- a/doc/css/components-test.rst
+++ b/doc/css/components-test.rst
@@ -620,6 +620,8 @@ Without link or caption:
 `Code figure`_
 ==============
 
+.. don't remove the header link, needed for testing!
+
 A flat code figure:
 
 .. raw:: html
@@ -642,3 +644,14 @@ should not affect it.
     snippet
And a resulting output.
+ +Console figure: + +.. raw:: html + +
+
Some
+        console
+    output
+ And a description of that illegal crackery that's done above. +
diff --git a/doc/css/components.rst b/doc/css/components.rst index 868dd15d..97e030e9 100644 --- a/doc/css/components.rst +++ b/doc/css/components.rst @@ -815,10 +815,45 @@ instead of :html:`
`:
 .. note-success::
 
     To make your life easier, m.css provides a
-    `Pelican plugin <{filename}/plugins/math-and-code.rst#code>`__
-    that integrates Pygments code highlighting as a :abbr:`reST `
+    `Pelican plugin <{filename}/plugins/math-and-code.rst#code>`__ that
+    integrates Pygments code highlighting as a :abbr:`reST `
     directive.
 
+`Colored terminal output`_
+==========================
+
+Besides code, it's also possible to "highlight" ANSI-colored terminal output.
+For that, m.css provides a custom Pygments lexer that's together with
+`pygments-console.css <{filename}/css.rst>`_ able to detect and highlight the
+basic 4-bit color codes (8 foreground colors in either normal or bright
+version). Download the :gh:`ansilexer.py `
+file or use it directly from your Git clone of m.css. Example usage:
+
+.. code:: sh
+
+    ls -C --color=always | pygmentize -l pelican-plugins/m/ansilexer.py:AnsiLexer -x -f html -O nowrap
+
+Wrap the HTML output in either :html:`
` for a block
+listing or :html:`` for inline listing. The output
+might then look similarly to this:
+
+.. code-figure::
+
+    .. code:: html
+
+        
CONTRIBUTING.rst  CREDITS.rst  doc      pelican-plugins  README.rst
+        COPYING           css          doxygen  pelican-theme    site
+ + .. raw:: html + +
CONTRIBUTING.rst  CREDITS.rst  doc      pelican-plugins  README.rst
+        COPYING           css          doxygen  pelican-theme    site
+ +.. note-success:: + + The Pelican plugin mentioned above is able to do + `colored console highlighting as well <{filename}/plugins/math-and-code.rst#colored-terminal-output>`_. + `Code figure`_ ============== @@ -853,6 +888,10 @@ bit of a figure inception shown here): And a resulting output. +It's also possible to have matching border for a console output. Just use +:css:`.m-console-figure` instead of :css:`.m-code-figure` on the :html:`
` +element. + `Math`_ ======= diff --git a/doc/css/themes.rst b/doc/css/themes.rst index b3a89d15..f1f22187 100644 --- a/doc/css/themes.rst +++ b/doc/css/themes.rst @@ -114,6 +114,7 @@ If you want to modify the Pygments style, it's a bit more involved. You need to edit the ``*.py`` file instead of the ``*.css``: - :gh:`pygments-dark.py ` +- :gh:`pygments-console.py ` After making changes, copy it somewhere so Pygments can load it as a style and then generate a CSS file out of it: @@ -123,6 +124,9 @@ then generate a CSS file out of it: sudo cp pygments-dark.py /usr/lib/python3.6/site-packages/pygments/styles/dark.py pygmentize -f html -S dark -a .m-code > pygments-dark.css + sudo cp pygments-console.py /usr/lib/python3.6/site-packages/pygments/styles/console.py + pygmentize -f html -S console -a .m-console > pygments-console.css + .. note-success:: Made a theme and want to share it with the world? I'm happy to diff --git a/doc/css/typography.rst b/doc/css/typography.rst index a1b1242d..2fcd42ab 100644 --- a/doc/css/typography.rst +++ b/doc/css/typography.rst @@ -293,7 +293,7 @@ will have negative margin to make its contents aligned with surrounding text. .. note-info:: The Components page has additional information about - `code blocks styling <{filename}/css/components.rst#code-blocks>`_. + `code block styling <{filename}/css/components.rst#code>`_. `Inline elements`_ ================== diff --git a/doc/plugins/components-test.rst b/doc/plugins/components-test.rst index 6029ef7e..7fc3420f 100644 --- a/doc/plugins/components-test.rst +++ b/doc/plugins/components-test.rst @@ -187,3 +187,12 @@ Flat code figure: snippet And a resulting output. + +Console figure: + +.. console-figure:: + + .. include:: math-and-code-console.ansi + :code: ansi + + And a description of that illegal crackery that's done above. diff --git a/doc/plugins/components.rst b/doc/plugins/components.rst index 7252ce42..839846ec 100644 --- a/doc/plugins/components.rst +++ b/doc/plugins/components.rst @@ -174,6 +174,9 @@ additional CSS classes. And a resulting output. +Use :rst:`.. console-figure::` to denote code figure styled for a +`console listing <{filename}/css/components.rst#colored-terminal-output>`_. + `Text`_ ======= diff --git a/doc/plugins/math-and-code-console.ansi b/doc/plugins/math-and-code-console.ansi new file mode 100644 index 00000000..43eca24b --- /dev/null +++ b/doc/plugins/math-and-code-console.ansi @@ -0,0 +1,3 @@ +![mosra@don-perverzo m.css]$ ls +CONTRIBUTING.rst CREDITS.rst doc pelican-plugins README.rst +COPYING css doxygen pelican-theme site diff --git a/doc/plugins/math-and-code.rst b/doc/plugins/math-and-code.rst index 7f7eca07..63bc65c2 100644 --- a/doc/plugins/math-and-code.rst +++ b/doc/plugins/math-and-code.rst @@ -234,3 +234,24 @@ specify which language should be highlighted, derive a custom role from it: With the :cmake:`add_executable(foo bar.cpp)` CMake command you can create an executable from a file that contains just :cpp:`int main() { return 666; }` and nothing else. + +`Colored terminal output`_ +-------------------------- + +Use the ``ansi`` pseudo-language for highlighting +`colored terminal output <{filename}/css/components.rst#colored-terminal-output>`_. +The plugin will take care of the rest like using the custom Pygments lexer and +assigning a proper CSS class. Because ANSI escape codes might cause problems +with some editors and look confusing when viewed via :sh:`git diff` on the +terminal, it's best to have the listings in external files and use +:rst:`.. include::`: + +.. code-figure:: + + .. code:: rst + + .. include:: console.ansi + :code: ansi + + .. include:: math-and-code-console.ansi + :code: ansi diff --git a/pelican-plugins/m/ansilexer.py b/pelican-plugins/m/ansilexer.py new file mode 100644 index 00000000..3781fbf2 --- /dev/null +++ b/pelican-plugins/m/ansilexer.py @@ -0,0 +1,72 @@ +# +# This file is part of m.css. +# +# Copyright © 2017 Vladimír Vondruš +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. +# + +from pygments.lexer import RegexLexer +from pygments.token import * + +class AnsiLexer(RegexLexer): + name = 'Ansi escape lexer' + + def callback(lexer, match): + bright = match.group(1) + color = match.group(2) + text = match.group(3) + + if int(bright) == 0: + token = 'Generic.Ansi' + elif int(bright) == 1: + token = 'Generic.AnsiBright' + else: + yield (match.start(), Text, text) + return + + if color == ';30': + token += 'Black' + elif color == ';31': + token += 'Red' + elif color == ';32': + token += 'Green' + elif color == ';33': + token += 'Yellow' + elif color == ';34': + token += 'Blue' + elif color == ';35': + token += 'Magenta' + elif color == ';36': + token += 'Cyan' + elif color == ';37': + token += 'White' + elif color == ';39': + token += 'Default' + else: + yield (match.start(), Text, text) + return + + yield (match.start(), string_to_tokentype(token), text) + + tokens = { + 'root': [ + ('[^\x1b]+', Text), + ('\x1b\\[(\\d+)(;\\d+)?m([^\x1b]*)', callback)] + } diff --git a/pelican-plugins/m/code.py b/pelican-plugins/m/code.py index ed8f3576..40b92be5 100644 --- a/pelican-plugins/m/code.py +++ b/pelican-plugins/m/code.py @@ -34,7 +34,34 @@ from docutils import io, nodes, utils, statemachine from pygments import highlight from pygments.formatters import HtmlFormatter -from pygments.lexers import TextLexer, get_lexer_by_name +from pygments.lexers import TextLexer, BashSessionLexer, get_lexer_by_name + +import logging + +logger = logging.getLogger(__name__) + +from . import ansilexer + +def _highlight(code, language, options): + # Use our own lexer for ANSI + if language == 'ansi': + lexer = ansilexer.AnsiLexer() + else: + try: + lexer = get_lexer_by_name(language) + except ValueError: + logger.warning("No lexer found for language '{}', code highlighting disabled".format(language)) + lexer = TextLexer() + + if (isinstance(lexer, BashSessionLexer) or + isinstance(lexer, ansilexer.AnsiLexer)): + class_ = 'm-console' + else: + class_ = 'm-code' + + formatter = HtmlFormatter(nowrap=True, **options) + parsed = highlight(code, lexer, formatter).strip() + return class_, parsed class Code(Directive): required_arguments = 1 @@ -48,21 +75,17 @@ class Code(Directive): def run(self): self.assert_has_content() - set_classes(self.options) - classes = ['m-code'] + set_classes(self.options) + classes = [] if 'classes' in self.options: classes += self.options['classes'] del self.options['classes'] - try: - lexer = get_lexer_by_name(self.arguments[0]) - except ValueError: - lexer = TextLexer() - formatter = HtmlFormatter(nowrap=True, **self.options) - parsed = highlight('\n'.join(self.content), lexer, formatter) + class_, highlighted = _highlight('\n'.join(self.content), self.arguments[0], self.options) + classes += [class_] - content = nodes.raw('', parsed, format='html') + content = nodes.raw('', highlighted, format='html') pre = nodes.literal_block('', classes=classes) pre.append(content) return [pre] @@ -151,22 +174,20 @@ class Include(docutils.parsers.rst.directives.misc.Include): def code(role, rawtext, text, lineno, inliner, options={}, content=[]): set_classes(options) - classes = ['m-code'] + classes = [] if 'classes' in options: classes += options['classes'] del options['classes'] # Not sure why language is duplicated in classes? language = options.get('language', '') + del options['language'] if language in classes: classes.remove(language) - try: - lexer = get_lexer_by_name(language) - except ValueError: - lexer = TextLexer() - formatter = HtmlFormatter(nowrap=True) - parsed = highlight(utils.unescape(text), lexer, formatter).strip() - - content = nodes.raw('', parsed, format='html') + + class_, highlighted = _highlight(utils.unescape(text), language, options) + classes += [class_] + + content = nodes.raw('', highlighted, format='html') node = nodes.literal(rawtext, '', classes=classes, **options) node.append(content) return [node], [] diff --git a/pelican-plugins/m/components.py b/pelican-plugins/m/components.py index ebbb823b..c8d0cb66 100644 --- a/pelican-plugins/m/components.py +++ b/pelican-plugins/m/components.py @@ -167,19 +167,22 @@ class CodeFigure(rst.Directive): has_content = True option_spec = {'class': directives.class_option} - style_class = '' + style_class = 'm-code-figure' def run(self): set_classes(self.options) text = '\n'.join(self.content) figure_node = nodes.figure(text, **self.options) - figure_node['classes'] += ['m-code-figure', self.style_class] + figure_node['classes'] += [self.style_class] self.state.nested_parse(self.content, self.content_offset, figure_node) return [figure_node] +class ConsoleFigure(CodeFigure): + style_class = 'm-console-figure' + class Text(rst.Directive): has_content = True option_spec = {'class': directives.class_option} @@ -308,6 +311,7 @@ def register(): rst.directives.register_directive('frame', Frame) rst.directives.register_directive('code-figure', CodeFigure) + rst.directives.register_directive('console-figure', ConsoleFigure) rst.directives.register_directive('text-default', DefaultText) rst.directives.register_directive('text-primary', PrimaryText) diff --git a/pelican-theme/static/pygments-console.css b/pelican-theme/static/pygments-console.css new file mode 120000 index 00000000..b0daadb4 --- /dev/null +++ b/pelican-theme/static/pygments-console.css @@ -0,0 +1 @@ +../../css/pygments-console.css \ No newline at end of file