packages feed

mmark-ext (empty) → 0.0.1.0

raw patch · 10 files changed

+815/−0 lines, 10 filesdep +basedep +data-default-classdep +foldlsetup-changed

Dependencies added: base, data-default-class, foldl, hspec, lucid, mmark, mmark-ext, modern-uri, semigroups, text

Files

+ CHANGELOG.md view
@@ -0,0 +1,3 @@+## MMark Ext 0.0.1.0++* Initial release.
+ LICENSE.md view
@@ -0,0 +1,28 @@+Copyright © 2017 Mark Karpov++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++* Redistributions of source code must retain the above copyright notice,+  this list of conditions and the following disclaimer.++* Redistributions in binary form must reproduce the above copyright+  notice, this list of conditions and the following disclaimer in the+  documentation and/or other materials provided with the distribution.++* Neither the name Mark Karpov nor the names of contributors may be used to+  endorse or promote products derived from this software without specific+  prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS “AS IS” AND ANY EXPRESS+OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN+NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT,+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,24 @@+# MMark Extensions++[![License BSD3](https://img.shields.io/badge/license-BSD3-brightgreen.svg)](http://opensource.org/licenses/BSD-3-Clause)+[![Hackage](https://img.shields.io/hackage/v/mmark-ext.svg?style=flat)](https://hackage.haskell.org/package/mmark-ext)+[![Stackage Nightly](http://stackage.org/package/mmark-ext/badge/nightly)](http://stackage.org/nightly/package/mmark-ext)+[![Stackage LTS](http://stackage.org/package/mmark-ext/badge/lts)](http://stackage.org/lts/package/mmark-ext)+[![Build Status](https://travis-ci.org/mrkkrp/mmark-ext.svg?branch=master)](https://travis-ci.org/mrkkrp/mmark-ext)+[![Coverage Status](https://coveralls.io/repos/mrkkrp/mmark-ext/badge.svg?branch=master&service=github)](https://coveralls.io/github/mrkkrp/mmark-ext?branch=master)++Commonly useful extensions for+[MMark](https://hackage.haskell.org/package/mmark) markdown processor.++## Contribution++Issues, bugs, and questions may be reported in [the GitHub issue tracker for+this project](https://github.com/mrkkrp/mmark-ext/issues).++Pull requests are also welcome and will be reviewed quickly.++## License++Copyright © 2017 Mark Karpov++Distributed under BSD 3 clause license.
+ Setup.hs view
@@ -0,0 +1,6 @@+module Main (main) where++import Distribution.Simple++main :: IO ()+main = defaultMain
+ Text/MMark/Extension/Common.hs view
@@ -0,0 +1,268 @@+-- |+-- Module      :  Text.MMark.Extension.Common+-- Copyright   :  © 2017 Mark Karpov+-- License     :  BSD 3 clause+--+-- Maintainer  :  Mark Karpov <markkarpov92@gmail.com>+-- Stability   :  experimental+-- Portability :  portable+--+-- Commonly useful extensions for MMark markdown processor.+--+-- We suggest using a qualified import, like this:+--+-- > import qualified Text.MMark.Extension.Common as Ext+--+-- Here is an example that uses several extensions from this module at the+-- same time, it should give you an idea where to start:+--+-- > {-# LANGUAGE OverloadedStrings #-}+-- >+-- > module Main (main) where+-- >+-- > import Data.Default.Class+-- > import qualified Data.Text.IO                as T+-- > import qualified Data.Text.Lazy.IO           as TL+-- > import qualified Lucid                       as L+-- > import qualified Text.MMark                  as MMark+-- > import qualified Text.MMark.Extension.Common as Ext+-- >+-- > main :: IO ()+-- > main = do+-- >   let input = "input.md"+-- >   txt <- T.readFile input+-- >   case MMark.parse input txt of+-- >     Left errs -> putStrLn (MMark.parseErrorsPretty txt errs)+-- >     Right r ->+-- >       let toc = MMark.runScanner r (Ext.tocScanner 4)+-- >       in TL.writeFile "output.html"+-- >           . L.renderText+-- >           . MMark.render+-- >           . MMark.useExtensions+-- >               [ Ext.toc "toc" toc+-- >               , Ext.punctuationPrettifier def+-- >               , Ext.obfuscateEmail "protected-email"+-- >               , Ext.fontAwesome ]+-- >           $ r++{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE DeriveGeneric      #-}+{-# LANGUAGE LambdaCase         #-}+{-# LANGUAGE OverloadedStrings  #-}+{-# LANGUAGE RecordWildCards    #-}++module Text.MMark.Extension.Common+  ( -- * Table of contents+    -- $table-of-contents+    Toc+  , tocScanner+  , toc+    -- * Punctuation prettifier+  , Punctuation (..)+  , punctuationPrettifier+    -- * Email address obfuscation+  , obfuscateEmail+    -- * Font Awesome icons+  , fontAwesome )+where++import Data.Data (Data)+import Data.Default.Class+import Data.List.NonEmpty (NonEmpty (..))+import Data.Maybe (maybeToList, fromJust)+import Data.Monoid ((<>))+import Data.Text (Text)+import Data.Typeable (Typeable)+import GHC.Generics+import Lucid+import Text.MMark+import Text.MMark.Extension (Bni, Block (..), Inline (..))+import qualified Control.Foldl        as L+import qualified Data.List.NonEmpty   as NE+import qualified Data.Text            as T+import qualified Text.MMark.Extension as Ext+import qualified Text.URI             as URI++----------------------------------------------------------------------------+-- Table of contents++-- $table-of-contents+--+-- Place this markup in markdown document where you want table of contents+-- to be inserted:+--+-- > ```toc+-- > ```+--+-- You may use something different than @\"toc\"@ as the info string of the+-- code block.++-- | An opaque type representing table of contents produced by the+-- 'tocScanner' scanner.++newtype Toc = Toc [(Int, NonEmpty Inline)]++-- | The scanner builds table of contents 'Toc' that can then be passed to+-- 'toc' to obtain an extension that renders the table of contents in HTML.+--+-- __Note__: Top level header (level 1) is never added to the table of+-- contents. Open an issue if you think it's not a good behavior.++tocScanner+  :: Int -- ^ Up to which level (inclusive) to collect headers? Values from+         -- 2 to 6 make sense here.+  -> L.Fold Bni Toc+tocScanner cutoff = fmap (Toc . reverse) . Ext.scanner [] $ \xs block ->+  case block of+    Heading2 x -> f 2 x xs+    Heading3 x -> f 3 x xs+    Heading4 x -> f 4 x xs+    Heading5 x -> f 5 x xs+    Heading6 x -> f 6 x xs+    _          -> xs+  where+    f n a as =+      if n > cutoff+        then as+        else (n, a) : as++-- | Create an extension that replaces a certain code block with previously+-- constructed table of contents.++toc+  :: Text -- ^ Label of the code block to replace by the table of contents+  -> Toc  -- ^ Previously generated by 'tocScanner'+  -> Extension+toc label (Toc xs) = Ext.blockTrans $ \case+  old@(CodeBlock mlabel _) ->+    case NE.nonEmpty xs of+      Nothing -> old+      Just ns ->+        if mlabel == pure label+          then renderToc ns+          else old+  other -> other++-- | Construct 'Bni' for a table of contents from given collection of+-- headers. This is a non-public helper.++renderToc :: NonEmpty (Int, NonEmpty Inline) -> Bni+renderToc = UnorderedList . NE.unfoldr f+  where+    f ((n,x) :| xs) =+      let (sitems, fitems) = span ((> n) . fst) xs+          url = Ext.headerFragment (Ext.headerId x)+      in ( Naked (Link x url Nothing :| [])+           : maybeToList (renderToc <$> NE.nonEmpty sitems)+         , NE.nonEmpty fitems )++----------------------------------------------------------------------------+-- Punctuation prettifier++-- | Settings for the punctuation-prettifying extension.++data Punctuation = Punctuation+  { punctEnDash :: !Bool+    -- ^ Whether to replace double hyphen @--@ by an en dash @–@ (default:+    -- 'True')+  , punctEmDash :: !Bool+    -- ^ Whether to replace triple hyphen @---@ by an em dash @—@ (default:+    -- 'True')+  } deriving (Eq, Ord, Show, Read, Data, Typeable, Generic)++instance Default Punctuation where+  def = Punctuation+    { punctEnDash = True+    , punctEmDash = True }++-- | Prettify punctuation according to the settings in 'Punctuation'.++punctuationPrettifier :: Punctuation -> Extension+punctuationPrettifier Punctuation {..} = Ext.inlineTrans $ \case+  Plain txt -> Plain+    . f punctEnDash (T.replace "--"  "–")+    . f punctEmDash (T.replace "---" "—")+    $ txt+  other -> other+  where+    f b g = if b then g else id++----------------------------------------------------------------------------+-- Email address obfuscation++-- | This extension makes email addresses in links be rendered as something+-- like this:+--+-- > <a class="protected-email"+-- >    data-email="something@example.org"+-- >    href="javascript:void(0)">Enable JavaScript to see the email</a>+--+-- You'll also need to include jQuery and this bit of JS code for the magic+-- to work:+--+-- > $(document).ready(function () {+-- >     $(".protected-email").each(function () {+-- >         var item = $(this);+-- >         var email = item.data('email');+-- >         item.attr('href', 'mailto:' + email);+-- >         item.html(email);+-- >     });+-- > });++obfuscateEmail+  :: Text+     -- ^ Name of class to assign to the links, e.g. @\"protected-email\"@+  -> Extension+obfuscateEmail class' = Ext.inlineRender $ \old inline ->+  case inline of+    l@(Link _ email mtitle) ->+      if URI.uriScheme email == URI.mkScheme "mailto"+        then let txt = Plain "Enable JavaScript to see the email" :| []+                 js  = fromJust (URI.mkURI "javascript:void(0)")+             in with (old (Link txt js mtitle))+                  [ class_ class'+                  , data_ "email"+                    (URI.render email { URI.uriScheme = Nothing }) ]+        else old l+    other -> old other++----------------------------------------------------------------------------+-- Font Awesome icons++-- | Allow to insert @span@s with font awesome icons using autolinks like+-- this:+--+-- > <fa:user>+--+-- This @user@ identifier is the name of icon you want to insert. You can+-- also control the size of the icon like this:+--+-- > <fa:user/fw> -- fixed width+-- > <fa:user/lg> -- large+-- > <fa:user/2x>+-- > <fa:user/3x>+-- > <fa:user/4x>+-- > <fa:user/5x>+--+-- In general, all path components in this URI that go after the name of+-- icon will be prefixed with @\"fa-\"@ and added as classes, so you can do+-- a lot of fancy stuff, see <http://fontawesome.io/examples/>:+--+-- > <fa:quote-left/3x/pull-left/border>+--+-- See also: <http://fontawesome.io>.++fontAwesome :: Extension+fontAwesome = Ext.inlineRender $ \old inline ->+  case inline of+    l@(Link _ fa _) ->+      if URI.uriScheme fa == URI.mkScheme "fa"+        then case URI.uriPath fa of+               [] -> old l+               xs ->+                 let g x = "fa-" <> URI.unRText x+                 in span_+                    [ (class_ . T.intercalate " ") ("fa" : fmap g xs) ]+                    ""+        else old l+    other -> old other
+ data/toc.html view
@@ -0,0 +1,155 @@+<h1 id="lorem-ipsum">Lorem ipsum</h1>+<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer varius mi+orci, rhoncus ornare nunc tincidunt nec. Aliquam cursus posuere ornare.+Quisque posuere euismod nunc, sed pellentesque metus hendrerit eu. Donec+scelerisque accumsan ante quis interdum. Nullam nec mauris dolor. Lorem+ipsum dolor sit amet, consectetur adipiscing elit. Phasellus id porttitor+nunc, sed laoreet eros. Maecenas ipsum ex, sagittis ut quam quis, vehicula+fringilla tortor. Vestibulum quis consequat mauris, sed porta risus.+Vestibulum nec ornare leo. Cras pharetra, ex sed dapibus pretium, diam+lectus accumsan enim, at malesuada tellus lorem et orci. Sed condimentum+varius ex in mollis.</p>+<p><a href="https://example.org/">https://example.org/</a></p>+<p>Ut in imperdiet neque. Etiam iaculis rhoncus nisl vel porta. Praesent velit+orci, laoreet suscipit bibendum eu, ornare et orci. Fusce feugiat, felis a+vehicula pulvinar, nulla purus dictum arcu, et varius urna purus et nibh.+Duis lobortis fringilla ligula, in aliquet sem maximus a. Suspendisse+potenti. Nullam consequat tellus a lectus vestibulum faucibus. Ut hendrerit+dolor ut libero efficitur accumsan. Mauris dapibus, leo non porttitor+lobortis, lectus ipsum tempor metus, quis iaculis arcu quam malesuada nulla.</p>+<ul>+<li><a href="#nullam-luctus">Nullam luctus</a><ul>+<li><a href="#sed-euismod">Sed euismod</a><ul>+<li><a href="#curabitur-ullamcorper">Curabitur ullamcorper</a><ul>+<li><a href="#ut-dictum-quis">Ut dictum quis</a><ul>+<li><a href="#mauris-efficitur">Mauris efficitur</a></li>+</ul>+</li>+</ul>+</li>+</ul>+</li>+</ul>+</li>+<li><a href="#aenean-auctor-nec">Aenean auctor nec</a></li>+</ul>+<h2 id="nullam-luctus">Nullam luctus</h2>+<p>Nullam <a href="http://example.org/luctus">luctus</a> placerat nisl in dapibus.+Phasellus id erat eros. Ut gravida risus sit amet massa tempor volutpat.+Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere+cubilia Curae; Quisque dictum sapien vel enim tempor, quis ornare justo+consequat. Suspendisse porttitor mollis consectetur. Curabitur sodales,+risus eu dapibus mattis, tellus dolor condimentum dolor, in ultricies nibh+augue vel nibh. Vivamus imperdiet, orci id posuere sollicitudin, diam purus+consequat eros, quis dictum mauris lacus ac lectus. Cras vitae pharetra+risus. Maecenas vehicula, leo vitae semper tristique, libero urna+consectetur massa, eget pharetra magna est nec massa. Vestibulum malesuada+lobortis lacinia.</p>+<p><img src="https://example.org/image.png" alt="My image"></p>+<p>Phasellus tincidunt metus quam, vel mollis turpis ultrices et. Phasellus+consequat diam eu turpis sollicitudin tempus. Fusce suscipit bibendum nisl,+quis rutrum eros volutpat in. Fusce tempor nisi eu ligula volutpat, eu+ultricies arcu blandit. Pellentesque habitant morbi tristique senectus et+netus et malesuada fames ac turpis egestas. Duis eleifend malesuada+venenatis. Morbi tincidunt quis diam ac aliquam.</p>+<h3 id="sed-euismod">Sed euismod</h3>+<p>Sed euismod nisi lorem, ac tempor nibh venenatis at. Integer porta nibh quis+mauris vehicula porta. Sed vel tellus nec lacus porttitor sollicitudin. Sed+facilisis nisl lorem, sed aliquet leo convallis sed. Curabitur vitae aliquet+diam, ac commodo ligula. Nulla aliquet odio at tellus auctor pellentesque.+In sagittis elementum tortor sed lobortis. Fusce nibh turpis, posuere eget+tristique eget, commodo quis leo.</p>+<hr>+<p>Etiam faucibus, ipsum id lobortis molestie, dolor lectus cursus purus, nec+volutpat massa odio vitae ligula. Nulla non consectetur ligula. In sem+felis, vehicula a convallis ut, pellentesque nec diam. Integer ullamcorper+rutrum nulla. Nam arcu dolor, placerat nec molestie et, eleifend sit amet+ante. Suspendisse laoreet orci sit amet vestibulum varius. In at leo eu+lorem tincidunt facilisis. Ut elementum elit ornare risus convallis, ut+viverra orci pretium. Vivamus mi orci, lacinia ac ligula a, condimentum+aliquet ligula.</p>+<h4 id="curabitur-ullamcorper">Curabitur ullamcorper</h4>+<p>Curabitur ullamcorper, lectus id porttitor vehicula, augue purus ornare+orci, ut consequat tellus mauris ac sem. Cras tincidunt sagittis mi, sit+amet viverra erat ultrices vulputate. Donec urna nulla, malesuada non cursus+et, posuere eu sapien. Fusce cursus mauris odio, id tincidunt felis+tincidunt sed. Duis vulputate lectus eu tellus pretium gravida. Nunc at eros+fringilla mi egestas imperdiet. In bibendum justo sapien, sed commodo tellus+auctor sit amet. Fusce at purus turpis. Aliquam a nibh at massa hendrerit+mollis a nec ipsum. Sed porta erat vitae justo sodales gravida nec sed+augue. Maecenas ultrices tristique hendrerit.</p>+<p>Curabitur venenatis vestibulum quam, a facilisis odio dignissim in.+Vestibulum ut turpis pharetra, aliquam metus a, dapibus massa. Mauris+vehicula sapien quis dolor facilisis, in placerat ipsum accumsan. Morbi+accumsan accumsan velit, vel auctor mauris suscipit non. Proin ex lacus,+dapibus et leo eu, lacinia viverra metus. Suspendisse potenti. Maecenas+finibus justo lectus, nec commodo velit cursus sit amet. In at lacus vel+augue porta volutpat eleifend ut orci.</p>+<h5 id="ut-dictum-quis">Ut dictum quis</h5>+<p>Ut dictum quis felis quis auctor. Phasellus lacinia tellus quis massa+commodo dignissim. Nam vitae felis non ex auctor tincidunt sit amet et+lorem. Nulla bibendum odio suscipit, tincidunt felis eget, mollis tortor.+Aliquam a feugiat augue, sed posuere massa. Donec pellentesque sodales+varius. Suspendisse quis mollis massa. Morbi imperdiet at felis in feugiat.+Praesent mauris urna, suscipit vitae condimentum venenatis, fermentum sit+amet purus. Cras non placerat nulla, vel lobortis ex. Nunc ut pulvinar+magna. Nulla vitae arcu turpis. Suspendisse elementum odio eros. Nullam+blandit molestie nibh eget pulvinar. Quisque placerat ante nec pulvinar+fringilla.</p>+<pre><code>Donec in feugiat quam, eget vulputate metus. Sed at velit aliquam, consequat+massa vel, pharetra urna. Donec sed auctor odio. In pretium magna porta mi+posuere malesuada. Morbi sagittis finibus ligula sit amet posuere. Praesent+egestas massa id leo ultricies bibendum. Nullam fringilla commodo mattis. Ut+ut odio eget elit suscipit imperdiet. Orci varius natoque penatibus et+magnis dis parturient montes, nascetur ridiculus mus. Integer interdum+malesuada sem, vitae faucibus risus hendrerit eget. Aliquam vitae imperdiet+sapien, eget feugiat dui. Morbi faucibus nulla et dolor aliquam scelerisque.+Praesent auctor aliquet egestas. Vivamus et blandit velit. Sed ut dui vitae+massa luctus malesuada a vel diam.+</code></pre>+<p>Proin sit amet erat consequat, pellentesque augue at, efficitur libero. Nunc+pretium nulla eros, vitae euismod ex commodo sit amet. Pellentesque laoreet+ac enim id commodo. Integer eleifend nibh quis lorem pellentesque venenatis.+Nulla et eros sit amet diam pharetra porttitor. Sed ullamcorper rutrum+volutpat. Quisque accumsan posuere arcu, scelerisque dignissim ipsum. Donec+congue odio ultricies eros maximus, id maximus metus bibendum. Aenean ornare+metus non nisi varius venenatis. Vestibulum ornare id urna quis tempus. Cras+non arcu vitae magna feugiat sodales. Fusce eget posuere ligula. Donec+porttitor, odio at scelerisque tempus, nunc enim cursus dui, finibus+tincidunt justo lorem quis urna. Integer mauris quam, ultrices quis enim ut,+consectetur consequat sapien. In dapibus arcu eget ultrices vehicula.</p>+<h6 id="mauris-efficitur">Mauris efficitur</h6>+<p>Mauris efficitur mollis purus, nec porttitor ligula condimentum a. Proin+ultricies semper neque ac pulvinar. Nullam vestibulum leo justo, eget+vehicula elit lobortis vitae. Suspendisse commodo nunc et lorem molestie+lobortis. Pellentesque habitant morbi tristique senectus et netus et+malesuada fames ac turpis egestas. Fusce nec mi ultricies, sagittis neque+sed, tincidunt leo. Proin in lorem a libero maximus posuere. Pellentesque+habitant morbi tristique senectus et netus et malesuada fames ac turpis+egestas. Sed interdum eget ipsum id ullamcorper.</p>+<p>Aenean feugiat orci leo. Morbi fringilla, tortor id posuere mollis, lectus+est ullamcorper tellus, nec sagittis augue nibh nec est. Sed pulvinar orci a+justo eleifend dapibus. Phasellus aliquam enim in semper tincidunt. Donec+tempor tristique purus eu pretium. In ornare est at varius elementum. Cras+finibus nisl in nisi vestibulum, sed sollicitudin eros finibus.</p>+<p>Nullam sed nisi blandit, ultrices neque a, finibus ligula. Etiam a ante+sagittis nibh ornare commodo. Morbi id elit vehicula, gravida enim vitae,+suscipit orci. Nunc mattis enim sit amet sem dictum molestie. Nam semper+arcu a libero molestie, at accumsan turpis egestas. Quisque at pulvinar+enim. Cras porttitor, dolor et commodo accumsan, nibh sem dapibus velit, sit+amet maximus urna justo id eros. Praesent vitae aliquet lorem, in facilisis+nulla. Proin sit amet odio nisl. Phasellus sed felis velit. Maecenas pretium+posuere laoreet. Nullam dapibus ullamcorper sapien. Integer nec quam elit.+Fusce at dictum lacus. Nam risus dui, efficitur eget urna at, ultricies+sagittis ligula.</p>+<h2 id="aenean-auctor-nec">Aenean auctor nec</h2>+<p>Aenean auctor nec libero ut laoreet. Etiam vulputate lorem quis ex+dignissim, sit amet tincidunt ligula scelerisque. Nam venenatis blandit+ipsum quis maximus. Curabitur vitae dolor a diam viverra efficitur. Nulla+feugiat iaculis orci quis sodales. Aliquam consequat bibendum turpis, quis+scelerisque lorem fermentum feugiat. Proin pellentesque interdum odio ac+ullamcorper. Proin laoreet purus leo, non eleifend velit venenatis id.+Aliquam consectetur finibus turpis quis sollicitudin. Duis congue est nec+odio hendrerit scelerisque at at sapien. Nunc lacinia ac leo sit amet+sodales. Donec posuere fermentum massa, at tristique nulla aliquet nec.+Mauris dapibus quam non augue maximus suscipit.</p>
+ data/toc.md view
@@ -0,0 +1,167 @@+# Lorem ipsum++Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer varius mi+orci, rhoncus ornare nunc tincidunt nec. Aliquam cursus posuere ornare.+Quisque posuere euismod nunc, sed pellentesque metus hendrerit eu. Donec+scelerisque accumsan ante quis interdum. Nullam nec mauris dolor. Lorem+ipsum dolor sit amet, consectetur adipiscing elit. Phasellus id porttitor+nunc, sed laoreet eros. Maecenas ipsum ex, sagittis ut quam quis, vehicula+fringilla tortor. Vestibulum quis consequat mauris, sed porta risus.+Vestibulum nec ornare leo. Cras pharetra, ex sed dapibus pretium, diam+lectus accumsan enim, at malesuada tellus lorem et orci. Sed condimentum+varius ex in mollis.++<https://example.org>++Ut in imperdiet neque. Etiam iaculis rhoncus nisl vel porta. Praesent velit+orci, laoreet suscipit bibendum eu, ornare et orci. Fusce feugiat, felis a+vehicula pulvinar, nulla purus dictum arcu, et varius urna purus et nibh.+Duis lobortis fringilla ligula, in aliquet sem maximus a. Suspendisse+potenti. Nullam consequat tellus a lectus vestibulum faucibus. Ut hendrerit+dolor ut libero efficitur accumsan. Mauris dapibus, leo non porttitor+lobortis, lectus ipsum tempor metus, quis iaculis arcu quam malesuada nulla.++```toc+```++## Nullam luctus++Nullam [luctus](http://example.org/luctus) placerat nisl in dapibus.+Phasellus id erat eros. Ut gravida risus sit amet massa tempor volutpat.+Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere+cubilia Curae; Quisque dictum sapien vel enim tempor, quis ornare justo+consequat. Suspendisse porttitor mollis consectetur. Curabitur sodales,+risus eu dapibus mattis, tellus dolor condimentum dolor, in ultricies nibh+augue vel nibh. Vivamus imperdiet, orci id posuere sollicitudin, diam purus+consequat eros, quis dictum mauris lacus ac lectus. Cras vitae pharetra+risus. Maecenas vehicula, leo vitae semper tristique, libero urna+consectetur massa, eget pharetra magna est nec massa. Vestibulum malesuada+lobortis lacinia.++![My image](https://example.org/image.png)++Phasellus tincidunt metus quam, vel mollis turpis ultrices et. Phasellus+consequat diam eu turpis sollicitudin tempus. Fusce suscipit bibendum nisl,+quis rutrum eros volutpat in. Fusce tempor nisi eu ligula volutpat, eu+ultricies arcu blandit. Pellentesque habitant morbi tristique senectus et+netus et malesuada fames ac turpis egestas. Duis eleifend malesuada+venenatis. Morbi tincidunt quis diam ac aliquam.++### Sed euismod++Sed euismod nisi lorem, ac tempor nibh venenatis at. Integer porta nibh quis+mauris vehicula porta. Sed vel tellus nec lacus porttitor sollicitudin. Sed+facilisis nisl lorem, sed aliquet leo convallis sed. Curabitur vitae aliquet+diam, ac commodo ligula. Nulla aliquet odio at tellus auctor pellentesque.+In sagittis elementum tortor sed lobortis. Fusce nibh turpis, posuere eget+tristique eget, commodo quis leo.++***++Etiam faucibus, ipsum id lobortis molestie, dolor lectus cursus purus, nec+volutpat massa odio vitae ligula. Nulla non consectetur ligula. In sem+felis, vehicula a convallis ut, pellentesque nec diam. Integer ullamcorper+rutrum nulla. Nam arcu dolor, placerat nec molestie et, eleifend sit amet+ante. Suspendisse laoreet orci sit amet vestibulum varius. In at leo eu+lorem tincidunt facilisis. Ut elementum elit ornare risus convallis, ut+viverra orci pretium. Vivamus mi orci, lacinia ac ligula a, condimentum+aliquet ligula.++#### Curabitur ullamcorper++Curabitur ullamcorper, lectus id porttitor vehicula, augue purus ornare+orci, ut consequat tellus mauris ac sem. Cras tincidunt sagittis mi, sit+amet viverra erat ultrices vulputate. Donec urna nulla, malesuada non cursus+et, posuere eu sapien. Fusce cursus mauris odio, id tincidunt felis+tincidunt sed. Duis vulputate lectus eu tellus pretium gravida. Nunc at eros+fringilla mi egestas imperdiet. In bibendum justo sapien, sed commodo tellus+auctor sit amet. Fusce at purus turpis. Aliquam a nibh at massa hendrerit+mollis a nec ipsum. Sed porta erat vitae justo sodales gravida nec sed+augue. Maecenas ultrices tristique hendrerit.++Curabitur venenatis vestibulum quam, a facilisis odio dignissim in.+Vestibulum ut turpis pharetra, aliquam metus a, dapibus massa. Mauris+vehicula sapien quis dolor facilisis, in placerat ipsum accumsan. Morbi+accumsan accumsan velit, vel auctor mauris suscipit non. Proin ex lacus,+dapibus et leo eu, lacinia viverra metus. Suspendisse potenti. Maecenas+finibus justo lectus, nec commodo velit cursus sit amet. In at lacus vel+augue porta volutpat eleifend ut orci.++##### Ut dictum quis++Ut dictum quis felis quis auctor. Phasellus lacinia tellus quis massa+commodo dignissim. Nam vitae felis non ex auctor tincidunt sit amet et+lorem. Nulla bibendum odio suscipit, tincidunt felis eget, mollis tortor.+Aliquam a feugiat augue, sed posuere massa. Donec pellentesque sodales+varius. Suspendisse quis mollis massa. Morbi imperdiet at felis in feugiat.+Praesent mauris urna, suscipit vitae condimentum venenatis, fermentum sit+amet purus. Cras non placerat nulla, vel lobortis ex. Nunc ut pulvinar+magna. Nulla vitae arcu turpis. Suspendisse elementum odio eros. Nullam+blandit molestie nibh eget pulvinar. Quisque placerat ante nec pulvinar+fringilla.++```+Donec in feugiat quam, eget vulputate metus. Sed at velit aliquam, consequat+massa vel, pharetra urna. Donec sed auctor odio. In pretium magna porta mi+posuere malesuada. Morbi sagittis finibus ligula sit amet posuere. Praesent+egestas massa id leo ultricies bibendum. Nullam fringilla commodo mattis. Ut+ut odio eget elit suscipit imperdiet. Orci varius natoque penatibus et+magnis dis parturient montes, nascetur ridiculus mus. Integer interdum+malesuada sem, vitae faucibus risus hendrerit eget. Aliquam vitae imperdiet+sapien, eget feugiat dui. Morbi faucibus nulla et dolor aliquam scelerisque.+Praesent auctor aliquet egestas. Vivamus et blandit velit. Sed ut dui vitae+massa luctus malesuada a vel diam.+```++Proin sit amet erat consequat, pellentesque augue at, efficitur libero. Nunc+pretium nulla eros, vitae euismod ex commodo sit amet. Pellentesque laoreet+ac enim id commodo. Integer eleifend nibh quis lorem pellentesque venenatis.+Nulla et eros sit amet diam pharetra porttitor. Sed ullamcorper rutrum+volutpat. Quisque accumsan posuere arcu, scelerisque dignissim ipsum. Donec+congue odio ultricies eros maximus, id maximus metus bibendum. Aenean ornare+metus non nisi varius venenatis. Vestibulum ornare id urna quis tempus. Cras+non arcu vitae magna feugiat sodales. Fusce eget posuere ligula. Donec+porttitor, odio at scelerisque tempus, nunc enim cursus dui, finibus+tincidunt justo lorem quis urna. Integer mauris quam, ultrices quis enim ut,+consectetur consequat sapien. In dapibus arcu eget ultrices vehicula.++###### Mauris efficitur++Mauris efficitur mollis purus, nec porttitor ligula condimentum a. Proin+ultricies semper neque ac pulvinar. Nullam vestibulum leo justo, eget+vehicula elit lobortis vitae. Suspendisse commodo nunc et lorem molestie+lobortis. Pellentesque habitant morbi tristique senectus et netus et+malesuada fames ac turpis egestas. Fusce nec mi ultricies, sagittis neque+sed, tincidunt leo. Proin in lorem a libero maximus posuere. Pellentesque+habitant morbi tristique senectus et netus et malesuada fames ac turpis+egestas. Sed interdum eget ipsum id ullamcorper.++Aenean feugiat orci leo. Morbi fringilla, tortor id posuere mollis, lectus+est ullamcorper tellus, nec sagittis augue nibh nec est. Sed pulvinar orci a+justo eleifend dapibus. Phasellus aliquam enim in semper tincidunt. Donec+tempor tristique purus eu pretium. In ornare est at varius elementum. Cras+finibus nisl in nisi vestibulum, sed sollicitudin eros finibus.++Nullam sed nisi blandit, ultrices neque a, finibus ligula. Etiam a ante+sagittis nibh ornare commodo. Morbi id elit vehicula, gravida enim vitae,+suscipit orci. Nunc mattis enim sit amet sem dictum molestie. Nam semper+arcu a libero molestie, at accumsan turpis egestas. Quisque at pulvinar+enim. Cras porttitor, dolor et commodo accumsan, nibh sem dapibus velit, sit+amet maximus urna justo id eros. Praesent vitae aliquet lorem, in facilisis+nulla. Proin sit amet odio nisl. Phasellus sed felis velit. Maecenas pretium+posuere laoreet. Nullam dapibus ullamcorper sapien. Integer nec quam elit.+Fusce at dictum lacus. Nam risus dui, efficitur eget urna at, ultricies+sagittis ligula.++## Aenean auctor nec++Aenean auctor nec libero ut laoreet. Etiam vulputate lorem quis ex+dignissim, sit amet tincidunt ligula scelerisque. Nam venenatis blandit+ipsum quis maximus. Curabitur vitae dolor a diam viverra efficitur. Nulla+feugiat iaculis orci quis sodales. Aliquam consequat bibendum turpis, quis+scelerisque lorem fermentum feugiat. Proin pellentesque interdum odio ac+ullamcorper. Proin laoreet purus leo, non eleifend velit venenatis id.+Aliquam consectetur finibus turpis quis sollicitudin. Duis congue est nec+odio hendrerit scelerisque at at sapien. Nunc lacinia ac leo sit amet+sodales. Donec posuere fermentum massa, at tristique nulla aliquet nec.+Mauris dapibus quam non augue maximus suscipit.
+ mmark-ext.cabal view
@@ -0,0 +1,62 @@+name:                 mmark-ext+version:              0.0.1.0+cabal-version:        >= 1.18+tested-with:          GHC==7.10.3, GHC==8.0.2, GHC==8.2.1+license:              BSD3+license-file:         LICENSE.md+author:               Mark Karpov <markkarpov92@gmail.com>+maintainer:           Mark Karpov <markkarpov92@gmail.com>+homepage:             https://github.com/mrkkrp/mmark-ext+bug-reports:          https://github.com/mrkkrp/mmark-ext/issues+category:             Text+synopsis:             Commonly useful extensions for MMark markdown processor+build-type:           Simple+description:          Commonly useful extensions for MMark markdown processor.+extra-doc-files:      CHANGELOG.md+                    , README.md+data-files:           data/*.md+                    , data/*.html++source-repository head+  type:               git+  location:           https://github.com/mrkkrp/mmark-ext.git++flag dev+  description:        Turn on development settings.+  manual:             True+  default:            False++library+  build-depends:      base             >= 4.8   && < 5.0+                    , data-default-class+                    , foldl            >= 1.2   && < 1.4+                    , lucid            >= 2.6   && < 3.0+                    , mmark            >= 0.0.1 && <= 0.1+                    , modern-uri       >= 0.1.1 && < 0.2+                    , text             >= 0.2   && < 1.3+  if !impl(ghc >= 8.0)+    build-depends:    semigroups       == 0.18.*+  exposed-modules:    Text.MMark.Extension.Common+  if flag(dev)+    ghc-options:      -O0 -Wall -Werror+  else+    ghc-options:      -O2 -Wall+  default-language:   Haskell2010++test-suite tests+  main-is:            Spec.hs+  hs-source-dirs:     tests+  type:               exitcode-stdio-1.0+  build-depends:      base             >= 4.8   && < 5.0+                    , data-default-class+                    , hspec            >= 2.0   && < 3.0+                    , lucid            >= 2.6   && < 3.0+                    , mmark            >= 0.0.1 && <= 0.1+                    , mmark-ext+                    , text             >= 0.2   && < 1.3+  if flag(dev)+    ghc-options:      -O0 -Wall -Werror+  else+    ghc-options:      -O2 -Wall+  other-modules:      Text.MMark.Extension.CommonSpec+  default-language:   Haskell2010
+ tests/Spec.hs view
@@ -0,0 +1,1 @@+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
+ tests/Text/MMark/Extension/CommonSpec.hs view
@@ -0,0 +1,101 @@+{-# LANGUAGE OverloadedStrings #-}++module Text.MMark.Extension.CommonSpec (spec) where++import Data.Default.Class+import Data.Text (Text)+import Test.Hspec+import qualified Data.Text.IO                as TIO+import qualified Data.Text.Lazy              as TL+import qualified Lucid                       as L+import qualified Text.MMark                  as MMark+import qualified Text.MMark.Extension.Common as Ext++spec :: Spec+spec = parallel $ do+  describe "toc" $+    it "works" $+      "data/toc.md" `withToc` "data/toc.html"+  describe "punctuationPrettifier" $ do+    let to = withExt (Ext.punctuationPrettifier def)+        ot = withExt $ Ext.punctuationPrettifier def+          { Ext.punctEnDash = False+          , Ext.punctEmDash = False }+    context "on plain inlines" $ do+      context "when enabeled" $ do+        it "replaces -- with en dash" $+          "Here we go -- at last." `to` "<p>Here we go – at last.</p>\n"+        it "replaces --- with em dash" $+          "Here we go---at last." `to` "<p>Here we go—at last.</p>\n"+      context "when disabled" $ do+        it "does not replace -- with en dash" $+          "Here we go -- at last." `ot` "<p>Here we go -- at last.</p>\n"+        it "does not replace --- with em dash" $+          "Here we go---at last." `ot` "<p>Here we go---at last.</p>\n"+    context "on other inlines" $+      it "has no effect" $+        "`code -- span`" `to` "<p><code>code -- span</code></p>\n"+  describe "obfuscateEmail" $ do+    let to = withExt (Ext.obfuscateEmail "foo")+    context "when URI has the mailto scheme" $+      it "produces the correct HTML" $+        "<mailto:me@example.org>" `to` "<p><a href=\"javascript:void(0)\" class=\"foo\" data-email=\"me@example.org\">Enable JavaScript to see the email</a></p>\n"+    context "when URI has some other scheme" $+      it "produces the correct HTML" $+        "<https:example.org>" `to` "<p><a href=\"https:example.org\">https:example.org</a></p>\n"+    context "other elements" $+      it "not affected" $+        "Something." `to` "<p>Something.</p>\n"+  describe "fontAwesome" $ do+    let to = withExt Ext.fontAwesome+    context "when URI has the fa scheme" $+      it "produces the correct HTML" $ do+        "<fa:>" `to` "<p><a href=\"fa:\">fa:</a></p>\n"+        "<fa:user>" `to` "<p><span class=\"fa fa-user\"></span></p>\n"+        "<fa:user/lg>" `to` "<p><span class=\"fa fa-user fa-lg\"></span></p>\n"+        "<fa:quote-left/3x/pull-left/border>" `to` "<p><span class=\"fa fa-quote-left fa-3x fa-pull-left fa-border\"></span></p>\n"+    context "when URI has some other scheme" $+      it "produces the correct HTML" $+        "<https://example.org>" `to` "<p><a href=\"https://example.org/\">https://example.org/</a></p>\n"+    context "other elements" $+      it "not affected" $+        "Something." `to` "<p>Something.</p>\n"++----------------------------------------------------------------------------+-- Helpers++-- | Feed input into MMark parser, apply an extension, render the parsed+-- document and demand that it matches the given example.++withExt+  :: MMark.Extension   -- ^ MMark extension to use+  -> Text              -- ^ Input for the parser+  -> Text              -- ^ Expected output of the render+  -> Expectation+withExt ext input expected = do+  let Right doc = MMark.parse "" input+      actual = TL.toStrict+        . L.renderText+        . MMark.render+        . MMark.useExtension ext+        $ doc++  actual `shouldBe` expected+-- | Similar to 'withExt' but specialized to test the 'Ext.toc' extension+-- and loads input and expected output from files.++withToc+  :: FilePath          -- ^ File containing input for the parser+  -> FilePath          -- ^ File containing expected output of the render+  -> Expectation+withToc ipath opath = do+  input    <- TIO.readFile ipath+  expected <- TIO.readFile opath+  let Right doc = MMark.parse "" input+      toc       = MMark.runScanner doc (Ext.tocScanner 6)+      actual = TL.toStrict+        . L.renderText+        . MMark.render+        . MMark.useExtension (Ext.toc "toc" toc)+        $ doc+  actual `shouldBe` expected