chiark / gitweb /
Support for colored terminal output highlighting.
authorVladimír Vondruš <mosra@centrum.cz>
Sun, 26 Nov 2017 01:56:01 +0000 (02:56 +0100)
committerVladimír Vondruš <mosra@centrum.cz>
Thu, 30 Nov 2017 17:50:46 +0000 (18:50 +0100)
18 files changed:
css/m-components.css
css/m-dark.css
css/m-light.css
css/pygments-console.css [new file with mode: 0644]
css/pygments-console.py [new file with mode: 0644]
doc/css.rst
doc/css/components-test.rst
doc/css/components.rst
doc/css/themes.rst
doc/css/typography.rst
doc/plugins/components-test.rst
doc/plugins/components.rst
doc/plugins/math-and-code-console.ansi [new file with mode: 0644]
doc/plugins/math-and-code.rst
pelican-plugins/m/ansilexer.py [new file with mode: 0644]
pelican-plugins/m/code.py
pelican-plugins/m/components.py
pelican-theme/static/pygments-console.css [new symlink]

index 54a6267f14f17d02878f913ee5b7db7d90804885..fbe7689cfd1a0b93eda721ceb613525a6fd4088e 100644 (file)
@@ -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 <pre> 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 {
index 1711056d8f8a0959e32062577edb9e3d2a796fbe..b7309e96f40944fefdb7d9af5e1d5f174baea18d 100644 (file)
@@ -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;
index 1d96657cd0f1d94b543a2c696a9f600608849b04..eb388f8f8687748cb02d79facc239a70fb7c874a 100644 (file)
@@ -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 (file)
index 0000000..06895f1
--- /dev/null
@@ -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 (file)
index 0000000..5f4e835
--- /dev/null
@@ -0,0 +1,59 @@
+#
+#   This file is part of m.css.
+#
+#   Copyright © 2017 Vladimír Vondruš <mosra@centrum.cz>
+#
+#   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'
+    }
index 674e68d4b0ad9ec986ca2c05ed4b2a0992686773..a2264faca4083d9b7de52401b71458b9f412045b 100644 (file)
@@ -49,12 +49,14 @@ CSS. Download them below or :gh:`grab the whole Git repository <mosra/m.css>`:
 -   :gh:`m-dark.css <mosra/m.css$master/css/m-dark.css>` or
     :gh:`m-light.css <mosra/m.css$master/css/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 <http://pygments.org/>`_,
-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 <http://pygments.org/>`_, matching m.css themes:
 
 -   :gh:`pygments-dark.css <mosra/m.css$master/css/pygments-dark.css>`,
     generated from :gh:`pygments-dark.py <mosra/m.css$master/css/pygments-dark.py>`
