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 +3/−0
- LICENSE.md +28/−0
- README.md +24/−0
- Setup.hs +6/−0
- Text/MMark/Extension/Common.hs +268/−0
- data/toc.html +155/−0
- data/toc.md +167/−0
- mmark-ext.cabal +62/−0
- tests/Spec.hs +1/−0
- tests/Text/MMark/Extension/CommonSpec.hs +101/−0
+ 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++[](http://opensource.org/licenses/BSD-3-Clause)+[](https://hackage.haskell.org/package/mmark-ext)+[](http://stackage.org/nightly/package/mmark-ext)+[](http://stackage.org/lts/package/mmark-ext)+[](https://travis-ci.org/mrkkrp/mmark-ext)+[](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.++++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