+-   :gh:`pygments-console.css <mosra/m.css$master/css/pygments-console.css>`,
+    generated from :gh:`pygments-dark.py <mosra/m.css$master/css/pygments-console.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:`<meta>` 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 <mosra/m.css$master/css/m-dark.compiled.css>`
         (:filesize:`{filename}/../css/m-dark.compiled.css`,
index c6ce420626897f195162e60f4acc87ed0434f09a..1c19be0d03af7269a60ce964b2a5ad88c3648740 100644 (file)
@@ -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</pre>
         <pre>And a resulting output.</pre>
     </figure>
+
+Console figure:
+
+.. raw:: html
+
+    <figure class="m-console-figure">
+        <pre class="m-console">Some
+        console
+    output</pre>
+        And a description of that illegal crackery that's done above.
+    </figure>
index 868dd15de196647ace13149685637550c606d6f5..97e030e98cca55cc306fd83d52d6a609e9e082a6 100644 (file)
@@ -815,10 +815,45 @@ instead of :html:`<pre>`:
 .. 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 <reStructuredText>`
+    `Pelican plugin <{filename}/plugins/math-and-code.rst#code>`__ that
+    integrates Pygments code highlighting as a :abbr:`reST <reStructuredText>`
     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 <mosra/m.css$master/pelican-plugins/m/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:`<pre class="m-console">` for a block
+listing or :html:`<code class="m-console">` for inline listing. The output
+might then look similarly to this:
+
+.. code-figure::
+
+    .. code:: html
+
+        <pre class="m-console">CONTRIBUTING.rst  CREDITS.rst  <span class="g g-AnsiBrightBlue">doc</span>      <span class="g g-AnsiBrightBlue">pelican-plugins</span>  README.rst
+        COPYING           <span class="g g-AnsiBrightBlue">css</span>          <span class="g g-AnsiBrightBlue">doxygen</span>  <span class="g g-AnsiBrightBlue">pelican-theme</span>    <span class="g g-AnsiBrightBlue">site</span></pre>
+
+    .. raw:: html
+
+        <pre class="m-console">CONTRIBUTING.rst  CREDITS.rst  <span class="g g-AnsiBrightBlue">doc</span>      <span class="g g-AnsiBrightBlue">pelican-plugins</span>  README.rst
+        COPYING           <span class="g g-AnsiBrightBlue">css</span>          <span class="g g-AnsiBrightBlue">doxygen</span>  <span class="g g-AnsiBrightBlue">pelican-theme</span>    <span class="g g-AnsiBrightBlue">site</span></pre>
+
+.. 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.
         </figure>
 
+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:`<figure>`
+element.
+
 `Math`_
 =======
 
index b3a89d1589b83c5cf97d1c768e7ee0b6ca93a074..f1f2218762d79077580ff2e971b3b218b74b7d0b 100644 (file)
@@ -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 <mosra/m.css$master/css/pygments-dark.py>`
+-   :gh:`pygments-console.py <mosra/m.css$master/css/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
index a1b1242ddf99118adee84cdd8f17167c8b7cb94a..2fcd42abd5465822807b0116202bc29abc722d75 100644 (file)
@@ -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`_
 ==================
index 6029ef7e9fa35d9528cb87dc58e42d7cf9924b11..7fc3420f3d6cba5a56befa45441fe2878f355bea 100644 (file)
@@ -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.
index 7252ce429fea7199b9801f870b2df425337aa531..839846ec7a499f0a581abf8322f74f8775ebb3de 100644 (file)
@@ -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 (file)
index 0000000..43eca24
--- /dev/null
@@ -0,0 +1,3 @@
+!\e[0;34m[\e[1;37mmosra@don-perverzo \e[0;37mm.css\e[0;34m]\e[1;36m$ \e[0mls
+CONTRIBUTING.rst  CREDITS.rst  \e[0m\e[01;34mdoc\e[0m      \e[01;34mpelican-plugins\e[0m  README.rst
+COPYING           \e[01;34mcss\e[0m          \e[01;34mdoxygen\e[0m  \e[01;34mpelican-theme\e[0m    \e[01;34msite\e[0m
index 7f7eca07258572f16143bb0e4048adf8b7f8a07c..63bc65c227ea5ac07e112235b0589c31335d857e 100644 (file)
@@ -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 (file)
index 0000000..3781fbf
--- /dev/null
@@ -0,0 +1,72 @@
+#
+#   This file is part of m.css.
+#
+#   Copyright © 2017 Vladimír Vondruš <mosra@centrum.cz>
+#
+#   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)]
+    }
index ed8f35762451115dd273a46a1066d0a04e3c20be..40b92be50036f453d2e98d61ae760afd3dfcbeaf 100644 (file)
@@ -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], []
index ebbb823bfa8e6f39ff64177a030aa66fdd11f2be..c8d0cb66ca5b4f7f640ecba602177d3321dd510c 100644 (file)
@@ -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 (symlink)
index 0000000..b0daadb
--- /dev/null
@@ -0,0 +1 @@
+../../css/pygments-console.css
\ No newline at end of file