packages feed

literatex (empty) → 0.1.0.0

raw patch · 28 files changed

+7684/−0 lines, 28 filesdep +ansi-wl-pprintdep +basedep +bytestringsetup-changed

Dependencies added: ansi-wl-pprint, base, bytestring, conduit, filepath, literatex, optparse-applicative, tasty, tasty-hunit, text, ttc, unliftio

Files

+ CHANGELOG.md view
@@ -0,0 +1,31 @@+# `literatex-haskell` Changelog++This project follows the [Haskell package versioning policy][PVP], with+versions in `A.B.C.D` format.  `A` may be incremented arbitrarily for+non-technical reasons, but [semantic versioning][SemVer] is otherwise+followed, where `A.B` is the major version, `C` is the minor version, and `D`+is the patch version.  Initial development uses versions `0.0.0.D`, for which+every version is considered breaking.++[PVP]: <https://pvp.haskell.org/>+[SemVer]: <https://semver.org/>++The format of this changelog is based on [Keep a Changelog][KaC], with the+following conventions:++* Level-two heading `Unreleased` is used to track changes that have not been+  released.+* Other level-two headings specify the release in `A.B.C.D (YYYY-MM-DD)`+  format, with newer versions above older versions.+* Level-three headings are used to categorize changes as follows:+    1. Breaking+    2. Non-Breaking+* Changes are listed in arbitrary order and present tense.++[KaC]: <https://keepachangelog.com/en/1.0.0/>++## 0.1.0.0 (2021-05-26)++### Breaking++* Initial public release
+ LICENSE view
@@ -0,0 +1,21 @@+The MIT License++Copyright (c) 2021 Travis Cardwell++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.
+ README.md view
@@ -0,0 +1,366 @@+# LiterateX++[![Project Status: Active – The project has reached a stable, usable state and is being actively developed.](https://www.repostatus.org/badges/latest/active.svg)](https://www.repostatus.org/#active)+[![GitHub CI](https://github.com/ExtremaIS/literatex-haskell/workflows/CI/badge.svg?branch=main)](https://github.com/ExtremaIS/literatex-haskell/actions)+[![Hackage](https://img.shields.io/hackage/v/literatex.svg)](https://hackage.haskell.org/package/literatex)+[![Stackage LTS](https://stackage.org/package/literatex/badge/lts)](https://stackage.org/package/literatex)+[![Stackage Nightly](https://stackage.org/package/literatex/badge/nightly)](https://stackage.org/nightly/package/literatex)++![LiterateX: LiterateBash, LiterateC, LiterateClojure, LiterateErlang, LiterateHaskell, LiterateIdris, LiterateJavaScript, LiteratePython, LiterateRacket, LiterateSQL, Literate...](project/animation/literatex.gif)++* [Overview](#overview)+* [Source Formats](#source-formats)+    * [Double-Dash Comments](#double-dash-comments)+    * [Double-Slash Comments](#double-slash-comments)+    * [Hash Comments](#hash-comments)+    * [Lisp Semicolon Comments](#lisp-semicolon-comments)+    * [Literate Haskell](#literate-haskell)+    * [Percent Comments](#percent-comments)+* [CLI](#cli)+    * [Requirements](#requirements)+    * [Installation](#installation)+        * [Installation From Source](#installation-from-source)+        * [`.deb` Package Installation](#deb-package-installation)+        * [`.rpm` Package Installation](#rpm-package-installation)+    * [Usage](#usage)+* [Library](#library)+* [Related Work](#related-work)+* [Project](#project)+    * [Links](#links)+    * [Releases](#releases)+    * [Contribution](#contribution)+    * [License](#license)++## Overview++LiterateX transforms literate source code to Markdown.  Write documentation in+Markdown format in the comments of source code, and LiterateX can transform+the file to Markdown, optionally including the source code with syntax+highlighting and line numbers.  Many [source formats](#source-formats) are+supported, and the following target formats are supported:++* [Pandoc Markdown](https://pandoc.org/MANUAL.html#pandocs-markdown)+* [GitHub Flavored Markdown](https://github.github.com/gfm/)++LiterateX can be used to document code that is particularly important or+difficult to understand.  For example, documentation can be written in the SQL+file that defines the schema for a complex database.  LiterateX can translate+the `.sql` file to a Markdown file, which can then be converted to HTML, PDF,+or even EPUB with [Pandoc][].++[Pandoc]: <https://pandoc.org/>++LiterateX can also be used in the publishing of blog entries, magazine+articles, or books.  Use the [command-line utility](#cli) or integrate the+Haskell [library](#library) into your own software.++LiterateX has support for **source code rules**, comment lines that are used+to visually separate sections of source code.  Since source code rules are+only used to make the source code easier to scan quickly, they are ignored+(treated as a blank line) when translating to Markdown.++LiterateX also has support for [shebang][] lines at the start of the file.+They can be ignored so that they do not precede the documentation.++[shebang]: <https://en.wikipedia.org/wiki/Shebang_(Unix)>++## Source Formats++LiterateX supports a number of source formats.  With the exception of+[literate Haskell](#literate-haskell), documentation is written in line+comments in the source language.  Note that multi-line comments are treated as+code, not documentation.++### Double-Dash Comments++Documentation is parsed from lines that begin with two dashes immediately+followed by a space (`-- `).  Lines that only contain two dashes are treated+as blank lines in the documentation.  Lines that contain three or more dashes+are treated as source code rules and are ignored.++``` haskell+-- # Haskell Example+--+-- Executables are implemented using a `Main` module that exposes a function+-- named `main`.++module Main (main) where++-- The `main` function is run when the program is executed.++main :: IO ()+main = putStrLn "Hello!"++-- This simple example just prints "Hello!" to the screen.+```++Languages that use double-dash comments include the following:++* [Elm](https://elm-lang.org/)+* [Haskell](https://www.haskell.org/)+* [Idris](https://www.idris-lang.org/)+* [Lua](http://www.lua.org/)+* [SQL](https://en.wikipedia.org/wiki/SQL)++### Double-Slash Comments++Documentation is parsed from lines that begin with two slashes immediately+followed by a space (`// `).  Lines that only contain two slashes are treated+as blank lines in the documentation.  Lines that contain three or more slashes+are treated as source code rules and are ignored.++``` rust+// # Rust Example+//+// The `main` function is run when the program is executed.++fn main() {+    println!("Hello!");+}++// This simple example just prints "Hello!" to the screen.+```++Languages that use double-slash comments include the following:++* [C][]+* [CSS](https://en.wikipedia.org/wiki/CSS)+* [Go](https://golang.org/)+* [Java](https://www.java.com/)+* [JavaScript](https://en.wikipedia.org/wiki/JavaScript)+* [Kotlin](https://kotlinlang.org/)+* [PHP](https://www.php.net/)+* [Rust](https://www.rust-lang.org/)+* [Scala](https://www.scala-lang.org/)+* [TypeScript](https://www.typescriptlang.org/)++[C]: <https://en.wikipedia.org/wiki/C_(programming_language)>++### Hash Comments++Documentation is parsed from lines that begin with a hash character+immediately followed by a space (`# `).  Lines that only contain a hash+character are treated as blank lines in the documentation.  Lines that contain+two or more hash characters are treated as source code rules and are ignored.++``` python+# # Python Example+#+# A quick-and-dirty Python script can include commands at the top level!++print("Hello!")++# This simple example just prints "Hello!" to the screen.+```++Languages that use hash comments include the following:++* [Bash](https://www.gnu.org/software/bash/)+* [Elixir](https://elixir-lang.org/)+* [Perl](https://www.perl.org/)+* [Python](https://www.python.org/)+* [R](https://www.r-project.org/)+* [Ruby](https://www.ruby-lang.org/)++### Lisp Semicolon Comments++Lisp languages use a semicolon (`;`) for line comments, but there is a+special convention to use a different number of semicolons according to the+context.  Documentation is parsed from lines that begin with one to four+semicolons immediately followed by a space (`; `, `;; `, `;;; `, or `;;;; `).+Lines that only contain one to four semicolons are treated as blank lines in+the documentation.  Lines that contain more than four semicolons are treated+as source code rules and are ignored.++``` scheme+; # Racket Example+;+; Racket programs must declare the language to use.++#lang racket/base++; Racket can also include commands at the top level!++(println "Hello!")++; This simple example just prints "Hello!" to the screen.+```++Languages that use Lisp semicolon comments include the following:++* [Clojure](https://clojure.org/)+* [Common Lisp](https://common-lisp.net/)+* [Racket](https://racket-lang.org/)+* [Scheme][]++[Scheme]: <https://en.wikipedia.org/wiki/Scheme_(programming_language)>++### Literate Haskell++[GHC](https://www.haskell.org/ghc/) has special support for+[literate programming](https://wiki.haskell.org/Literate_programming).  With+this source format, documentation is not written in comments.  Instead, lines+that contain source code are prefixes with a greater-than sign and a space+(`> `).++Note that this source format does not support source code rules.++``` lhaskell+# Literate Haskell Example++Executables are implemented using a `Main` module that exposes a function+named `main`.++> module Main (main) where++The `main` function is run when the program is executed.++> main :: IO ()+> main = putStrLn "Hello!"++This simple example just prints "Hello!" to the screen.+```++### Percent Comments++Documentation is parsed from lines that begin with a percent character+immediately followed by a space (`% `).  Lines that only contain a percent+character are treated as blank lines in the documentation.  Lines that contain+two or more percent characters are treated as source code rules and are+ignored.++``` erlang+% # Erlang Example+%+% Programs are implemented using a `main` module that exports a `start/0`+% function.++-module(main).+-export([start/0]).++% The `start` function is run when the program is executed.++start() -> io.fwrite("Hello!\n").++% This simple example just prints "Hello!" to the screen.+```++Languages that use percent comments include the following:++* [Erlang](https://www.erlang.org/)+* [LaTeX](https://www.latex-project.org/)++## CLI++LiterateX may be used via a command-line utility named `literatex`.++### Requirements++`literatex` has only been tested on Linux.  It *might* work on Windows and+macOS.++### Installation++#### Installation From Source++`literatex` can be built from source using [Stack][].  For example, you can+install the latest release (to `/usr/local` on Linux) as follows:++```+$ git clone https://github.com/ExtremaIS/literatex-haskell.git+$ cd literatex-haskell+$ make+$ sudo make install+```++[Stack]: <https://www.haskellstack.org>++#### `.deb` Package Installation++Check the [Releases][] page for `.deb` packages.++#### `.rpm` Package Installation++Check the [Releases][] page for `.rpm` packages.++[Releases]: <https://github.com/ExtremaIS/literatex-haskell/releases>++### Usage++See the [`literatex` man page](doc/literatex.1.md) for usage information.++## Library++The [LiterateX Haskell library][] provides an API for integrating LiterateX+functionality in your own software.++[LiterateX Haskell library]: <https://hackage.haskell.org/package/literatex>++## Related Work++[Literate programming][] is a style of programming introduced by+[Donald Knuth][] in which [the main idea][] is "to regard a program as a+communication to human beings rather than as a set of instructions to a+computer."  LiterateX is faithful to this idea in that it is used for+communication to human beings.  Note, however, that LiterateX does *not*+support another core aspect of Knuth's literate programming: the ability to+write source code in the order best for human understanding.  Since LiterateX+transforms actual source code files, the source code has to be written in+whatever order is required by the language.  Those interested in writing code+in different order are encouraged to check out [noweb][] and [CWEB][].++[Literate programming]: <https://en.wikipedia.org/wiki/Literate_programming>+[Donald Knuth]: <https://en.wikipedia.org/wiki/Donald_Knuth>+[the main idea]: <https://www-cs-faculty.stanford.edu/~knuth/cweb.html>+[noweb]: <https://en.wikipedia.org/wiki/Noweb>+[CWEB]: <https://en.wikipedia.org/wiki/CWEB>++The [lhs2tex][] utility is used to work with literate Haskell and LaTeX.++[lhs2tex]: <https://github.com/kosmikus/lhs2tex>++The [src2md][] utility, written in Common Lisp, also supports multiple source+formats.  It outputs Markdown that includes HTML, which limits the usefulness+of the Markdown.++[src2md]: <https://git.sr.ht/~aerique/src2markup>++The [extract-documentation-comments][] utility, written in JavaScript,+extracts documentation from multi-line JavaScript comments.++[extract-documentation-comments]: <https://github.com/Anadian/extract-documentation-comments#readme>++[mlp.clj][], written in Clojure, is a [babashka][] script that transforms+literate Clojure source code to Markdown, including HTML.  The author uses it+to implement a [live preview][] of literate Clojure documentation while using+the [Notepad++][] (Windows editor).++[mlp.clj]: <https://github.com/linpengcheng/ClojureBoxNpp/blob/master/Notepad%2B%2B/tools/clj/mlp.clj>+[babashka]: <https://github.com/babashka/babashka#readme>+[live preview]: <https://github.com/linpengcheng/ClojureBoxNpp/tree/master/Notepad%2B%2B/plugins/Config/PreviewHTML>+[Notepad++]: <https://notepad-plus-plus.org/>++## Project++### Links++* GitHub: <https://github.com/ExtremaIS/literatex-haskell>++### Releases++All releases are tagged in the `main` branch.  Release tags are signed using+the+[`security@extrema.is` GPG key](http://keys.gnupg.net/pks/lookup?op=vindex&fingerprint=on&search=0x1D484E4B4705FADF).++### Contribution++Issues and feature requests are tracked on GitHub:+<https://github.com/ExtremaIS/literatex-haskell/issues>++Issues may also be submitted via email to <bugs@extrema.is>.++### License++This project is released under the+[MIT License](https://opensource.org/licenses/MIT) as specified in the+[`LICENSE`](LICENSE) file.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ app/LibOA.hs view
@@ -0,0 +1,122 @@+------------------------------------------------------------------------------+-- |+-- Module      : LibOA+-- Description : supplementary functions for optparse-applicative+-- Copyright   : Copyright (c) 2019-2021 Travis Cardwell+-- License     : MIT+--+-- This is a collection of functions that I often use with+-- @optparse-applicative@.  I do not feel that it is worth maintaining yet+-- another helper package on Hackage, so I just copy the code to different+-- projects as required.  If the library grows to a substantial size or others+-- with to use it, I will reconsider.+--+-- Revision: 2021-04-04+------------------------------------------------------------------------------++{-# LANGUAGE CPP #-}++module LibOA+  ( -- * Options+    -- $Options+    helper+  , versioner+    -- * Utilities+  , commands+    -- * Help+  , (<||>)+  , section+  , table+  , vspace+  ) where++-- https://hackage.haskell.org/package/ansi-wl-pprint+import qualified Text.PrettyPrint.ANSI.Leijen as Doc+import Text.PrettyPrint.ANSI.Leijen (Doc)++-- https://hackage.haskell.org/package/base+import qualified Data.List as List+#if !MIN_VERSION_base (4,11,0)+import Data.Monoid ((<>))+#endif++-- https://hackage.haskell.org/package/optparse-applicative+import qualified Options.Applicative as OA+import qualified Options.Applicative.Common as OAC+import qualified Options.Applicative.Types as OAT++------------------------------------------------------------------------------+-- $Options+--+-- Option descriptions are not capitalized.++-- | A hidden @-h@ / @--help@ option that always fails, showing the help+--+-- This is the same as 'OA.helper' except that it has a different help+-- message.+helper :: OA.Parser (a -> a)+#if MIN_VERSION_optparse_applicative (0,16,0)+helper = OA.option helpReader $ mconcat+    [ OA.short 'h'+    , OA.long "help"+    , OA.help "show this help text"+    , OA.hidden+    ]+  where+    helpReader = do+      potentialCommand <- OAT.readerAsk+      OA.readerAbort $ OA.ShowHelpText (Just potentialCommand)+#else+helper = OA.abortOption OA.ShowHelpText $ mconcat+    [ OA.short 'h'+    , OA.long "help"+    , OA.help "show help and exit"+    , OA.hidden+    ]+#endif++-- | A hidden @--version@ option that always fails, showing the version+versioner+  :: String  -- ^ version string+  -> OA.Parser (a -> a)+versioner verStr = OA.infoOption verStr $ mconcat+    [ OA.long "version"+    , OA.help "show version and exit"+    , OA.hidden+    ]++------------------------------------------------------------------------------+-- $Utilities++-- | Get a list of commands for a parser+commands :: OA.Parser a -> [String]+commands =+    let go _ opt = case OAT.optMain opt of+           OAT.CmdReader _ cmds _ -> reverse cmds+           _otherReader           -> []+    in  concat . OAC.mapParser go++------------------------------------------------------------------------------+-- $Help++-- | Insert a blank line between two documents+(<||>) :: Doc -> Doc -> Doc+d1 <||> d2 = d1 <> Doc.line <> Doc.line <> d2+infixr 5 <||>++-- | Create a section with a title and indented body+section :: String -> Doc -> Doc+section title = (Doc.text title Doc.<$$>) . Doc.indent 2++-- | Create a two-column table+table :: [(String, String)] -> Doc+table rows =+    let width = 1 + maximum (map (length . fst) rows)+    in  Doc.vcat+          [ Doc.fillBreak width (Doc.text l) Doc.<+> Doc.text r+          | (l, r) <- rows+          ]++-- | Vertically space documents with blank lines between them+vspace :: [Doc] -> Doc+vspace = mconcat . List.intersperse (Doc.line <> Doc.line)
+ app/Main.hs view
@@ -0,0 +1,210 @@+------------------------------------------------------------------------------+-- |+-- Module      : Main+-- Description : literatex command-line utility+-- Copyright   : Copyright (c) 2021 Travis Cardwell+-- License     : MIT+------------------------------------------------------------------------------++{-# LANGUAGE RecordWildCards #-}++module Main (main) where++-- https://hackage.haskell.org/package/ansi-wl-pprint+import Text.PrettyPrint.ANSI.Leijen (Doc)++-- https://hackage.haskell.org/package/base+import Control.Applicative (optional)+import System.Exit (ExitCode(ExitFailure), exitWith)+import System.IO (hPutStrLn, stderr, stdin, stdout)++-- https://hackage.haskell.org/package/optparse-applicative+import qualified Options.Applicative as OA++-- https://hackage.haskell.org/package/ttc+import qualified Data.TTC as TTC++-- (literatex)+import qualified LiterateX+import qualified LiterateX.Renderer as Renderer+import qualified LiterateX.SourceDefaults as SourceDefaults+import LiterateX.Types (CodeLanguage, SourceFormat, TargetFormat)+import qualified LiterateX.Types.SourceFormat as SourceFormat+import qualified LiterateX.Types.TargetFormat as TargetFormat++-- (literatex:executable)+import qualified LibOA++------------------------------------------------------------------------------+-- $Defaults++defaultTargetFormat :: TargetFormat+defaultTargetFormat = TargetFormat.PandocMarkdown++------------------------------------------------------------------------------+-- $Options++data Options+  = Options+    { formatOpt      :: !(Maybe SourceFormat)+    , rendererOpts   :: !Renderer.Options+    , noHighlightOpt :: !Bool+    , inputOpt       :: !(Maybe FilePath)+    , outputOpt      :: !(Maybe FilePath)+    }++sourceFormatOption :: OA.Parser SourceFormat+sourceFormatOption = OA.option (OA.eitherReader TTC.parse) $ mconcat+    [ OA.long "source"+    , OA.short 's'+    , OA.metavar "SOURCE"+    , OA.help "source format"+    ]++targetFormatOption :: OA.Parser TargetFormat+targetFormatOption = OA.option (OA.eitherReader TTC.parse) $ mconcat+    [ OA.long "target"+    , OA.short 't'+    , OA.metavar "TARGET"+    , OA.value defaultTargetFormat+    , OA.showDefaultWith TTC.render+    , OA.help "target format"+    ]++languageOption :: OA.Parser CodeLanguage+languageOption = OA.option (OA.eitherReader TTC.parse) $ mconcat+    [ OA.long "language"+    , OA.short 'l'+    , OA.metavar "LANGUAGE"+    , OA.help "code language"+    ]++ignoreShebangOption :: OA.Parser Bool+ignoreShebangOption = OA.switch $ mconcat+    [ OA.long "ignore-shebang"+    , OA.help "ignore shebangs"+    ]++noCodeOption :: OA.Parser Bool+noCodeOption = OA.switch $ mconcat+    [ OA.long "no-code"+    , OA.help "do not display code"+    ]++noNumbersOption :: OA.Parser Bool+noNumbersOption = OA.switch $ mconcat+    [ OA.long "no-numbers"+    , OA.help "do not number code lines"+    ]++renderOptions :: OA.Parser Renderer.Options+renderOptions = Renderer.Options+    <$> targetFormatOption+    <*> optional languageOption+    <*> ignoreShebangOption+    <*> fmap not noCodeOption+    <*> fmap not noNumbersOption++noHighlightOption :: OA.Parser Bool+noHighlightOption = OA.switch $ mconcat+    [ OA.long "no-highlight"+    , OA.help "do not highlight code"+    ]++inputOption :: OA.Parser FilePath+inputOption = OA.strOption $ mconcat+    [ OA.long "input"+    , OA.short 'i'+    , OA.metavar "FILE"+    , OA.help "input file (default: STDIN)"+    ]++outputOption :: OA.Parser FilePath+outputOption = OA.strOption $ mconcat+    [ OA.long "output"+    , OA.short 'o'+    , OA.metavar "FILE"+    , OA.help "output file (default: STDOUT)"+    ]++options :: OA.Parser Options+options = Options+    <$> optional sourceFormatOption+    <*> renderOptions+    <*> noHighlightOption+    <*> optional inputOption+    <*> optional outputOption++------------------------------------------------------------------------------+-- $Library++errorExit :: String -> IO a+errorExit message = do+    hPutStrLn stderr $ "error: " ++ message+    exitWith $ ExitFailure 1++------------------------------------------------------------------------------+-- $Main++main :: IO ()+main = do+    Options{..} <- OA.execParser pinfo+    let defaultOpts = SourceDefaults.defaultsFor =<< inputOpt+    format <- case (formatOpt, defaultOpts, inputOpt) of+      (Just format, _, _) -> pure format+      (Nothing, Just (format, _), _) -> pure format+      (Nothing, Nothing, Just{}) ->+        errorExit "source format not specified and unknown input filename"+      (Nothing, Nothing, Nothing) ->+        errorExit "source format not specified and no input filename"+    let codeLanguage = Renderer.codeLanguage rendererOpts+        opts = rendererOpts+          { Renderer.codeLanguage =+              case (noHighlightOpt, codeLanguage, defaultOpts) of+                (True, _, _) -> Nothing+                (False, opt@Just{}, _) -> opt+                (False, Nothing, Just (_, lang)) -> Just lang+                (False, Nothing, Nothing) -> Nothing+          }+    case (inputOpt, outputOpt) of+      (Just input, Just output) ->+        LiterateX.transformFileToFile format opts input output+      (Just input, Nothing) ->+        LiterateX.transformFileToHandle format opts input stdout+      (Nothing, Just output) ->+        LiterateX.transformHandleToFile format opts stdin output+      (Nothing, Nothing) ->+        LiterateX.transformHandleToHandle format opts stdin stdout+  where+    pinfo :: OA.ParserInfo Options+    pinfo+      = OA.info+          (LibOA.helper <*> LibOA.versioner LiterateX.version <*> options)+      $ mconcat+          [ OA.fullDesc+          , OA.progDesc "transform literate source code to Markdown"+          , OA.failureCode 2+          , OA.footerDoc . Just $ LibOA.vspace+              [ sourceFormatHelp+              , targetFormatHelp+              , defaultsHelp+              ]+          ]++    sourceFormatHelp :: Doc+    sourceFormatHelp = LibOA.section "SOURCE options:" $ LibOA.table+      [ (TTC.render format, SourceFormat.describe format)+      | format <- SourceFormat.list+      ]++    targetFormatHelp :: Doc+    targetFormatHelp = LibOA.section "TARGET options:" $ LibOA.table+      [ (TTC.render format, TargetFormat.describe format)+      | format <- TargetFormat.list+      ]++    defaultsHelp :: Doc+    defaultsHelp = LibOA.section "Default options:" $ LibOA.table+      [ (ext, TTC.render lang ++ " (" ++ TTC.render format ++ ")")+      | (ext, (format, lang)) <- SourceDefaults.extensionDefaults+      ]
+ examples/highlevel.lhs view
@@ -0,0 +1,41 @@+LiterateX High-Level Example+============================++This is a simple example of using the [LiterateX][] high-level API to+transform Haskell source code to [Pandoc Markdown][].++[LiterateX]: <https://github.com/ExtremaIS/literatex-haskell#readme>+[Pandoc Markdown]: <https://pandoc.org/MANUAL.html#pandocs-markdown>++Code+----++When executed, this example program reads this source code and transforms it+to Markdown, writing the output to `STDOUT`.  Note that the path is+hard-coded, so be sure to execute the program from the project directory.++The `OverloadedStrings` extension is used to set the code language below.++> {-# LANGUAGE OverloadedStrings #-}+>+> module Main (main) where++Imports from [base](https://hackage.haskell.org/package/base):++> import System.IO (stdout)++Imports from [literatex](https://hackage.haskell.org/package/literatex):++> import qualified LiterateX+> import qualified LiterateX.Renderer as Renderer+> import qualified LiterateX.Types.SourceFormat as SourceFormat++The `main` function is just a call to `LiterateX.transformFileToHandle` with+appropriate options.++> main :: IO ()+> main = LiterateX.transformFileToHandle+>     SourceFormat.LiterateHaskell+>     (Renderer.defaultOptionsFor "haskell")+>     "examples/highlevel.lhs"+>     stdout
+ examples/lowlevel.hs view
@@ -0,0 +1,36 @@+{-# LANGUAGE OverloadedStrings #-}++module Main (main) where++-- https://hackage.haskell.org/package/base+import System.IO (stdout)++-- https://hackage.haskell.org/package/literatex+import qualified LiterateX+import qualified LiterateX.Renderer as Renderer+import qualified LiterateX.Types.SourceFormat as SourceFormat++------------------------------------------------------------------------------++demo :: String+demo = unlines+    [ "-- Demo"+    , "-- ===="+    , "--"+    , "-- This source code is embedded in the example, but it could come from"+    , "-- a database or API call."+    , ""+    , "module Main (main) where"+    , ""+    , "main :: IO ()"+    , "main = putStrLn \"Hello!\""+    ]++------------------------------------------------------------------------------++main :: IO ()+main = LiterateX.runIO+    SourceFormat.DoubleDash+    (Renderer.defaultOptionsFor "haskell")+    (LiterateX.sourceString demo)+    (LiterateX.sinkHandle stdout)
+ literatex.cabal view
@@ -0,0 +1,138 @@+name:           literatex+version:        0.1.0.0+category:       Utils+synopsis:       transform literate source code to Markdown+description:+  This package provides a library as well as a command-line utility that+  transforms literate source code to Markdown.  Please see the README on+  GitHub at <https://github.com/ExtremaIS/literatex-haskell#readme>.++homepage:       https://github.com/ExtremaIS/literatex-haskell#readme+bug-reports:    https://github.com/ExtremaIS/literatex-haskell/issues+author:         Travis Cardwell <travis.cardwell@extrema.is>+maintainer:     Travis Cardwell <travis.cardwell@extrema.is>+copyright:      Copyright (c) 2021 Travis Cardwell+license:        MIT+license-file:   LICENSE++cabal-version:  1.24+build-type:     Simple+tested-with:+  GHC ==8.2.2+   || ==8.4.4+   || ==8.6.5+   || ==8.8.4+   || ==8.10.4+   || ==9.0.1++extra-source-files:+  CHANGELOG.md+  README.md++source-repository head+  type: git+  location: https://github.com/ExtremaIS/literatex-haskell.git++flag examples+  description: build examples+  default: False++flag write-hie+  description: write .hie files+  default: False++library+  hs-source-dirs: src+  exposed-modules:+      LiterateX+    , LiterateX.Parser+    , LiterateX.Renderer+    , LiterateX.SourceDefaults+    , LiterateX.Types+    , LiterateX.Types.CodeLanguage+    , LiterateX.Types.SourceFormat+    , LiterateX.Types.SourceLine+    , LiterateX.Types.TargetFormat+  other-modules:+      Paths_literatex+  build-depends:+      base >=4.7 && <5+    , bytestring >=0.10.8 && <0.12+    , conduit >=1.3 && <1.4+    , text >=1.2.3 && <1.3+    , ttc >=0.4 && <0.5+    , unliftio >=0.2 && <0.3+  default-language: Haskell2010+  default-extensions:+      OverloadedStrings+  if flag(write-hie)+    ghc-options: -Wall -fwrite-ide-info -hiedir=.hie+  else+    ghc-options: -Wall++executable literatex+  hs-source-dirs: app+  main-is: Main.hs+  other-modules:+      LibOA+  build-depends:+      ansi-wl-pprint >=0.6 && <0.7+    , base+    , optparse-applicative >=0.14 && <0.17+    , literatex+    , ttc+  default-language: Haskell2010+  ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N++executable example-highlevel+  if flag(examples)+    buildable: True+  else+    buildable: False+  hs-source-dirs: examples+  main-is: highlevel.lhs+  build-depends:+      base+    , literatex+  default-language: Haskell2010+  ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N++executable example-lowlevel+  if flag(examples)+    buildable: True+  else+    buildable: False+  hs-source-dirs: examples+  main-is: lowlevel.hs+  build-depends:+      base+    , literatex+  default-language: Haskell2010+  ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N++test-suite literatex-test+  type: exitcode-stdio-1.0+  hs-source-dirs: test+  main-is: Spec.hs+  other-modules:+      LiterateX.Test.API+    , LiterateX.Test.SourceFormat+    , LiterateX.Test.SourceFormat.DoubleDash+    , LiterateX.Test.SourceFormat.DoubleSlash+    , LiterateX.Test.SourceFormat.Hash+    , LiterateX.Test.SourceFormat.LispSemicolons+    , LiterateX.Test.SourceFormat.LiterateHaskell+    , LiterateX.Test.SourceFormat.Percent+    , LiterateX.Test.TargetFormat+  build-depends:+      base+    , bytestring+    , filepath+    , literatex+    , tasty+    , tasty-hunit+    , text+    , ttc+    , unliftio+  default-language: Haskell2010+  ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N
+ src/LiterateX.hs view
@@ -0,0 +1,531 @@+------------------------------------------------------------------------------+-- |+-- Module      : LiterateX+-- Description : API+-- Copyright   : Copyright (c) 2021 Travis Cardwell+-- License     : MIT+--+-- This module provides high-level as well as low-level API functions for+-- transforming literate source code.+------------------------------------------------------------------------------++{-# LANGUAGE FlexibleInstances #-}++module LiterateX+  ( -- * Constants+    version+    -- * API+    -- ** High-Level+    -- $HighLevelAPI+  , transformTextToText+  , transformTextToHandle+  , transformTextToFile+  , transformHandleToText+  , transformHandleToHandle+  , transformHandleToFile+  , transformFileToText+  , transformFileToHandle+  , transformFileToFile+    -- ** Low-Level+    -- $LowLevelAPI+  , runPure+  , runIO+  , runResource+    -- *** Producers+  , sourceString+  , sourceText+  , sourceLazyText+  , sourceByteString+  , sourceLazyByteString+  , sourceHandle+  , sourceFile+    -- *** Consumers+  , sinkString+  , sinkText+  , sinkLazyText+  , sinkByteString+  , sinkLazyByteString+  , sinkHandle+  , sinkFile+    -- *** Transformers+  , transform+  ) where++-- https://hackage.haskell.org/package/base+import Control.Monad.IO.Class (MonadIO)+import Data.Version (showVersion)+import System.IO (Handle)++-- https://hackage.haskell.org/package/bytestring+import qualified Data.ByteString as BS+import qualified Data.ByteString.Lazy as BSL++-- https://hackage.haskell.org/package/conduit+import qualified Conduit as C+import Data.Conduit ((.|))+import qualified Data.Conduit.Combinators as CC+import qualified Data.Conduit.List as CL++-- https://hackage.haskell.org/package/text+import qualified Data.Text as T+import qualified Data.Text.Encoding as TE+import qualified Data.Text.Encoding.Error as TEE+import qualified Data.Text.Lazy as TL++-- https://hackage.haskell.org/package/unliftio+import UnliftIO (MonadUnliftIO)++-- (literatex)+import qualified LiterateX.Parser as Parser+import qualified LiterateX.Renderer as Renderer+import LiterateX.Types (SourceFormat)++-- (literatex:cabal)+import qualified Paths_literatex as Project++------------------------------------------------------------------------------+-- $Constants++-- | LiterateX version string (\"@literatex-haskell X.X.X@\")+version :: String+version = "literatex-haskell " ++ showVersion Project.version++------------------------------------------------------------------------------+-- $HighLevelAPI+--+-- This high-level API provides functions for transforming literate source+-- code.  These functions provide support for transforming from/to lazy+-- 'TL.Text', 'Handle's, and files.+--+-- Example usage:+--+-- @+-- {-\# LANGUAGE OverloadedStrings \#-}+--+-- module Main (main) where+--+-- -- https://hackage.haskell.org/package/literatex+-- import qualified LiterateX+-- import qualified LiterateX.Renderer as Renderer+-- import qualified LiterateX.Types.SourceFormat as SourceFormat+--+-- main :: IO ()+-- main = LiterateX.transformFileToFile+--     SourceFormat.LiterateHaskell+--     (Renderer.defaultOptionsFor "haskell")+--     "demo.lhs"+--     "demo.md"+-- @++-- | Transform from lazy 'TL.Text' to lazy 'TL.Text'+--+-- @since 0.0.1.0+transformTextToText+  :: SourceFormat+  -> Renderer.Options+  -> TL.Text+  -> TL.Text+transformTextToText sourceFormat rendererOpts source =+    runPure sourceFormat rendererOpts (sourceLazyText source) sinkLazyText++-- | Transform from lazy 'TL.Text' to a 'Handle'+--+-- @since 0.0.1.0+transformTextToHandle+  :: MonadIO m+  => SourceFormat+  -> Renderer.Options+  -> TL.Text+  -> Handle+  -> m ()+transformTextToHandle sourceFormat rendererOpts source target =+    runIO sourceFormat rendererOpts+      (sourceLazyText source)+      (sinkHandle target)++-- | Transform from lazy 'TL.Text' to a file+--+-- @since 0.0.1.0+transformTextToFile+  :: MonadUnliftIO m+  => SourceFormat+  -> Renderer.Options+  -> TL.Text+  -> FilePath+  -> m ()+transformTextToFile sourceFormat rendererOpts source target =+    runResource sourceFormat rendererOpts+      (sourceLazyText source)+      (sinkFile target)++-- | Transform from a 'Handle' to lazy 'TL.Text'+--+-- @since 0.0.1.0+transformHandleToText+  :: MonadIO m+  => SourceFormat+  -> Renderer.Options+  -> Handle+  -> m TL.Text+transformHandleToText sourceFormat rendererOpts source =+    runIO sourceFormat rendererOpts (sourceHandle source) sinkLazyText++-- | Transform from a 'Handle' to a 'Handle'+--+-- @since 0.0.1.0+transformHandleToHandle+  :: MonadIO m+  => SourceFormat+  -> Renderer.Options+  -> Handle+  -> Handle+  -> m ()+transformHandleToHandle sourceFormat rendererOpts source target =+    runIO sourceFormat rendererOpts (sourceHandle source) (sinkHandle target)++-- | Transform from a 'Handle' to a file+--+-- @since 0.0.1.0+transformHandleToFile+  :: MonadUnliftIO m+  => SourceFormat+  -> Renderer.Options+  -> Handle+  -> FilePath+  -> m ()+transformHandleToFile sourceFormat rendererOpts source target =+    runResource sourceFormat rendererOpts+      (sourceHandle source)+      (sinkFile target)++-- | Transform from a file to lazy 'TL.Text'+--+-- @since 0.0.1.0+transformFileToText+  :: MonadUnliftIO m+  => SourceFormat+  -> Renderer.Options+  -> FilePath+  -> m TL.Text+transformFileToText sourceFormat rendererOpts source =+    runResource sourceFormat rendererOpts (sourceFile source) sinkLazyText++-- | Transform from a file to a 'Handle'+--+-- @since 0.0.1.0+transformFileToHandle+  :: MonadUnliftIO m+  => SourceFormat+  -> Renderer.Options+  -> FilePath+  -> Handle+  -> m ()+transformFileToHandle sourceFormat rendererOpts source target =+    runResource sourceFormat rendererOpts+      (sourceFile source)+      (sinkHandle target)++-- | Transform from a file to a file+--+-- @since 0.0.1.0+transformFileToFile+  :: MonadUnliftIO m+  => SourceFormat+  -> Renderer.Options+  -> FilePath+  -> FilePath+  -> m ()+transformFileToFile sourceFormat rendererOpts source target =+    runResource sourceFormat rendererOpts+      (sourceFile source)+      (sinkFile target)++------------------------------------------------------------------------------+-- $LowLevelAPI+--+-- This low-level API provides more control over transforming literate source+-- code, using "Conduit".  The 'transform' transformer implements the+-- transformation, transforming lines of input 'T.Text' to lines of output+-- 'T.Text'.  Various producers are provided to produce lines of input+-- 'T.Text' from common sources, and various consumers are provided to consume+-- lines of output 'T.Text' to common sinks.  These can be used separately if+-- necessary, but some run functions are provided for common usage.+--+-- Example usage:+--+-- @+-- {-\# LANGUAGE OverloadedStrings \#-}+--+-- module Main (main) where+--+-- -- https://hackage.haskell.org/package/base+-- import System.IO (stdout)+--+-- -- https://hackage.haskell.org/package/literatex+-- import qualified LiterateX+-- import qualified LiterateX.Renderer as Renderer+-- import qualified LiterateX.Types.SourceFormat as SourceFormat+--+-- main :: IO ()+-- main = do+--     let demoBS = "..."+--     LiterateX.runIO+--       SourceFormat.LiterateHaskell+--       (Renderer.defaultOptionsFor "haskell")+--       (LiterateX.sourceByteString demoBS)+--       (LiterateX.sinkHandle stdout)+-- @++-- | Run a pure LiterateX transformation+--+-- This function works with the following input line producers:+--+-- * 'sourceString'+-- * 'sourceText'+-- * 'sourceLazyText'+-- * 'sourceByteString'+-- * 'sourceLazyByteString'+--+-- This function works with the following output line consumers:+--+-- * 'sinkString'+-- * 'sinkText'+-- * 'sinkLazyText'+-- * 'sinkByteString'+-- * 'sinkLazyByteString'+--+-- @since 0.0.1.0+runPure+  :: SourceFormat+  -> Renderer.Options+  -> C.ConduitT () T.Text C.Identity ()     -- ^ input line producer+  -> C.ConduitT T.Text C.Void C.Identity r  -- ^ output line consumer+  -> r+runPure sourceFormat rendererOpts source sink = C.runConduitPure $+    source .| transform sourceFormat rendererOpts .| sink++-- | Run a LiterateX transformation using IO+--+-- This function works with the following input line producers:+--+-- * 'sourceString'+-- * 'sourceText'+-- * 'sourceLazyText'+-- * 'sourceByteString'+-- * 'sourceLazyByteString'+-- * 'sourceHandle'+--+-- This function works with the following output line consumers:+--+-- * 'sinkString'+-- * 'sinkText'+-- * 'sinkLazyText'+-- * 'sinkByteString'+-- * 'sinkLazyByteString'+-- * 'sinkHandle'+--+-- @since 0.0.1.0+runIO+  :: MonadIO m+  => SourceFormat+  -> Renderer.Options+  -> C.ConduitT () T.Text m ()     -- ^ input line producer+  -> C.ConduitT T.Text C.Void m r  -- ^ output line consumer+  -> m r+runIO sourceFormat rendererOpts source sink = C.runConduit $+    source .| transform sourceFormat rendererOpts .| sink++-- | Run a LiterateX transformation using resource management+--+-- This function works with the following input line producers:+--+-- * 'sourceString'+-- * 'sourceText'+-- * 'sourceLazyText'+-- * 'sourceByteString'+-- * 'sourceLazyByteString'+-- * 'sourceHandle'+-- * 'sourceFile'+--+-- This function works with the following output line consumers:+--+-- * 'sinkString'+-- * 'sinkText'+-- * 'sinkLazyText'+-- * 'sinkByteString'+-- * 'sinkLazyByteString'+-- * 'sinkHandle'+-- * 'sinkFile'+--+-- @since 0.0.1.0+runResource+  :: MonadUnliftIO m+  => SourceFormat+  -> Renderer.Options+  -> C.ConduitT () T.Text (C.ResourceT m) ()     -- ^ input line producer+  -> C.ConduitT T.Text C.Void (C.ResourceT m) r  -- ^ output line consumer+  -> m r+runResource sourceFormat rendererOpts source sink = C.runConduitRes $+    source .| transform sourceFormat rendererOpts .| sink++------------------------------------------------------------------------------+-- $Producers++-- | Produce input lines from a 'String' source+--+-- @since 0.0.1.0+sourceString+  :: Monad m+  => String+  -> C.ConduitT i T.Text m ()+sourceString = CC.yieldMany . map T.pack . lines++-- | Produce input lines from a 'T.Text' source+--+-- @since 0.0.1.0+sourceText+  :: Monad m+  => T.Text+  -> C.ConduitT i T.Text m ()+sourceText source = C.yield source .| CC.linesUnbounded++-- | Produce input lines from a lazy 'TL.Text' source+--+-- @since 0.0.1.0+sourceLazyText+  :: Monad m+  => TL.Text+  -> C.ConduitT i T.Text m ()+sourceLazyText source = CC.sourceLazy source .| CC.linesUnbounded++-- | Produce input lines from a 'BS.ByteString' source+--+-- @since 0.0.1.0+sourceByteString+  :: Monad m+  => BS.ByteString+  -> C.ConduitT i T.Text m ()+sourceByteString source =+    C.yield source .| CC.linesUnboundedAscii .| decodeUtf8LinesLenient++-- | Produce input lines from a lazy 'BS.ByteString' source+--+-- @since 0.0.1.0+sourceLazyByteString+  :: Monad m+  => BSL.ByteString+  -> C.ConduitT i T.Text m ()+sourceLazyByteString source =+    CC.sourceLazy source .| CC.linesUnboundedAscii .| decodeUtf8LinesLenient++-- | Produce input lines from a 'Handle' source+--+-- @since 0.0.1.0+sourceHandle+  :: MonadIO m+  => Handle+  -> C.ConduitT i T.Text m ()+sourceHandle source =+    CC.sourceHandle source .| CC.linesUnboundedAscii .| decodeUtf8LinesLenient++-- | Produce input lines from a file source+--+-- @since 0.0.1.0+sourceFile+  :: C.MonadResource m+  => FilePath+  -> C.ConduitT i T.Text m ()+sourceFile source =+    CC.sourceFile source .| CC.linesUnboundedAscii .| decodeUtf8LinesLenient++------------------------------------------------------------------------------+-- $Consumers++-- | Consume output lines, returning a 'String'+--+-- @since 0.0.1.0+sinkString+  :: Monad m+  => C.ConduitT T.Text o m String+sinkString = fmap TL.unpack $ CC.unlines .| CC.sinkLazy++-- | Consume output lines, returning 'T.Text'+--+-- @since 0.0.1.0+sinkText+  :: Monad m+  => C.ConduitT T.Text o m T.Text+sinkText = fmap TL.toStrict $ CC.unlines .| CC.sinkLazy++-- | Consume output lines, returning lazy 'T.Text'+--+-- @since 0.0.1.0+sinkLazyText+  :: Monad m+  => C.ConduitT T.Text o m TL.Text+sinkLazyText = CC.unlines .| CC.sinkLazy++-- | Consume output lines, returning a 'BS.ByteString'+--+-- @since 0.0.1.0+sinkByteString+  :: Monad m+  => C.ConduitT T.Text o m BS.ByteString+sinkByteString =+    fmap BSL.toStrict $ CC.unlines .| CC.encodeUtf8 .| CC.sinkLazy++-- | Consume output lines, returning a lazy 'BS.ByteString'+--+-- @since 0.0.1.0+sinkLazyByteString+  :: Monad m+  => C.ConduitT T.Text o m BSL.ByteString+sinkLazyByteString = CC.unlines .| CC.encodeUtf8 .| CC.sinkLazy++-- | Consume output lines, writing to a 'Handle'+--+-- @since 0.0.1.0+sinkHandle+  :: MonadIO m+  => Handle+  -> C.ConduitT T.Text o m ()+sinkHandle target = CC.encodeUtf8 .| CC.unlinesAscii .| CC.sinkHandle target++-- | Consume output lines, writing to a file+--+-- @since 0.0.1.0+sinkFile+  :: C.MonadResource m+  => FilePath+  -> C.ConduitT T.Text o m ()+sinkFile target = CC.encodeUtf8 .| CC.unlinesAscii .| CC.sinkFile target++------------------------------------------------------------------------------+-- $Transformers++-- | Transform input lines to output lines+--+-- @since 0.0.1.0+transform+  :: Monad m+  => SourceFormat+  -> Renderer.Options+  -> C.ConduitT T.Text T.Text m ()+transform sourceFormat rendererOpts+    = Parser.parse sourceFormat+    .| CL.concatMapAccum (\x n -> (n + 1, [(n, x)])) 1+    .| Renderer.render rendererOpts++------------------------------------------------------------------------------+-- $Internal++-- | Decode UTF-8 'BS.ByteString' lines to 'T.Text'+--+-- Note that 'CC.decodeUtf8Lenient' decodes UTF-8 streams and therefore+-- discards empty strings.  This version decodes UTF-8 lines and does not+-- discard empty lines.+decodeUtf8LinesLenient+  :: Monad m+  => C.ConduitT BS.ByteString T.Text m ()+decodeUtf8LinesLenient =+    C.awaitForever $ C.yield . TE.decodeUtf8With TEE.lenientDecode
+ src/LiterateX/Parser.hs view
@@ -0,0 +1,193 @@+------------------------------------------------------------------------------+-- |+-- Module      : LiterateX.Parser+-- Description : source parser+-- Copyright   : Copyright (c) 2021 Travis Cardwell+-- License     : MIT+--+-- This module implements the source parser.+------------------------------------------------------------------------------++{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE RecordWildCards #-}++module LiterateX.Parser+  ( -- * API+    parse+  ) where++-- https://hackage.haskell.org/package/base+import Control.Monad (guard)+import Data.Maybe (fromMaybe)++-- https://hackage.haskell.org/package/conduit+import qualified Data.Conduit as C+import Data.Conduit (ConduitT)++-- https://hackage.haskell.org/package/text+import qualified Data.Text as T+import Data.Text (Text)++-- (literatex)+import LiterateX.Types (SourceFormat, SourceLine)+import qualified LiterateX.Types.SourceFormat as SourceFormat+import qualified LiterateX.Types.SourceLine as SourceLine++------------------------------------------------------------------------------+-- $API++-- | Create a "Conduit" transformer that parses the specified source format+--+-- The transformer consumes lines of the input and produces a 'SourceLine' for+-- each line of input.+--+-- @since 0.0.1.0+parse+  :: Monad m+  => SourceFormat+  -> ConduitT Text SourceLine m ()+parse = parseSourceLines . parserFunctionsFor++------------------------------------------------------------------------------+-- $Internal++-- | Parser functions that determine how input is parsed+data ParserFunctions+  = ParserFunctions+    { isCodeBlank :: !(Text -> Bool)+    , isDocBlank  :: !(Text -> Bool)+    , isRule      :: !(Text -> Bool)+    , getDoc      :: !(Text -> Maybe Text)+    , getCode     :: !(Text -> Text)+    }++------------------------------------------------------------------------------++-- | Get the parser functions for the specified source format+parserFunctionsFor :: SourceFormat -> ParserFunctions+parserFunctionsFor = \case+    SourceFormat.DoubleDash      -> lineCommentParserFunctions '-' 2+    SourceFormat.DoubleSlash     -> lineCommentParserFunctions '/' 2+    SourceFormat.Hash            -> lineCommentParserFunctions '#' 1+    SourceFormat.LiterateHaskell -> literateHaskellParserFunctions+    SourceFormat.Percent         -> lineCommentParserFunctions '%' 1+    SourceFormat.LispSemicolons  -> lispCommentParserFunctions++------------------------------------------------------------------------------++-- | Get parser functions for source with line-based comments+lineCommentParserFunctions+  :: Char  -- ^ comment character+  -> Int   -- ^ number of comment characters to create line comment+  -> ParserFunctions+lineCommentParserFunctions char count = ParserFunctions{..}+  where+    docBlank :: Text+    docBlank = T.pack $ replicate count char++    prefixLen :: Int+    prefixLen = count + 1++    prefix :: Text+    prefix = T.pack $ replicate count char ++ " "++    isCodeBlank :: Text -> Bool+    isCodeBlank = T.null++    isDocBlank :: Text -> Bool+    isDocBlank = (== docBlank)++    isRule :: Text -> Bool+    isRule line = T.length line > count && T.all (== char) line++    getDoc :: Text -> Maybe Text+    getDoc line = do+      let (linePrefix, lineSuffix) = T.splitAt prefixLen line+      guard $ linePrefix == prefix+      pure lineSuffix++    getCode :: Text -> Text+    getCode = id++------------------------------------------------------------------------------++-- | Get parser functions for source with Lisp-style comments+--+-- Lisp-style comments begin with one or more semicolons.+lispCommentParserFunctions :: ParserFunctions+lispCommentParserFunctions = ParserFunctions{..}+  where+    isCodeBlank :: Text -> Bool+    isCodeBlank = T.null++    isDocBlank :: Text -> Bool+    isDocBlank line =+      let len = T.length line+      in  len >= 1 && len <= 4 && T.all (== ';') line++    isRule :: Text -> Bool+    isRule line = T.length line > 4 && T.all (== ';') line++    getDoc :: Text -> Maybe Text+    getDoc line = do+      let (linePrefix, (sep, lineSuffix)) = T.splitAt 1 <$> T.breakOn " " line+      guard $ not (T.null linePrefix) && T.all (== ';') linePrefix+      guard $ sep == " "+      pure lineSuffix++    getCode :: Text -> Text+    getCode = id++------------------------------------------------------------------------------++-- | Get parser functions for parsing literate Haskell+literateHaskellParserFunctions :: ParserFunctions+literateHaskellParserFunctions = ParserFunctions{..}+  where+    isCodeBlank :: Text -> Bool+    isCodeBlank = (== ">")++    isDocBlank :: Text -> Bool+    isDocBlank = T.null++    isRule :: Text -> Bool+    isRule = const False++    getDoc :: Text -> Maybe Text+    getDoc line+      | "> " `T.isPrefixOf` line = Nothing+      | otherwise                = Just line++    getCode :: Text -> Text+    getCode line = fromMaybe line $ T.stripPrefix "> " line++------------------------------------------------------------------------------++-- | Create a "Conduit" transformer for the specified parser functions+--+-- This function produces a 'SourceLine' for each line of input.  A+-- 'SourceLine.Shebang' can only be produced on the first line.  Note that the+-- order that the parser functions are used is significant; the parser+-- functions are written for this order.+parseSourceLines+  :: Monad m+  => ParserFunctions+  -> ConduitT Text SourceLine m ()+parseSourceLines ParserFunctions{..} = do+    mLine <- C.await+    case mLine of+      Just line -> do+        C.yield $ if "#!" `T.isPrefixOf` line+          then SourceLine.Shebang line+          else parse' line+        C.awaitForever $ C.yield . parse'+      Nothing -> return ()+  where+    parse' :: Text -> SourceLine+    parse' line+      | isCodeBlank line = SourceLine.CodeBlank+      | isDocBlank line  = SourceLine.DocBlank+      | isRule line      = SourceLine.Rule+      | otherwise        = case (getDoc line, getCode line) of+          (Just doc, _code) -> SourceLine.Doc doc+          (Nothing,  code)  -> SourceLine.Code code
+ src/LiterateX/Renderer.hs view
@@ -0,0 +1,216 @@+------------------------------------------------------------------------------+-- |+-- Module      : LiterateX.Renderer+-- Description : target renderer+-- Copyright   : Copyright (c) 2021 Travis Cardwell+-- License     : MIT+--+-- This module implements the target renderer.+------------------------------------------------------------------------------++{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE ScopedTypeVariables #-}++module LiterateX.Renderer+  ( -- * Types+    Options(..)+    -- * API+  , defaultOptions+  , defaultOptionsFor+  , render+  ) where++-- https://hackage.haskell.org/package/base+import Control.Monad (replicateM_)++-- https://hackage.haskell.org/package/conduit+import qualified Data.Conduit as C+import Data.Conduit (ConduitT)++-- https://hackage.haskell.org/package/text+import qualified Data.Text as T+import Data.Text (Text)++-- (literatex)+import LiterateX.Types (CodeLanguage, SourceLine, TargetFormat)+import qualified LiterateX.Types.SourceLine as SourceLine+import qualified LiterateX.Types.TargetFormat as TargetFormat++------------------------------------------------------------------------------+-- $Types++-- | Renderer options determine how output is rendered+--+-- @since 0.0.1.0+data Options+  = Options+    { targetFormat    :: !TargetFormat+    , codeLanguage    :: !(Maybe CodeLanguage)+    , ignoreShebang   :: !Bool  -- ^ 'True' to ignore shebangs+    , renderCode      :: !Bool  -- ^ 'True' to render code+    , numberCodeLines :: !Bool  -- ^ 'True' to number code lines+    }+  deriving Show++------------------------------------------------------------------------------+-- $API++-- | Default options+--+-- * @targetFormat@: 'TargetFormat.PandocMarkdown'+-- * @codeLanguage@: 'Nothing'+-- * @ignoreShebang@: 'True'+-- * @renderCode@: 'True'+-- * @numberCodeLines@: 'True'+--+-- @since 0.0.1.0+defaultOptions :: Options+defaultOptions = Options+    { targetFormat    = TargetFormat.PandocMarkdown+    , codeLanguage    = Nothing+    , ignoreShebang   = True+    , renderCode      = True+    , numberCodeLines = True+    }++-- | Default options for the specified code language+--+-- @since 0.0.1.0+defaultOptionsFor :: CodeLanguage -> Options+defaultOptionsFor lang = defaultOptions+    { codeLanguage = Just lang+    }++-- | Create a "Conduit" transformer that renders using the specified options+--+-- The transformer consumes 'SourceLine' values annotated with the line number+-- and produces lines of output.+--+-- @since 0.0.1.0+render+  :: forall m. Monad m+  => Options+  -> ConduitT (Int, SourceLine) Text m ()+render Options{..} = C.await >>= sStart+  where+    -- Start state+    sStart+      :: Maybe (Int, SourceLine)+      -> ConduitT (Int, SourceLine) Text m ()+    sStart (Just (lineNum, sourceLine)) = case sourceLine of+      -- render shebang if enabled, otherwise skip+      SourceLine.Shebang line+        | renderCode && not ignoreShebang -> do+            C.yield $ startShebang lineNum+            C.yield line+            C.yield endCode+            C.await >>= sBlank+        | otherwise -> C.await >>= sStart+      -- start rendering documentation+      SourceLine.Doc line -> do+        C.yield line+        C.await >>= sDoc+      -- start rendering code if enabled, otherwise skip+      SourceLine.Code line+        | renderCode -> do+            C.yield $ startCode lineNum+            C.yield line+            C.await >>= sCode 0+        | otherwise -> C.await >>= sStart+      -- skip blank lines and rules+      _BlankOrRule -> C.await >>= sStart+    sStart Nothing = return ()++    -- Code state+    sCode+      :: Int  -- ^ number of pending (code) blank lines+      -> Maybe (Int, SourceLine)+      -> ConduitT (Int, SourceLine) Text m ()+    sCode !numBlanks (Just (_lineNum, sourceLine)) = case sourceLine of+      -- render any pending blank lines and code+      SourceLine.Code line -> do+        replicateM_ numBlanks $ C.yield T.empty+        C.yield line+        C.await >>= sCode 0+      -- increment number of pending blank lines+      SourceLine.CodeBlank -> C.await >>= sCode (numBlanks + 1)+      -- end code, add blank line, start rendering documentation+      SourceLine.Doc line -> do+        C.yield endCode+        C.yield T.empty+        C.yield line+        C.await >>= sDoc+      -- end code, enter blank state+      SourceLine.DocBlank -> do+        C.yield endCode+        C.await >>= sBlank+      -- end code, enter blank state+      SourceLine.Rule -> do+        C.yield endCode+        C.await >>= sBlank+      -- shebang only possible on first line (seen in start state)+      SourceLine.Shebang _line -> error "impossible: Shebang in sCode"+    sCode _numBlanks Nothing = C.yield endCode++    -- Documentation state+    sDoc+      :: Maybe (Int, SourceLine)+      -> ConduitT (Int, SourceLine) Text m ()+    sDoc (Just (lineNum, sourceLine)) = case sourceLine of+      -- render documentation+      SourceLine.Doc line -> do+        C.yield line+        C.await >>= sDoc+      -- start rendering code if enabled, otherwise enter blank state+      SourceLine.Code line+        | renderCode -> do+            C.yield T.empty+            C.yield $ startCode lineNum+            C.yield line+            C.await >>= sCode 0+        | otherwise -> C.await >>= sBlank+      -- shebang only possible on first line (seen in start state)+      SourceLine.Shebang _line -> error "impossible: Shebang in sDoc"+      -- blank lines and rules transition to blank state+      _BlankOrRule -> C.await >>= sBlank+    sDoc Nothing = return ()++    -- Blank state+    sBlank+      :: Maybe (Int, SourceLine)+      -> ConduitT (Int, SourceLine) Text m ()+    sBlank (Just (lineNum, sourceLine)) = case sourceLine of+      -- start rendering code if enabled, otherwise skip+      SourceLine.Code line+        | renderCode -> do+            C.yield T.empty+            C.yield $ startCode lineNum+            C.yield line+            C.await >>= sCode 0+        | otherwise -> C.await >>= sBlank+      -- render blank line, start rendering documentation+      SourceLine.Doc line -> do+        C.yield T.empty+        C.yield line+        C.await >>= sDoc+      -- shebang only possible on first line (seen in start state)+      SourceLine.Shebang _line  -> error "impossible: Shebang in sBlank"+      -- skip additional blank lines and rules+      _BlankOrRule -> C.await >>= sBlank+    sBlank Nothing = return ()++    -- start code line for code starting at given line number+    startCode :: Int -> Text+    startCode =+      TargetFormat.mkBeginCode targetFormat codeLanguage numberCodeLines++    -- shebang start code line does not use syntax highlighting+    startShebang :: Int -> Text+    startShebang =+      TargetFormat.mkBeginCode targetFormat Nothing numberCodeLines++    -- end code line+    endCode :: Text+    endCode = TargetFormat.mkEndCode targetFormat+{-# ANN render ("HLint: ignore Reduce duplication" :: String) #-}
+ src/LiterateX/SourceDefaults.hs view
@@ -0,0 +1,70 @@+------------------------------------------------------------------------------+-- |+-- Module      : LiterateX.SourceDefaults+-- Description : default options by source extension+-- Copyright   : Copyright (c) 2021 Travis Cardwell+-- License     : MIT+--+-- This module provides some default options for various sources.+------------------------------------------------------------------------------++module LiterateX.SourceDefaults+  ( -- * API+    defaultsFor+  , extensionDefaults+  ) where++-- https://hackage.haskell.org/package/base+import Data.List (find, isSuffixOf)++-- (literatex)+import LiterateX.Types (CodeLanguage, SourceFormat)+import qualified LiterateX.Types.SourceFormat as SourceFormat++------------------------------------------------------------------------------+-- $API++-- | Get the default source format and code language for the given filename+--+-- @since 0.0.1.0+defaultsFor :: FilePath -> Maybe (SourceFormat, CodeLanguage)+defaultsFor path =+  fmap snd . flip find extensionDefaults $ flip isSuffixOf path . fst++------------------------------------------------------------------------------++-- | List of default options for various filename extensions+--+-- @since 0.0.1.0+extensionDefaults :: [(String, (SourceFormat, CodeLanguage))]+extensionDefaults =+    [ (".c",    (SourceFormat.DoubleSlash,     "c"))+    , (".clj",  (SourceFormat.LispSemicolons,  "clojure"))+    , (".css",  (SourceFormat.DoubleSlash,     "css"))+    , (".elm",  (SourceFormat.DoubleDash,      "elm"))+    , (".erl",  (SourceFormat.Percent,         "erlang"))+    , (".ex",   (SourceFormat.Hash,            "elixir"))+    , (".exs",  (SourceFormat.Hash,            "elixir"))+    , (".go",   (SourceFormat.DoubleSlash,     "go"))+    , (".hs",   (SourceFormat.DoubleDash,      "haskell"))+    , (".idr",  (SourceFormat.DoubleDash,      "idris"))+    , (".java", (SourceFormat.DoubleSlash,     "java"))+    , (".js",   (SourceFormat.DoubleSlash,     "javascript"))+    , (".kt",   (SourceFormat.DoubleSlash,     "kotlin"))+    , (".lhs",  (SourceFormat.LiterateHaskell, "haskell"))+    , (".lisp", (SourceFormat.LispSemicolons,  "commonlisp"))+    , (".lua",  (SourceFormat.DoubleDash,      "lua"))+    , (".php",  (SourceFormat.DoubleSlash,     "php"))+    , (".pl",   (SourceFormat.Hash,            "perl"))+    , (".py",   (SourceFormat.Hash,            "python"))+    , (".r",    (SourceFormat.Hash,            "r"))+    , (".rb",   (SourceFormat.Hash,            "ruby"))+    , (".rkt",  (SourceFormat.LispSemicolons,  "scheme"))+    , (".rs",   (SourceFormat.DoubleSlash,     "rust"))+    , (".sc",   (SourceFormat.DoubleSlash,     "scala"))+    , (".scm",  (SourceFormat.LispSemicolons,  "scheme"))+    , (".sh",   (SourceFormat.Hash,            "bash"))+    , (".sql",  (SourceFormat.DoubleDash,      "sql"))+    , (".tex",  (SourceFormat.Percent,         "latex"))+    , (".ts",   (SourceFormat.DoubleSlash,     "typescript"))+    ]
+ src/LiterateX/Types.hs view
@@ -0,0 +1,23 @@+------------------------------------------------------------------------------+-- |+-- Module      : LiterateX.Types+-- Description : type re-exports for convenience+-- Copyright   : Copyright (c) 2021 Travis Cardwell+-- License     : MIT+--+-- The type modules are generally imported qualified, so the types are+-- re-exported by this module for convenience.+------------------------------------------------------------------------------++module LiterateX.Types+  ( module LiterateX.Types.CodeLanguage+  , module LiterateX.Types.SourceFormat+  , module LiterateX.Types.SourceLine+  , module LiterateX.Types.TargetFormat+  ) where++-- (literatex)+import LiterateX.Types.CodeLanguage (CodeLanguage)+import LiterateX.Types.SourceFormat (SourceFormat)+import LiterateX.Types.SourceLine (SourceLine)+import LiterateX.Types.TargetFormat (TargetFormat)
+ src/LiterateX/Types/CodeLanguage.hs view
@@ -0,0 +1,60 @@+------------------------------------------------------------------------------+-- |+-- Module      : LiterateX.Types.CodeLanguage+-- Description : source code language type+-- Copyright   : Copyright (c) 2021 Travis Cardwell+-- License     : MIT+------------------------------------------------------------------------------++module LiterateX.Types.CodeLanguage+  ( -- * Type+    CodeLanguage+  ) where++-- https://hackage.haskell.org/package/base+import Control.Monad (when)+import Data.Bifunctor (first)+import Data.Char (isSpace)+import Data.String (IsString(fromString))++-- https://hackage.haskell.org/package/text+import qualified Data.Text as T+import Data.Text (Text)++-- https://hackage.haskell.org/package/ttc+import qualified Data.TTC as TTC++------------------------------------------------------------------------------+-- $Type++-- | Source code language+--+-- This string is used for syntax highlighting of source code.+--+-- When using Pandoc Markdown, run the following command to see a list of+-- supported languages:+--+-- @+-- \$ pandoc --list-highlight-languages+-- @+--+-- When using GitHub Flavored Markdown, check the following file for supported+-- languages:+--+-- <https://github.com/github/linguist/blob/master/lib/linguist/languages.yml>+--+-- @since 0.0.1.0+newtype CodeLanguage = CodeLanguage { unCodeLanguage :: Text }+  deriving (Eq, Ord, Show)++instance IsString CodeLanguage where+  fromString = either error id . TTC.parse++instance TTC.Parse CodeLanguage where+  parse = TTC.asT $ \t -> first TTC.fromS $ do+    when (T.null t) $ Left "invalid code language: empty"+    when (T.any isSpace t) $ Left "invalid code language: contains whitespace"+    return $ CodeLanguage t++instance TTC.Render CodeLanguage where+  render = TTC.fromT . unCodeLanguage
+ src/LiterateX/Types/SourceFormat.hs view
@@ -0,0 +1,72 @@+------------------------------------------------------------------------------+-- |+-- Module      : LiterateX.Types.SourceFormat+-- Description : source format type+-- Copyright   : Copyright (c) 2021 Travis Cardwell+-- License     : MIT+------------------------------------------------------------------------------++{-# LANGUAGE LambdaCase #-}++module LiterateX.Types.SourceFormat+  ( -- * Type+    SourceFormat(..)+    -- * API+  , describe+  , list+  ) where++-- https://hackage.haskell.org/package/ttc+import qualified Data.TTC as TTC++------------------------------------------------------------------------------+-- $Type++-- | Source format+--+-- This sum type defines the supported source formats.+--+-- @since 0.0.1.0+data SourceFormat+  = DoubleDash       -- ^ \-- comments+  | DoubleSlash      -- ^ // comments+  | Hash             -- ^ # comments+  | LispSemicolons   -- ^ Lisp semicolon comments+  | LiterateHaskell  -- ^ literate Haskell+  | Percent          -- ^ % comments+  deriving (Bounded, Enum, Eq, Ord, Show)++instance TTC.Parse SourceFormat where+  parse = TTC.parseEnum' "source format" True False++instance TTC.Render SourceFormat where+  render = TTC.fromS . \case+    DoubleDash      -> "ddash"+    DoubleSlash     -> "dslash"+    Hash            -> "hash"+    LispSemicolons  -> "lisp"+    LiterateHaskell -> "lhs"+    Percent         -> "percent"++------------------------------------------------------------------------------+-- $API++-- | Get a description of a source format+--+-- @since 0.0.1.0+describe :: SourceFormat -> String+describe = \case+    DoubleDash      -> "-- comments"+    DoubleSlash     -> "// comments"+    Hash            -> "# comments"+    LispSemicolons  -> "Lisp semicolon comments"+    LiterateHaskell -> "literate Haskell"+    Percent         -> "% comments"++------------------------------------------------------------------------------++-- | List of all supported source formats+--+-- @since 0.0.1.0+list :: [SourceFormat]+list = [minBound ..]
+ src/LiterateX/Types/SourceLine.hs view
@@ -0,0 +1,30 @@+------------------------------------------------------------------------------+-- |+-- Module      : LiterateX.Types.SourceLine+-- Description : source line type+-- Copyright   : Copyright (c) 2021 Travis Cardwell+-- License     : MIT+------------------------------------------------------------------------------++module LiterateX.Types.SourceLine+  ( -- * Type+    SourceLine(..)+  ) where++-- https://hackage.haskell.org/package/text+import Data.Text (Text)++------------------------------------------------------------------------------+-- $Type++-- | Parsed source line+--+-- @since 0.0.1.0+data SourceLine+  = Shebang !Text  -- ^ script shebang on first line+  | CodeBlank      -- ^ code blank line+  | DocBlank       -- ^ documentation blank line+  | Rule           -- ^ comment rule used to organize source code+  | Doc !Text      -- ^ documentation line+  | Code !Text     -- ^ code line+  deriving Show
+ src/LiterateX/Types/TargetFormat.hs view
@@ -0,0 +1,115 @@+------------------------------------------------------------------------------+-- |+-- Module      : LiterateX.Types.TargetFormat+-- Description : target format type+-- Copyright   : Copyright (c) 2021 Travis Cardwell+-- License     : MIT+------------------------------------------------------------------------------++{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE OverloadedStrings #-}++module LiterateX.Types.TargetFormat+  ( -- * Type+    TargetFormat(..)+    -- * API+  , describe+  , list+  , mkEndCode+  , mkBeginCode+  ) where++-- https://hackage.haskell.org/package/text+import qualified Data.Text as T+import Data.Text (Text)++-- https://hackage.haskell.org/package/ttc+import qualified Data.TTC as TTC++-- (literatex)+import LiterateX.Types.CodeLanguage (CodeLanguage)++------------------------------------------------------------------------------+-- $Type++-- | Target format+--+-- This sum type defines the supported target formats.+--+-- @since 0.0.1.0+data TargetFormat+  = PandocMarkdown+  | GitHubFlavoredMarkdown+  deriving (Bounded, Enum, Eq, Ord, Show)++instance TTC.Parse TargetFormat where+  parse = TTC.parseEnum' "target format" True False++instance TTC.Render TargetFormat where+  render = TTC.fromS . \case+    PandocMarkdown         -> "pandoc"+    GitHubFlavoredMarkdown -> "github"++------------------------------------------------------------------------------+-- $API++-- | Get a description of a target format+--+-- @since 0.0.1.0+describe :: TargetFormat -> String+describe = \case+    PandocMarkdown         -> "Pandoc Markdown"+    GitHubFlavoredMarkdown -> "GitHub Flavored Markdown"++------------------------------------------------------------------------------++-- | List of all supported target formats+--+-- @since 0.0.1.0+list :: [TargetFormat]+list = [minBound ..]++------------------------------------------------------------------------------++-- | Make line in the target format to end a block of source code+--+-- @since 0.0.1.0+mkEndCode+  :: TargetFormat+  -> Text+mkEndCode _anyFormat = "```"++------------------------------------------------------------------------------++-- | Make line in the target format to begin a block of code+--+-- Note that this function is written to indicate how it is used.  Given the+-- target format, optional code language, and line numbering flag, this+-- function returns a function that takes a line number and returns a line.+--+-- @since 0.0.1.0+mkBeginCode+  :: TargetFormat+  -> Maybe CodeLanguage+  -> Bool           -- ^ 'True' to number code lines+  -> (Int -> Text)  -- ^ make line for code starting at specified line number+mkBeginCode PandocMarkdown (Just lang) True = \lineNum -> T.concat+    [ "``` {.", TTC.render lang, " .numberSource startFrom=\""+    , TTC.renderWithShow lineNum, "\"}"+    ]+mkBeginCode GitHubFlavoredMarkdown (Just lang) True = \lineNum -> T.concat+    [ "``` ", TTC.render lang, " startline=", TTC.renderWithShow lineNum+    ]+mkBeginCode PandocMarkdown (Just lang) False = const $ T.concat+    [ "```", TTC.render lang+    ]+mkBeginCode GitHubFlavoredMarkdown (Just lang) False = const $ T.concat+    [ "``` ", TTC.render lang+    ]+mkBeginCode PandocMarkdown Nothing True = \lineNum -> T.concat+    [ "``` {.numberSource startFrom=\"", TTC.renderWithShow lineNum, "\"}"+    ]+mkBeginCode GitHubFlavoredMarkdown Nothing True = \lineNum -> T.concat+    [ "``` startline=", TTC.renderWithShow lineNum+    ]+mkBeginCode _anyFormat Nothing False = const "```"
+ test/LiterateX/Test/API.hs view
@@ -0,0 +1,367 @@+{-# LANGUAGE OverloadedStrings #-}++module LiterateX.Test.API (tests) where++-- https://hackage.haskell.org/package/bytestring+import qualified Data.ByteString as BS+import qualified Data.ByteString.Lazy as BSL++-- https://hackage.haskell.org/package/filepath+import System.FilePath ((</>))++-- https://hackage.haskell.org/package/tasty+import Test.Tasty (TestTree, testGroup)++-- https://hackage.haskell.org/package/tasty-hunit+import Test.Tasty.HUnit ((@=?), testCase)++-- https://hackage.haskell.org/package/text+import qualified Data.Text as T+import qualified Data.Text.Lazy as TL++-- https://hackage.haskell.org/package/ttc+import qualified Data.TTC as TTC++-- https://hackage.haskell.org/package/unliftio+import qualified UnliftIO.IO as IO+import UnliftIO.Temporary (withSystemTempDirectory)++-- (literatex)+import qualified LiterateX+import qualified LiterateX.Renderer as Renderer+import qualified LiterateX.Types.SourceFormat as SourceFormat+import qualified LiterateX.Types.TargetFormat as TargetFormat++------------------------------------------------------------------------------++sourceS :: String+sourceS = unlines+    [ "#!/usr/bin/env python"+    , ""+    , "# # Example Python Program"+    , "#"+    , "# This is an example program."+    , ""+    , ""+    , "######################################################################"+    , ""+    , "# ## Function `greet`"+    , "#"+    , "# This function prints a greeting."+    , "def greet(name: str) -> None:"+    , "    if name == '':"+    , "        print('Hello!')"+    , "        return"+    , ""+    , "    print(f'Hello {name}!')"+    , ""+    , ""+    , "if __name__ == '__main__':"+    , "    greet()"+    ]++sourceT :: T.Text+sourceT = TTC.fromS sourceS++sourceTL :: TL.Text+sourceTL = TTC.fromS sourceS++sourceBS :: BS.ByteString+sourceBS = TTC.fromS sourceS++sourceBSL :: BSL.ByteString+sourceBSL = TTC.fromS sourceS++------------------------------------------------------------------------------++targetS :: String+targetS = unlines+    [ "# Example Python Program"+    , ""+    , "This is an example program."+    , ""+    , "## Function `greet`"+    , ""+    , "This function prints a greeting."+    , ""+    , "``` {.python .numberSource startFrom=\"13\"}"+    , "def greet(name: str) -> None:"+    , "    if name == '':"+    , "        print('Hello!')"+    , "        return"+    , ""+    , "    print(f'Hello {name}!')"+    , ""+    , ""+    , "if __name__ == '__main__':"+    , "    greet()"+    , "```"+    ]++targetT :: T.Text+targetT = TTC.fromS targetS++targetTL :: TL.Text+targetTL = TTC.fromS targetS++targetBS :: BS.ByteString+targetBS = TTC.fromS targetS++targetBSL :: BSL.ByteString+targetBSL = TTC.fromS targetS++------------------------------------------------------------------------------++withSourceHandle+  :: String  -- ^ content+  -> (IO.Handle -> IO a)+  -> IO a+withSourceHandle content action =+    withSystemTempDirectory "literatex-test-" $ \tmpDirPath -> do+      let sourcePath = tmpDirPath </> "source"+      writeFile sourcePath content+      IO.withFile sourcePath IO.ReadMode action++------------------------------------------------------------------------------++withTargetHandle+  :: (IO.Handle -> IO ())+  -> IO String+withTargetHandle action =+    withSystemTempDirectory "literatex-test-" $ \tmpDirPath -> do+      let targetPath = tmpDirPath </> "target"+      IO.withFile targetPath IO.WriteMode action+      readFile targetPath++------------------------------------------------------------------------------++withSourceFile+  :: String  -- ^ content+  -> (FilePath -> IO a)+  -> IO a+withSourceFile content action =+    withSystemTempDirectory "literatex-test-" $ \tmpDirPath -> do+      let sourcePath = tmpDirPath </> "source"+      writeFile sourcePath content+      action sourcePath++------------------------------------------------------------------------------++withTargetFile+  :: (FilePath -> IO ())+  -> IO String+withTargetFile action =+    withSystemTempDirectory "literatex-test-" $ \tmpDirPath -> do+      let targetPath = tmpDirPath </> "target"+      action targetPath+      readFile targetPath++------------------------------------------------------------------------------++rendererOpts :: Renderer.Options+rendererOpts = Renderer.Options+    { Renderer.targetFormat    = TargetFormat.PandocMarkdown+    , Renderer.codeLanguage    = Just "python"+    , Renderer.ignoreShebang   = True+    , Renderer.renderCode      = True+    , Renderer.numberCodeLines = True+    }++------------------------------------------------------------------------------++testTransformTextToText :: TestTree+testTransformTextToText = testCase "transformTextToText" $+    targetTL @=?+      LiterateX.transformTextToText SourceFormat.Hash rendererOpts sourceTL++------------------------------------------------------------------------------++testTransformTextToHandle :: TestTree+testTransformTextToHandle = testCase "transformTextToHandle" $ do+    result <- withTargetHandle $+      LiterateX.transformTextToHandle SourceFormat.Hash rendererOpts sourceTL+    targetS @=? result++------------------------------------------------------------------------------++testTransformTextToFile :: TestTree+testTransformTextToFile = testCase "transformTextToFile" $ do+    result <- withTargetFile $+      LiterateX.transformTextToFile SourceFormat.Hash rendererOpts sourceTL+    targetS @=? result++------------------------------------------------------------------------------++testTransformHandleToText :: TestTree+testTransformHandleToText = testCase "transformHandleToText" $ do+    result <- withSourceHandle sourceS $+      LiterateX.transformHandleToText SourceFormat.Hash rendererOpts+    targetTL @=? result++------------------------------------------------------------------------------++testTransformHandleToHandle :: TestTree+testTransformHandleToHandle = testCase "transformHandleToHandle" $ do+    result <- withSourceHandle sourceS $ withTargetHandle .+      LiterateX.transformHandleToHandle SourceFormat.Hash rendererOpts+    targetS @=? result++------------------------------------------------------------------------------++testTransformHandleToFile :: TestTree+testTransformHandleToFile = testCase "transformHandleToFile" $ do+    result <- withSourceHandle sourceS $ withTargetFile .+      LiterateX.transformHandleToFile SourceFormat.Hash rendererOpts+    targetS @=? result++------------------------------------------------------------------------------++testTransformFileToText :: TestTree+testTransformFileToText = testCase "transformFileToText" $ do+    result <- withSourceFile sourceS $+      LiterateX.transformFileToText SourceFormat.Hash rendererOpts+    targetTL @=? result++------------------------------------------------------------------------------++testTransformFileToHandle :: TestTree+testTransformFileToHandle = testCase "transformFileToHandle" $ do+    result <- withSourceFile sourceS $ withTargetHandle .+      LiterateX.transformFileToHandle SourceFormat.Hash rendererOpts+    targetS @=? result++------------------------------------------------------------------------------++testTransformFileToFile :: TestTree+testTransformFileToFile = testCase "transformFileToFile" $ do+    result <- withSourceFile sourceS $ withTargetFile .+      LiterateX.transformFileToFile SourceFormat.Hash rendererOpts+    targetS @=? result++------------------------------------------------------------------------------++testRunPure :: TestTree+testRunPure = testGroup "runPure"+    [ testCase "sourceString" $+        targetTL @=? LiterateX.runPure SourceFormat.Hash rendererOpts+          (LiterateX.sourceString sourceS)+          LiterateX.sinkLazyText+    , testCase "sourceText" $+        targetTL @=? LiterateX.runPure SourceFormat.Hash rendererOpts+          (LiterateX.sourceText sourceT)+          LiterateX.sinkLazyText+    , testCase "sourceLazyText" $+        targetTL @=? LiterateX.runPure SourceFormat.Hash rendererOpts+          (LiterateX.sourceLazyText sourceTL)+          LiterateX.sinkLazyText+    , testCase "sourceByteString" $+        targetTL @=? LiterateX.runPure SourceFormat.Hash rendererOpts+          (LiterateX.sourceByteString sourceBS)+          LiterateX.sinkLazyText+    , testCase "sourceLazyByteString" $+        targetTL @=? LiterateX.runPure SourceFormat.Hash rendererOpts+          (LiterateX.sourceLazyByteString sourceBSL)+          LiterateX.sinkLazyText+    , testCase "sinkString" $+        targetS @=? LiterateX.runPure SourceFormat.Hash rendererOpts+          (LiterateX.sourceLazyText sourceTL)+          LiterateX.sinkString+    , testCase "sinkText" $+        targetT @=? LiterateX.runPure SourceFormat.Hash rendererOpts+          (LiterateX.sourceLazyText sourceTL)+          LiterateX.sinkText+    , testCase "sinkLazyText" $+        targetTL @=? LiterateX.runPure SourceFormat.Hash rendererOpts+          (LiterateX.sourceLazyText sourceTL)+          LiterateX.sinkLazyText+    , testCase "sinkByteString" $+        targetBS @=? LiterateX.runPure SourceFormat.Hash rendererOpts+          (LiterateX.sourceLazyText sourceTL)+          LiterateX.sinkByteString+    , testCase "sinkLazyByteString" $+        targetBSL @=? LiterateX.runPure SourceFormat.Hash rendererOpts+          (LiterateX.sourceLazyText sourceTL)+          LiterateX.sinkLazyByteString+    ]++------------------------------------------------------------------------------++testRunIO :: TestTree+testRunIO = testGroup "runIO"+    [ testCase "TextToHandle" $ do+        result <- withTargetHandle $ \target ->+          LiterateX.runIO SourceFormat.Hash rendererOpts+            (LiterateX.sourceLazyText sourceTL)+            (LiterateX.sinkHandle target)+        targetS @=? result+    , testCase "HandleToText" $ do+        result <- withSourceHandle sourceS $ \source ->+          LiterateX.runIO SourceFormat.Hash rendererOpts+            (LiterateX.sourceHandle source)+            LiterateX.sinkLazyText+        targetTL @=? result+    , testCase "HandleToHandle" $ do+        result <- withSourceHandle sourceS $ \source ->+          withTargetHandle $ \target ->+            LiterateX.runIO SourceFormat.Hash rendererOpts+              (LiterateX.sourceHandle source)+              (LiterateX.sinkHandle target)+        targetS @=? result+    ]++------------------------------------------------------------------------------++testRunResource :: TestTree+testRunResource = testGroup "runResource"+    [ testCase "TextToFile" $ do+        result <- withTargetFile $ \target ->+          LiterateX.runResource SourceFormat.Hash rendererOpts+            (LiterateX.sourceLazyText sourceTL)+            (LiterateX.sinkFile target)+        targetS @=? result+    , testCase "HandleToFile" $ do+        result <- withSourceHandle sourceS $ \source ->+          withTargetFile $ \target ->+            LiterateX.runResource SourceFormat.Hash rendererOpts+              (LiterateX.sourceHandle source)+              (LiterateX.sinkFile target)+        targetS @=? result+    , testCase "FileToText" $ do+        result <- withSourceFile sourceS $ \source ->+          LiterateX.runResource SourceFormat.Hash rendererOpts+            (LiterateX.sourceFile source)+            LiterateX.sinkLazyText+        targetTL @=? result+    , testCase "FileToHandle" $ do+        result <- withSourceFile sourceS $ \source ->+          withTargetHandle $ \target ->+            LiterateX.runResource SourceFormat.Hash rendererOpts+              (LiterateX.sourceFile source)+              (LiterateX.sinkHandle target)+        targetS @=? result+    , testCase "FileToFile" $ do+        result <- withSourceFile sourceS $ \source ->+          withTargetFile $ \target ->+            LiterateX.runResource SourceFormat.Hash rendererOpts+              (LiterateX.sourceFile source)+              (LiterateX.sinkFile target)+        targetS @=? result+    ]++------------------------------------------------------------------------------++tests :: TestTree+tests = testGroup "LiterateX.Test.API"+    [ testTransformTextToText+    , testTransformTextToHandle+    , testTransformTextToFile+    , testTransformHandleToText+    , testTransformHandleToHandle+    , testTransformHandleToFile+    , testTransformFileToText+    , testTransformFileToHandle+    , testTransformFileToFile+    , testRunPure+    , testRunIO+    , testRunResource+    ]
+ test/LiterateX/Test/SourceFormat.hs view
@@ -0,0 +1,24 @@+module LiterateX.Test.SourceFormat (tests) where++-- https://hackage.haskell.org/package/tasty+import Test.Tasty (TestTree, testGroup)++-- (literatex:test)+import qualified LiterateX.Test.SourceFormat.DoubleDash+import qualified LiterateX.Test.SourceFormat.DoubleSlash+import qualified LiterateX.Test.SourceFormat.Hash+import qualified LiterateX.Test.SourceFormat.LispSemicolons+import qualified LiterateX.Test.SourceFormat.LiterateHaskell+import qualified LiterateX.Test.SourceFormat.Percent++------------------------------------------------------------------------------++tests :: TestTree+tests = testGroup "LiterateX.Test.SourceFormat"+    [ LiterateX.Test.SourceFormat.DoubleDash.tests+    , LiterateX.Test.SourceFormat.DoubleSlash.tests+    , LiterateX.Test.SourceFormat.Hash.tests+    , LiterateX.Test.SourceFormat.LispSemicolons.tests+    , LiterateX.Test.SourceFormat.LiterateHaskell.tests+    , LiterateX.Test.SourceFormat.Percent.tests+    ]
+ test/LiterateX/Test/SourceFormat/DoubleDash.hs view
@@ -0,0 +1,871 @@+{-# LANGUAGE OverloadedStrings #-}++module LiterateX.Test.SourceFormat.DoubleDash (tests) where++-- https://hackage.haskell.org/package/tasty+import Test.Tasty (TestTree, testGroup)++-- https://hackage.haskell.org/package/tasty-hunit+import Test.Tasty.HUnit ((@=?), testCase)++-- https://hackage.haskell.org/package/text+import qualified Data.Text.Lazy as TL+import Data.Text.Lazy (Text)++-- (literatex)+import qualified LiterateX+import qualified LiterateX.Renderer as Renderer+import qualified LiterateX.Types.SourceFormat as SourceFormat+import qualified LiterateX.Types.TargetFormat as TargetFormat++------------------------------------------------------------------------------++rendererOpts :: Renderer.Options+rendererOpts = Renderer.Options+    { Renderer.targetFormat    = TargetFormat.PandocMarkdown+    , Renderer.codeLanguage    = Just "haskell"+    , Renderer.ignoreShebang   = True+    , Renderer.renderCode      = True+    , Renderer.numberCodeLines = True+    }++run' :: Text -> Renderer.Options -> Text+run' = flip $ LiterateX.transformTextToText SourceFormat.DoubleDash++run :: Text -> Text+run source = run' source rendererOpts++------------------------------------------------------------------------------++sourceStartShebangDoc :: Text+sourceStartShebangDoc = TL.unlines+    [ "#!/usr/bin/env stack"+    , ""+    , "-- This results in warnings!"+    , ""+    , "{- stack script --resolver lts-17.11 -}"+    , ""+    , "module Main (main) where"+    , ""+    , "main :: IO ()"+    , "main = putStrLn \"Hello!\""+    ]++targetStartShebangDocIgnore :: Text+targetStartShebangDocIgnore = TL.unlines+    [ "This results in warnings!"+    , ""+    , "``` {.haskell .numberSource startFrom=\"5\"}"+    , "{- stack script --resolver lts-17.11 -}"+    , ""+    , "module Main (main) where"+    , ""+    , "main :: IO ()"+    , "main = putStrLn \"Hello!\""+    , "```"+    ]++targetStartShebangDocNoIgnore :: Text+targetStartShebangDocNoIgnore = TL.unlines+    [ "``` {.numberSource startFrom=\"1\"}"+    , "#!/usr/bin/env stack"+    , "```"+    , ""+    , "This results in warnings!"+    , ""+    , "``` {.haskell .numberSource startFrom=\"5\"}"+    , "{- stack script --resolver lts-17.11 -}"+    , ""+    , "module Main (main) where"+    , ""+    , "main :: IO ()"+    , "main = putStrLn \"Hello!\""+    , "```"+    ]++targetStartShebangDocNoCode :: Text+targetStartShebangDocNoCode = TL.unlines+    [ "This results in warnings!"+    ]++testStartShebangDoc :: TestTree+testStartShebangDoc = testGroup "startShebangDoc"+    [ testCase "Ignore" $+        targetStartShebangDocIgnore @=? run sourceStartShebangDoc+    , testCase "NoIgnore" $+        targetStartShebangDocNoIgnore @=?+          run' sourceStartShebangDoc rendererOpts+            { Renderer.ignoreShebang = False+            }+    , testCase "NoCode" $+        targetStartShebangDocNoCode @=?+          run' sourceStartShebangDoc rendererOpts+            { Renderer.ignoreShebang = False+            , Renderer.renderCode    = False+            }+    ]++------------------------------------------------------------------------------++sourceStartShebangCode :: Text+sourceStartShebangCode = TL.unlines+    [ "#!/usr/bin/env stack"+    , "{- stack script --resolver lts-17.11 -}"+    , ""+    , "module Main (main) where"+    , ""+    , "main :: IO ()"+    , "main = putStrLn \"Hello!\""+    , ""+    , "-- Test"+    ]++targetStartShebangCodeIgnore :: Text+targetStartShebangCodeIgnore = TL.unlines+    [ "``` {.haskell .numberSource startFrom=\"2\"}"+    , "{- stack script --resolver lts-17.11 -}"+    , ""+    , "module Main (main) where"+    , ""+    , "main :: IO ()"+    , "main = putStrLn \"Hello!\""+    , "```"+    , ""+    , "Test"+    ]++targetStartShebangCodeNoIgnore :: Text+targetStartShebangCodeNoIgnore = TL.unlines+    [ "``` {.numberSource startFrom=\"1\"}"+    , "#!/usr/bin/env stack"+    , "```"+    , ""+    , "``` {.haskell .numberSource startFrom=\"2\"}"+    , "{- stack script --resolver lts-17.11 -}"+    , ""+    , "module Main (main) where"+    , ""+    , "main :: IO ()"+    , "main = putStrLn \"Hello!\""+    , "```"+    , ""+    , "Test"+    ]++targetStartShebangCodeNoCode :: Text+targetStartShebangCodeNoCode = TL.unlines+    [ "Test"+    ]++testStartShebangCode :: TestTree+testStartShebangCode = testGroup "startShebangCode"+    [ testCase "Ignore" $+        targetStartShebangCodeIgnore @=? run sourceStartShebangCode+    , testCase "NoIgnore" $+        targetStartShebangCodeNoIgnore @=?+          run' sourceStartShebangCode rendererOpts+            { Renderer.ignoreShebang = False+            }+    , testCase "NoCode" $+        targetStartShebangCodeNoCode @=?+          run' sourceStartShebangCode rendererOpts+            { Renderer.ignoreShebang = False+            , Renderer.renderCode    = False+            }+    ]++------------------------------------------------------------------------------++sourceStartDoc :: Text+sourceStartDoc = TL.unlines+    [ "-- Test"+    , ""+    , "module Main (main) where"+    , ""+    , "main :: IO ()"+    , "main = putStrLn \"Hello!\""+    ]++targetStartDoc :: Text+targetStartDoc = TL.unlines+    [ "Test"+    , ""+    , "``` {.haskell .numberSource startFrom=\"3\"}"+    , "module Main (main) where"+    , ""+    , "main :: IO ()"+    , "main = putStrLn \"Hello!\""+    , "```"+    ]++testStartDoc :: TestTree+testStartDoc = testCase "startDoc" $+    targetStartDoc @=? run sourceStartDoc++------------------------------------------------------------------------------++sourceStartCode :: Text+sourceStartCode = TL.unlines+    [ "module Main (main) where"+    , ""+    , "main :: IO ()"+    , "main = putStrLn \"Hello!\""+    , ""+    , "-- Test"+    ]++targetStartCode :: Text+targetStartCode = TL.unlines+    [ "``` {.haskell .numberSource startFrom=\"1\"}"+    , "module Main (main) where"+    , ""+    , "main :: IO ()"+    , "main = putStrLn \"Hello!\""+    , "```"+    , ""+    , "Test"+    ]++testStartCode :: TestTree+testStartCode = testCase "startCode" $+    targetStartCode @=? run sourceStartCode++------------------------------------------------------------------------------++sourceStartDocBlank :: Text+sourceStartDocBlank = TL.unlines+    [ "--"+    , "-- Test"+    , ""+    , "module Main (main) where"+    , ""+    , "main :: IO ()"+    , "main = putStrLn \"Hello!\""+    ]++targetStartDocBlank :: Text+targetStartDocBlank = TL.unlines+    [ "Test"+    , ""+    , "``` {.haskell .numberSource startFrom=\"4\"}"+    , "module Main (main) where"+    , ""+    , "main :: IO ()"+    , "main = putStrLn \"Hello!\""+    , "```"+    ]++testStartDocBlank :: TestTree+testStartDocBlank = testCase "startDocBlank" $+    targetStartDocBlank @=? run sourceStartDocBlank++------------------------------------------------------------------------------++sourceStartCodeBlank :: Text+sourceStartCodeBlank = TL.unlines+    [ ""+    , "module Main (main) where"+    , ""+    , "main :: IO ()"+    , "main = putStrLn \"Hello!\""+    , ""+    , "-- Test"+    ]++targetStartCodeBlank :: Text+targetStartCodeBlank = TL.unlines+    [ "``` {.haskell .numberSource startFrom=\"2\"}"+    , "module Main (main) where"+    , ""+    , "main :: IO ()"+    , "main = putStrLn \"Hello!\""+    , "```"+    , ""+    , "Test"+    ]++testStartCodeBlank :: TestTree+testStartCodeBlank = testCase "startCodeBlank" $+    targetStartCodeBlank @=? run sourceStartCodeBlank++------------------------------------------------------------------------------++sourceStartRuleDoc :: Text+sourceStartRuleDoc = TL.unlines+    [ "----------------------------------------------------------------------"+    , "-- Test"+    , ""+    , "module Main (main) where"+    , ""+    , "main :: IO ()"+    , "main = putStrLn \"Hello!\""+    ]++targetStartRuleDoc :: Text+targetStartRuleDoc = TL.unlines+    [ "Test"+    , ""+    , "``` {.haskell .numberSource startFrom=\"4\"}"+    , "module Main (main) where"+    , ""+    , "main :: IO ()"+    , "main = putStrLn \"Hello!\""+    , "```"+    ]++testStartRuleDoc :: TestTree+testStartRuleDoc = testCase "startRuleDoc" $+    targetStartRuleDoc @=? run sourceStartRuleDoc++------------------------------------------------------------------------------++sourceStartRuleCode :: Text+sourceStartRuleCode = TL.unlines+    [ "----------------------------------------------------------------------"+    , ""+    , "module Main (main) where"+    , ""+    , "main :: IO ()"+    , "main = putStrLn \"Hello!\""+    , ""+    , "-- Test"+    ]++targetStartRuleCode :: Text+targetStartRuleCode = TL.unlines+    [ "``` {.haskell .numberSource startFrom=\"3\"}"+    , "module Main (main) where"+    , ""+    , "main :: IO ()"+    , "main = putStrLn \"Hello!\""+    , "```"+    , ""+    , "Test"+    ]++testStartRuleCode :: TestTree+testStartRuleCode = testCase "startRuleCode" $+    targetStartRuleCode @=? run sourceStartRuleCode++------------------------------------------------------------------------------++sourceCodeDoc :: Text+sourceCodeDoc = TL.unlines+    [ "module Main (main) where"+    , ""+    , "main :: IO ()"+    , "main = putStrLn \"Hello!\""+    , "-- Test"+    ]++targetCodeDoc :: Text+targetCodeDoc = TL.unlines+    [ "``` {.haskell .numberSource startFrom=\"1\"}"+    , "module Main (main) where"+    , ""+    , "main :: IO ()"+    , "main = putStrLn \"Hello!\""+    , "```"+    , ""+    , "Test"+    ]++testCodeDoc :: TestTree+testCodeDoc = testCase "codeDoc" $+    targetCodeDoc @=? run sourceCodeDoc++------------------------------------------------------------------------------++sourceCodeCodeBlanksCode :: Text+sourceCodeCodeBlanksCode = TL.unlines+    [ "module Main (main) where"+    , ""+    , ""+    , "main :: IO ()"+    , "main = putStrLn \"Hello!\""+    ]++targetCodeCodeBlanksCode :: Text+targetCodeCodeBlanksCode = TL.unlines+    [ "``` {.haskell .numberSource startFrom=\"1\"}"+    , "module Main (main) where"+    , ""+    , ""+    , "main :: IO ()"+    , "main = putStrLn \"Hello!\""+    , "```"+    ]++testCodeCodeBlanksCode :: TestTree+testCodeCodeBlanksCode = testCase "codeCodeBlanksCode" $+    targetCodeCodeBlanksCode @=? run sourceCodeCodeBlanksCode++------------------------------------------------------------------------------++sourceCodeCodeBlanksDoc :: Text+sourceCodeCodeBlanksDoc = TL.unlines+    [ "module Main (main) where"+    , ""+    , "main :: IO ()"+    , "main = putStrLn \"Hello!\""+    , ""+    , ""+    , "-- Test"+    ]++targetCodeCodeBlanksDoc :: Text+targetCodeCodeBlanksDoc = TL.unlines+    [ "``` {.haskell .numberSource startFrom=\"1\"}"+    , "module Main (main) where"+    , ""+    , "main :: IO ()"+    , "main = putStrLn \"Hello!\""+    , "```"+    , ""+    , "Test"+    ]++testCodeCodeBlanksDoc :: TestTree+testCodeCodeBlanksDoc = testCase "codeCodeBlanksDoc" $+    targetCodeCodeBlanksDoc @=? run sourceCodeCodeBlanksDoc++------------------------------------------------------------------------------++sourceCodeDocBlanksDoc :: Text+sourceCodeDocBlanksDoc = TL.unlines+    [ "module Main (main) where"+    , ""+    , "main :: IO ()"+    , "main = putStrLn \"Hello!\""+    , "--"+    , "--"+    , "-- Test"+    ]++targetCodeDocBlanksDoc :: Text+targetCodeDocBlanksDoc = TL.unlines+    [ "``` {.haskell .numberSource startFrom=\"1\"}"+    , "module Main (main) where"+    , ""+    , "main :: IO ()"+    , "main = putStrLn \"Hello!\""+    , "```"+    , ""+    , "Test"+    ]++testCodeDocBlanksDoc :: TestTree+testCodeDocBlanksDoc = testCase "codeDocBlanksDoc" $+    targetCodeDocBlanksDoc @=? run sourceCodeDocBlanksDoc++------------------------------------------------------------------------------++sourceCodeDocBlanksCode :: Text+sourceCodeDocBlanksCode = TL.unlines+    [ "module Main (main) where"+    , "--"+    , "--"+    , "main :: IO ()"+    , "main = putStrLn \"Hello!\""+    ]++targetCodeDocBlanksCode :: Text+targetCodeDocBlanksCode = TL.unlines+    [ "``` {.haskell .numberSource startFrom=\"1\"}"+    , "module Main (main) where"+    , "```"+    , ""+    , "``` {.haskell .numberSource startFrom=\"4\"}"+    , "main :: IO ()"+    , "main = putStrLn \"Hello!\""+    , "```"+    ]++testCodeDocBlanksCode :: TestTree+testCodeDocBlanksCode = testCase "codeDocBlanksCode" $+    targetCodeDocBlanksCode @=? run sourceCodeDocBlanksCode++------------------------------------------------------------------------------++sourceCodeRuleCode :: Text+sourceCodeRuleCode = TL.unlines+    [ "module Main (main) where"+    , "----------------------------------------------------------------------"+    , "main :: IO ()"+    , "main = putStrLn \"Hello!\""+    ]++targetCodeRuleCode :: Text+targetCodeRuleCode = TL.unlines+    [ "``` {.haskell .numberSource startFrom=\"1\"}"+    , "module Main (main) where"+    , "```"+    , ""+    , "``` {.haskell .numberSource startFrom=\"3\"}"+    , "main :: IO ()"+    , "main = putStrLn \"Hello!\""+    , "```"+    ]++testCodeRuleCode :: TestTree+testCodeRuleCode = testCase "codeRuleCode" $+    targetCodeRuleCode @=? run sourceCodeRuleCode++------------------------------------------------------------------------------++sourceCodeRuleDoc :: Text+sourceCodeRuleDoc = TL.unlines+    [ "module Main (main) where"+    , ""+    , "main :: IO ()"+    , "main = putStrLn \"Hello!\""+    , "----------------------------------------------------------------------"+    , "-- Test"+    ]++targetCodeRuleDoc :: Text+targetCodeRuleDoc = TL.unlines+    [ "``` {.haskell .numberSource startFrom=\"1\"}"+    , "module Main (main) where"+    , ""+    , "main :: IO ()"+    , "main = putStrLn \"Hello!\""+    , "```"+    , ""+    , "Test"+    ]++testCodeRuleDoc :: TestTree+testCodeRuleDoc = testCase "codeRuleDoc" $+    targetCodeRuleDoc @=? run sourceCodeRuleDoc++------------------------------------------------------------------------------++sourceDocCode :: Text+sourceDocCode = TL.unlines+    [ "-- Test"+    , "module Main (main) where"+    , ""+    , "main :: IO ()"+    , "main = putStrLn \"Hello!\""+    ]++targetDocCode :: Text+targetDocCode = TL.unlines+    [ "Test"+    , ""+    , "``` {.haskell .numberSource startFrom=\"2\"}"+    , "module Main (main) where"+    , ""+    , "main :: IO ()"+    , "main = putStrLn \"Hello!\""+    , "```"+    ]++testDocCode :: TestTree+testDocCode = testCase "docCode" $+    targetDocCode @=? run sourceDocCode++------------------------------------------------------------------------------++sourceDocDocBlanksDoc :: Text+sourceDocDocBlanksDoc = TL.unlines+    [ "-- one"+    , "--"+    , "--"+    , "-- two"+    ]++targetDocDocBlanksDoc :: Text+targetDocDocBlanksDoc = TL.unlines+    [ "one"+    , ""+    , "two"+    ]++testDocDocBlanksDoc :: TestTree+testDocDocBlanksDoc = testCase "docDocBlanksDoc" $+    targetDocDocBlanksDoc @=? run sourceDocDocBlanksDoc++------------------------------------------------------------------------------++sourceDocDocBlanksCode :: Text+sourceDocDocBlanksCode = TL.unlines+    [ "-- Test"+    , "--"+    , "--"+    , "module Main (main) where"+    , ""+    , "main :: IO ()"+    , "main = putStrLn \"Hello!\""+    ]++targetDocDocBlanksCode :: Text+targetDocDocBlanksCode = TL.unlines+    [ "Test"+    , ""+    , "``` {.haskell .numberSource startFrom=\"4\"}"+    , "module Main (main) where"+    , ""+    , "main :: IO ()"+    , "main = putStrLn \"Hello!\""+    , "```"+    ]++testDocDocBlanksCode :: TestTree+testDocDocBlanksCode = testCase "docDocBlanksCode" $+    targetDocDocBlanksCode @=? run sourceDocDocBlanksCode++------------------------------------------------------------------------------++sourceDocCodeBlanksCode :: Text+sourceDocCodeBlanksCode = TL.unlines+    [ "-- Test"+    , ""+    , ""+    , "module Main (main) where"+    , ""+    , "main :: IO ()"+    , "main = putStrLn \"Hello!\""+    ]++targetDocCodeBlanksCode :: Text+targetDocCodeBlanksCode = TL.unlines+    [ "Test"+    , ""+    , "``` {.haskell .numberSource startFrom=\"4\"}"+    , "module Main (main) where"+    , ""+    , "main :: IO ()"+    , "main = putStrLn \"Hello!\""+    , "```"+    ]++testDocCodeBlanksCode :: TestTree+testDocCodeBlanksCode = testCase "docCodeBlanksCode" $+    targetDocCodeBlanksCode @=? run sourceDocCodeBlanksCode++------------------------------------------------------------------------------++sourceDocCodeBlanksDoc :: Text+sourceDocCodeBlanksDoc = TL.unlines+    [ "-- one"+    , ""+    , ""+    , "-- two"+    ]++targetDocCodeBlanksDoc :: Text+targetDocCodeBlanksDoc = TL.unlines+    [ "one"+    , ""+    , "two"+    ]++testDocCodeBlanksDoc :: TestTree+testDocCodeBlanksDoc = testCase "docCodeBlanksDoc" $+    targetDocCodeBlanksDoc @=? run sourceDocCodeBlanksDoc++------------------------------------------------------------------------------++sourceDocRuleDoc :: Text+sourceDocRuleDoc = TL.unlines+    [ "-- one"+    , "----------------------------------------------------------------------"+    , "-- two"+    ]++targetDocRuleDoc :: Text+targetDocRuleDoc = TL.unlines+    [ "one"+    , ""+    , "two"+    ]++testDocRuleDoc :: TestTree+testDocRuleDoc = testCase "docRuleDoc" $+    targetDocRuleDoc @=? run sourceDocRuleDoc++------------------------------------------------------------------------------++sourceDocRuleCode :: Text+sourceDocRuleCode = TL.unlines+    [ "-- Test"+    , "----------------------------------------------------------------------"+    , "module Main (main) where"+    , ""+    , "main :: IO ()"+    , "main = putStrLn \"Hello!\""+    ]++targetDocRuleCode :: Text+targetDocRuleCode = TL.unlines+    [ "Test"+    , ""+    , "``` {.haskell .numberSource startFrom=\"3\"}"+    , "module Main (main) where"+    , ""+    , "main :: IO ()"+    , "main = putStrLn \"Hello!\""+    , "```"+    ]++testDocRuleCode :: TestTree+testDocRuleCode = testCase "docRuleCode" $+    targetDocRuleCode @=? run sourceDocRuleCode++------------------------------------------------------------------------------++sourceEndDoc :: Text+sourceEndDoc = TL.unlines+    [ "-- Test"+    ]++targetEndDoc :: Text+targetEndDoc = TL.unlines+    [ "Test"+    ]++testEndDoc :: TestTree+testEndDoc = testCase "endDoc" $+    targetEndDoc @=? run sourceEndDoc++------------------------------------------------------------------------------++sourceEndDocBlanks :: Text+sourceEndDocBlanks = TL.unlines+    [ "--"+    , "--"+    ]++targetEndDocBlanks :: Text+targetEndDocBlanks = ""++testEndDocBlanks :: TestTree+testEndDocBlanks = testCase "endDocBlanks" $+    targetEndDocBlanks @=? run sourceEndDocBlanks++------------------------------------------------------------------------------++sourceEndCode :: Text+sourceEndCode = TL.unlines+    [ "module Main (main) where"+    , ""+    , "main :: IO ()"+    , "main = putStrLn \"Hello!\""+    ]++targetEndCode :: Text+targetEndCode = TL.unlines+    [ "``` {.haskell .numberSource startFrom=\"1\"}"+    , "module Main (main) where"+    , ""+    , "main :: IO ()"+    , "main = putStrLn \"Hello!\""+    , "```"+    ]++testEndCode :: TestTree+testEndCode = testCase "endCode" $+    targetEndCode @=? run sourceEndCode++------------------------------------------------------------------------------++sourceEndCodeBlanks :: Text+sourceEndCodeBlanks = TL.unlines+    [ "module Main (main) where"+    , ""+    , "main :: IO ()"+    , "main = putStrLn \"Hello!\""+    , ""+    , ""+    ]++targetEndCodeBlanks :: Text+targetEndCodeBlanks = TL.unlines+    [ "``` {.haskell .numberSource startFrom=\"1\"}"+    , "module Main (main) where"+    , ""+    , "main :: IO ()"+    , "main = putStrLn \"Hello!\""+    , "```"+    ]++testEndCodeBlanks :: TestTree+testEndCodeBlanks = testCase "endCodeBlanks" $+    targetEndCodeBlanks @=? run sourceEndCodeBlanks++------------------------------------------------------------------------------++sourceEmpty :: Text+sourceEmpty = ""++sourceOnlyRule :: Text+sourceOnlyRule = "-----------------------------------------------------------"++sourceOnlyDocBlanks :: Text+sourceOnlyDocBlanks = TL.unlines+    [ "--"+    , "--"+    ]++sourceOnlyCodeBlanks :: Text+sourceOnlyCodeBlanks = TL.unlines+    [ ""+    , ""+    ]++targetEmpty :: Text+targetEmpty = ""++testEmpty :: TestTree+testEmpty = testCase "empty" $+    targetEmpty @=? run sourceEmpty++testOnlyRule :: TestTree+testOnlyRule = testCase "onlyRule" $+    targetEmpty @=? run sourceOnlyRule++testOnlyDocBlanks :: TestTree+testOnlyDocBlanks = testCase "onlyDocBlanks" $+    targetEmpty @=? run sourceOnlyDocBlanks++testOnlyCodeBlanks :: TestTree+testOnlyCodeBlanks = testCase "onlyCodeBlanks" $+    targetEmpty @=? run sourceOnlyCodeBlanks++------------------------------------------------------------------------------++tests :: TestTree+tests = testGroup "DoubleDash"+    [ testStartShebangDoc+    , testStartShebangCode+    , testStartDoc+    , testStartCode+    , testStartDocBlank+    , testStartCodeBlank+    , testStartRuleDoc+    , testStartRuleCode+    , testCodeDoc+    , testCodeCodeBlanksCode+    , testCodeCodeBlanksDoc+    , testCodeDocBlanksDoc+    , testCodeDocBlanksCode+    , testCodeRuleCode+    , testCodeRuleDoc+    , testDocCode+    , testDocDocBlanksDoc+    , testDocDocBlanksCode+    , testDocCodeBlanksCode+    , testDocCodeBlanksDoc+    , testDocRuleDoc+    , testDocRuleCode+    , testEndDoc+    , testEndDocBlanks+    , testEndCode+    , testEndCodeBlanks+    , testEmpty+    , testOnlyRule+    , testOnlyDocBlanks+    , testOnlyCodeBlanks+    ]
+ test/LiterateX/Test/SourceFormat/DoubleSlash.hs view
@@ -0,0 +1,827 @@+{-# LANGUAGE OverloadedStrings #-}++module LiterateX.Test.SourceFormat.DoubleSlash (tests) where++-- https://hackage.haskell.org/package/tasty+import Test.Tasty (TestTree, testGroup)++-- https://hackage.haskell.org/package/tasty-hunit+import Test.Tasty.HUnit ((@=?), testCase)++-- https://hackage.haskell.org/package/text+import qualified Data.Text.Lazy as TL+import Data.Text.Lazy (Text)++-- (literatex)+import qualified LiterateX+import qualified LiterateX.Renderer as Renderer+import qualified LiterateX.Types.SourceFormat as SourceFormat+import qualified LiterateX.Types.TargetFormat as TargetFormat++------------------------------------------------------------------------------++rendererOpts :: Renderer.Options+rendererOpts = Renderer.Options+    { Renderer.targetFormat    = TargetFormat.PandocMarkdown+    , Renderer.codeLanguage    = Just "rust"+    , Renderer.ignoreShebang   = True+    , Renderer.renderCode      = True+    , Renderer.numberCodeLines = True+    }++run' :: Text -> Renderer.Options -> Text+run' = flip $ LiterateX.transformTextToText SourceFormat.DoubleSlash++run :: Text -> Text+run source = run' source rendererOpts++------------------------------------------------------------------------------++sourceStartShebangDoc :: Text+sourceStartShebangDoc = TL.unlines+    [ "#!/usr/bin/env run-cargo-script"+    , ""+    , "// Test"+    , ""+    , "fn main() {"+    , "    println!(\"Hello!\");"+    , "}"+    ]++targetStartShebangDocIgnore :: Text+targetStartShebangDocIgnore = TL.unlines+    [ "Test"+    , ""+    , "``` {.rust .numberSource startFrom=\"5\"}"+    , "fn main() {"+    , "    println!(\"Hello!\");"+    , "}"+    , "```"+    ]++targetStartShebangDocNoIgnore :: Text+targetStartShebangDocNoIgnore = TL.unlines+    [ "``` {.numberSource startFrom=\"1\"}"+    , "#!/usr/bin/env run-cargo-script"+    , "```"+    , ""+    , "Test"+    , ""+    , "``` {.rust .numberSource startFrom=\"5\"}"+    , "fn main() {"+    , "    println!(\"Hello!\");"+    , "}"+    , "```"+    ]++targetStartShebangDocNoCode :: Text+targetStartShebangDocNoCode = TL.unlines+    [ "Test"+    ]++testStartShebangDoc :: TestTree+testStartShebangDoc = testGroup "startShebangDoc"+    [ testCase "Ignore" $+        targetStartShebangDocIgnore @=? run sourceStartShebangDoc+    , testCase "NoIgnore" $+        targetStartShebangDocNoIgnore @=?+          run' sourceStartShebangDoc rendererOpts+            { Renderer.ignoreShebang = False+            }+    , testCase "NoCode" $+        targetStartShebangDocNoCode @=?+          run' sourceStartShebangDoc rendererOpts+            { Renderer.ignoreShebang = False+            , Renderer.renderCode    = False+            }+    ]++------------------------------------------------------------------------------++sourceStartShebangCode :: Text+sourceStartShebangCode = TL.unlines+    [ "#!/usr/bin/env run-cargo-script"+    , "fn main() {"+    , "    println!(\"Hello!\");"+    , "}"+    , ""+    , "// Test"+    ]++targetStartShebangCodeIgnore :: Text+targetStartShebangCodeIgnore = TL.unlines+    [ "``` {.rust .numberSource startFrom=\"2\"}"+    , "fn main() {"+    , "    println!(\"Hello!\");"+    , "}"+    , "```"+    , ""+    , "Test"+    ]++targetStartShebangCodeNoIgnore :: Text+targetStartShebangCodeNoIgnore = TL.unlines+    [ "``` {.numberSource startFrom=\"1\"}"+    , "#!/usr/bin/env run-cargo-script"+    , "```"+    , ""+    , "``` {.rust .numberSource startFrom=\"2\"}"+    , "fn main() {"+    , "    println!(\"Hello!\");"+    , "}"+    , "```"+    , ""+    , "Test"+    ]++targetStartShebangCodeNoCode :: Text+targetStartShebangCodeNoCode = TL.unlines+    [ "Test"+    ]++testStartShebangCode :: TestTree+testStartShebangCode = testGroup "startShebangCode"+    [ testCase "Ignore" $+        targetStartShebangCodeIgnore @=? run sourceStartShebangCode+    , testCase "NoIgnore" $+        targetStartShebangCodeNoIgnore @=?+          run' sourceStartShebangCode rendererOpts+            { Renderer.ignoreShebang = False+            }+    , testCase "NoCode" $+        targetStartShebangCodeNoCode @=?+          run' sourceStartShebangCode rendererOpts+            { Renderer.ignoreShebang = False+            , Renderer.renderCode    = False+            }+    ]++------------------------------------------------------------------------------++sourceStartDoc :: Text+sourceStartDoc = TL.unlines+    [ "// Test"+    , ""+    , "fn main() {"+    , "    println!(\"Hello!\");"+    , "}"+    ]++targetStartDoc :: Text+targetStartDoc = TL.unlines+    [ "Test"+    , ""+    , "``` {.rust .numberSource startFrom=\"3\"}"+    , "fn main() {"+    , "    println!(\"Hello!\");"+    , "}"+    , "```"+    ]++testStartDoc :: TestTree+testStartDoc = testCase "startDoc" $+    targetStartDoc @=? run sourceStartDoc++------------------------------------------------------------------------------++sourceStartCode :: Text+sourceStartCode = TL.unlines+    [ "fn main() {"+    , "    println!(\"Hello!\");"+    , "}"+    , ""+    , "// Test"+    ]++targetStartCode :: Text+targetStartCode = TL.unlines+    [ "``` {.rust .numberSource startFrom=\"1\"}"+    , "fn main() {"+    , "    println!(\"Hello!\");"+    , "}"+    , "```"+    , ""+    , "Test"+    ]++testStartCode :: TestTree+testStartCode = testCase "startCode" $+    targetStartCode @=? run sourceStartCode++------------------------------------------------------------------------------++sourceStartDocBlank :: Text+sourceStartDocBlank = TL.unlines+    [ "//"+    , "// Test"+    , ""+    , "fn main() {"+    , "    println!(\"Hello!\");"+    , "}"+    ]++targetStartDocBlank :: Text+targetStartDocBlank = TL.unlines+    [ "Test"+    , ""+    , "``` {.rust .numberSource startFrom=\"4\"}"+    , "fn main() {"+    , "    println!(\"Hello!\");"+    , "}"+    , "```"+    ]++testStartDocBlank :: TestTree+testStartDocBlank = testCase "startDocBlank" $+    targetStartDocBlank @=? run sourceStartDocBlank++------------------------------------------------------------------------------++sourceStartCodeBlank :: Text+sourceStartCodeBlank = TL.unlines+    [ ""+    , "fn main() {"+    , "    println!(\"Hello!\");"+    , "}"+    , ""+    , "// Test"+    ]++targetStartCodeBlank :: Text+targetStartCodeBlank = TL.unlines+    [ "``` {.rust .numberSource startFrom=\"2\"}"+    , "fn main() {"+    , "    println!(\"Hello!\");"+    , "}"+    , "```"+    , ""+    , "Test"+    ]++testStartCodeBlank :: TestTree+testStartCodeBlank = testCase "startCodeBlank" $+    targetStartCodeBlank @=? run sourceStartCodeBlank++------------------------------------------------------------------------------++sourceStartRuleDoc :: Text+sourceStartRuleDoc = TL.unlines+    [ "//////////////////////////////////////////////////////////////////////"+    , "// Test"+    , ""+    , "fn main() {"+    , "    println!(\"Hello!\");"+    , "}"+    ]++targetStartRuleDoc :: Text+targetStartRuleDoc = TL.unlines+    [ "Test"+    , ""+    , "``` {.rust .numberSource startFrom=\"4\"}"+    , "fn main() {"+    , "    println!(\"Hello!\");"+    , "}"+    , "```"+    ]++testStartRuleDoc :: TestTree+testStartRuleDoc = testCase "startRuleDoc" $+    targetStartRuleDoc @=? run sourceStartRuleDoc++------------------------------------------------------------------------------++sourceStartRuleCode :: Text+sourceStartRuleCode = TL.unlines+    [ "//////////////////////////////////////////////////////////////////////"+    , ""+    , "fn main() {"+    , "    println!(\"Hello!\");"+    , "}"+    , ""+    , "// Test"+    ]++targetStartRuleCode :: Text+targetStartRuleCode = TL.unlines+    [ "``` {.rust .numberSource startFrom=\"3\"}"+    , "fn main() {"+    , "    println!(\"Hello!\");"+    , "}"+    , "```"+    , ""+    , "Test"+    ]++testStartRuleCode :: TestTree+testStartRuleCode = testCase "startRuleCode" $+    targetStartRuleCode @=? run sourceStartRuleCode++------------------------------------------------------------------------------++sourceCodeDoc :: Text+sourceCodeDoc = TL.unlines+    [ "fn main() {"+    , "    println!(\"Hello!\");"+    , "}"+    , "// Test"+    ]++targetCodeDoc :: Text+targetCodeDoc = TL.unlines+    [ "``` {.rust .numberSource startFrom=\"1\"}"+    , "fn main() {"+    , "    println!(\"Hello!\");"+    , "}"+    , "```"+    , ""+    , "Test"+    ]++testCodeDoc :: TestTree+testCodeDoc = testCase "codeDoc" $+    targetCodeDoc @=? run sourceCodeDoc++------------------------------------------------------------------------------++sourceCodeCodeBlanksCode :: Text+sourceCodeCodeBlanksCode = TL.unlines+    [ "fn main() {"+    , "    println!(\"Hello...\");"+    , ""+    , ""+    , "    println!(\"world!\");"+    , "}"+    ]++targetCodeCodeBlanksCode :: Text+targetCodeCodeBlanksCode = TL.unlines+    [ "``` {.rust .numberSource startFrom=\"1\"}"+    , "fn main() {"+    , "    println!(\"Hello...\");"+    , ""+    , ""+    , "    println!(\"world!\");"+    , "}"+    , "```"+    ]++testCodeCodeBlanksCode :: TestTree+testCodeCodeBlanksCode = testCase "codeCodeBlanksCode" $+    targetCodeCodeBlanksCode @=? run sourceCodeCodeBlanksCode++------------------------------------------------------------------------------++sourceCodeCodeBlanksDoc :: Text+sourceCodeCodeBlanksDoc = TL.unlines+    [ "fn main() {"+    , "    println!(\"Hello!\");"+    , "}"+    , ""+    , ""+    , "// Test"+    ]++targetCodeCodeBlanksDoc :: Text+targetCodeCodeBlanksDoc = TL.unlines+    [ "``` {.rust .numberSource startFrom=\"1\"}"+    , "fn main() {"+    , "    println!(\"Hello!\");"+    , "}"+    , "```"+    , ""+    , "Test"+    ]++testCodeCodeBlanksDoc :: TestTree+testCodeCodeBlanksDoc = testCase "codeCodeBlanksDoc" $+    targetCodeCodeBlanksDoc @=? run sourceCodeCodeBlanksDoc++------------------------------------------------------------------------------++sourceCodeDocBlanksDoc :: Text+sourceCodeDocBlanksDoc = TL.unlines+    [ "fn main() {"+    , "    println!(\"Hello!\");"+    , "}"+    , "//"+    , "//"+    , "// Test"+    ]++targetCodeDocBlanksDoc :: Text+targetCodeDocBlanksDoc = TL.unlines+    [ "``` {.rust .numberSource startFrom=\"1\"}"+    , "fn main() {"+    , "    println!(\"Hello!\");"+    , "}"+    , "```"+    , ""+    , "Test"+    ]++testCodeDocBlanksDoc :: TestTree+testCodeDocBlanksDoc = testCase "codeDocBlanksDoc" $+    targetCodeDocBlanksDoc @=? run sourceCodeDocBlanksDoc++------------------------------------------------------------------------------++sourceCodeDocBlanksCode :: Text+sourceCodeDocBlanksCode = TL.unlines+    [ "fn main() {"+    , "    println!(\"Hello...\");"+    , "//"+    , "//"+    , "    println!(\"world!\");"+    , "}"+    ]++targetCodeDocBlanksCode :: Text+targetCodeDocBlanksCode = TL.unlines+    [ "``` {.rust .numberSource startFrom=\"1\"}"+    , "fn main() {"+    , "    println!(\"Hello...\");"+    , "```"+    , ""+    , "``` {.rust .numberSource startFrom=\"5\"}"+    , "    println!(\"world!\");"+    , "}"+    , "```"+    ]++testCodeDocBlanksCode :: TestTree+testCodeDocBlanksCode = testCase "codeDocBlanksCode" $+    targetCodeDocBlanksCode @=? run sourceCodeDocBlanksCode++------------------------------------------------------------------------------++sourceCodeRuleCode :: Text+sourceCodeRuleCode = TL.unlines+    [ "fn main() {"+    , "    println!(\"Hello...\");"+    , "//////////////////////////////////////////////////////////////////////"+    , "    println!(\"world!\");"+    , "}"+    ]++targetCodeRuleCode :: Text+targetCodeRuleCode = TL.unlines+    [ "``` {.rust .numberSource startFrom=\"1\"}"+    , "fn main() {"+    , "    println!(\"Hello...\");"+    , "```"+    , ""+    , "``` {.rust .numberSource startFrom=\"4\"}"+    , "    println!(\"world!\");"+    , "}"+    , "```"+    ]++testCodeRuleCode :: TestTree+testCodeRuleCode = testCase "codeRuleCode" $+    targetCodeRuleCode @=? run sourceCodeRuleCode++------------------------------------------------------------------------------++sourceCodeRuleDoc :: Text+sourceCodeRuleDoc = TL.unlines+    [ "fn main() {"+    , "    println!(\"Hello!\");"+    , "}"+    , "//////////////////////////////////////////////////////////////////////"+    , "// Test"+    ]++targetCodeRuleDoc :: Text+targetCodeRuleDoc = TL.unlines+    [ "``` {.rust .numberSource startFrom=\"1\"}"+    , "fn main() {"+    , "    println!(\"Hello!\");"+    , "}"+    , "```"+    , ""+    , "Test"+    ]++testCodeRuleDoc :: TestTree+testCodeRuleDoc = testCase "codeRuleDoc" $+    targetCodeRuleDoc @=? run sourceCodeRuleDoc++------------------------------------------------------------------------------++sourceDocCode :: Text+sourceDocCode = TL.unlines+    [ "// Test"+    , "fn main() {"+    , "    println!(\"Hello!\");"+    , "}"+    ]++targetDocCode :: Text+targetDocCode = TL.unlines+    [ "Test"+    , ""+    , "``` {.rust .numberSource startFrom=\"2\"}"+    , "fn main() {"+    , "    println!(\"Hello!\");"+    , "}"+    , "```"+    ]++testDocCode :: TestTree+testDocCode = testCase "docCode" $+    targetDocCode @=? run sourceDocCode++------------------------------------------------------------------------------++sourceDocDocBlanksDoc :: Text+sourceDocDocBlanksDoc = TL.unlines+    [ "// one"+    , "//"+    , "//"+    , "// two"+    ]++targetDocDocBlanksDoc :: Text+targetDocDocBlanksDoc = TL.unlines+    [ "one"+    , ""+    , "two"+    ]++testDocDocBlanksDoc :: TestTree+testDocDocBlanksDoc = testCase "docDocBlanksDoc" $+    targetDocDocBlanksDoc @=? run sourceDocDocBlanksDoc++------------------------------------------------------------------------------++sourceDocDocBlanksCode :: Text+sourceDocDocBlanksCode = TL.unlines+    [ "// Test"+    , "//"+    , "//"+    , "fn main() {"+    , "    println!(\"Hello!\");"+    , "}"+    ]++targetDocDocBlanksCode :: Text+targetDocDocBlanksCode = TL.unlines+    [ "Test"+    , ""+    , "``` {.rust .numberSource startFrom=\"4\"}"+    , "fn main() {"+    , "    println!(\"Hello!\");"+    , "}"+    , "```"+    ]++testDocDocBlanksCode :: TestTree+testDocDocBlanksCode = testCase "docDocBlanksCode" $+    targetDocDocBlanksCode @=? run sourceDocDocBlanksCode++------------------------------------------------------------------------------++sourceDocCodeBlanksCode :: Text+sourceDocCodeBlanksCode = TL.unlines+    [ "// Test"+    , ""+    , ""+    , "fn main() {"+    , "    println!(\"Hello!\");"+    , "}"+    ]++targetDocCodeBlanksCode :: Text+targetDocCodeBlanksCode = TL.unlines+    [ "Test"+    , ""+    , "``` {.rust .numberSource startFrom=\"4\"}"+    , "fn main() {"+    , "    println!(\"Hello!\");"+    , "}"+    , "```"+    ]++testDocCodeBlanksCode :: TestTree+testDocCodeBlanksCode = testCase "docCodeBlanksCode" $+    targetDocCodeBlanksCode @=? run sourceDocCodeBlanksCode++------------------------------------------------------------------------------++sourceDocCodeBlanksDoc :: Text+sourceDocCodeBlanksDoc = TL.unlines+    [ "// one"+    , ""+    , ""+    , "// two"+    ]++targetDocCodeBlanksDoc :: Text+targetDocCodeBlanksDoc = TL.unlines+    [ "one"+    , ""+    , "two"+    ]++testDocCodeBlanksDoc :: TestTree+testDocCodeBlanksDoc = testCase "docCodeBlanksDoc" $+    targetDocCodeBlanksDoc @=? run sourceDocCodeBlanksDoc++------------------------------------------------------------------------------++sourceDocRuleDoc :: Text+sourceDocRuleDoc = TL.unlines+    [ "// one"+    , "//////////////////////////////////////////////////////////////////////"+    , "// two"+    ]++targetDocRuleDoc :: Text+targetDocRuleDoc = TL.unlines+    [ "one"+    , ""+    , "two"+    ]++testDocRuleDoc :: TestTree+testDocRuleDoc = testCase "docRuleDoc" $+    targetDocRuleDoc @=? run sourceDocRuleDoc++------------------------------------------------------------------------------++sourceDocRuleCode :: Text+sourceDocRuleCode = TL.unlines+    [ "// Test"+    , "//////////////////////////////////////////////////////////////////////"+    , "fn main() {"+    , "    println!(\"Hello!\");"+    , "}"+    ]++targetDocRuleCode :: Text+targetDocRuleCode = TL.unlines+    [ "Test"+    , ""+    , "``` {.rust .numberSource startFrom=\"3\"}"+    , "fn main() {"+    , "    println!(\"Hello!\");"+    , "}"+    , "```"+    ]++testDocRuleCode :: TestTree+testDocRuleCode = testCase "docRuleCode" $+    targetDocRuleCode @=? run sourceDocRuleCode++------------------------------------------------------------------------------++sourceEndDoc :: Text+sourceEndDoc = TL.unlines+    [ "// Test"+    ]++targetEndDoc :: Text+targetEndDoc = TL.unlines+    [ "Test"+    ]++testEndDoc :: TestTree+testEndDoc = testCase "endDoc" $+    targetEndDoc @=? run sourceEndDoc++------------------------------------------------------------------------------++sourceEndDocBlanks :: Text+sourceEndDocBlanks = TL.unlines+    [ "//"+    , "//"+    ]++targetEndDocBlanks :: Text+targetEndDocBlanks = ""++testEndDocBlanks :: TestTree+testEndDocBlanks = testCase "endDocBlanks" $+    targetEndDocBlanks @=? run sourceEndDocBlanks++------------------------------------------------------------------------------++sourceEndCode :: Text+sourceEndCode = TL.unlines+    [ "fn main() {"+    , "    println!(\"Hello!\");"+    , "}"+    ]++targetEndCode :: Text+targetEndCode = TL.unlines+    [ "``` {.rust .numberSource startFrom=\"1\"}"+    , "fn main() {"+    , "    println!(\"Hello!\");"+    , "}"+    , "```"+    ]++testEndCode :: TestTree+testEndCode = testCase "endCode" $+    targetEndCode @=? run sourceEndCode++------------------------------------------------------------------------------++sourceEndCodeBlanks :: Text+sourceEndCodeBlanks = TL.unlines+    [ "fn main() {"+    , "    println!(\"Hello!\");"+    , "}"+    , ""+    , ""+    ]++targetEndCodeBlanks :: Text+targetEndCodeBlanks = TL.unlines+    [ "``` {.rust .numberSource startFrom=\"1\"}"+    , "fn main() {"+    , "    println!(\"Hello!\");"+    , "}"+    , "```"+    ]++testEndCodeBlanks :: TestTree+testEndCodeBlanks = testCase "endCodeBlanks" $+    targetEndCodeBlanks @=? run sourceEndCodeBlanks++------------------------------------------------------------------------------++sourceEmpty :: Text+sourceEmpty = ""++sourceOnlyRule :: Text+sourceOnlyRule = "///////////////////////////////////////////////////////////"++sourceOnlyDocBlanks :: Text+sourceOnlyDocBlanks = TL.unlines+    [ "//"+    , "//"+    ]++sourceOnlyCodeBlanks :: Text+sourceOnlyCodeBlanks = TL.unlines+    [ ""+    , ""+    ]++targetEmpty :: Text+targetEmpty = ""++testEmpty :: TestTree+testEmpty = testCase "empty" $+    targetEmpty @=? run sourceEmpty++testOnlyRule :: TestTree+testOnlyRule = testCase "onlyRule" $+    targetEmpty @=? run sourceOnlyRule++testOnlyDocBlanks :: TestTree+testOnlyDocBlanks = testCase "onlyDocBlanks" $+    targetEmpty @=? run sourceOnlyDocBlanks++testOnlyCodeBlanks :: TestTree+testOnlyCodeBlanks = testCase "onlyCodeBlanks" $+    targetEmpty @=? run sourceOnlyCodeBlanks++------------------------------------------------------------------------------++tests :: TestTree+tests = testGroup "DoubleSlash"+    [ testStartShebangDoc+    , testStartShebangCode+    , testStartDoc+    , testStartCode+    , testStartDocBlank+    , testStartCodeBlank+    , testStartRuleDoc+    , testStartRuleCode+    , testCodeDoc+    , testCodeCodeBlanksCode+    , testCodeCodeBlanksDoc+    , testCodeDocBlanksDoc+    , testCodeDocBlanksCode+    , testCodeRuleCode+    , testCodeRuleDoc+    , testDocCode+    , testDocDocBlanksDoc+    , testDocDocBlanksCode+    , testDocCodeBlanksCode+    , testDocCodeBlanksDoc+    , testDocRuleDoc+    , testDocRuleCode+    , testEndDoc+    , testEndDocBlanks+    , testEndCode+    , testEndCodeBlanks+    , testEmpty+    , testOnlyRule+    , testOnlyDocBlanks+    , testOnlyCodeBlanks+    ]
+ test/LiterateX/Test/SourceFormat/Hash.hs view
@@ -0,0 +1,740 @@+{-# LANGUAGE OverloadedStrings #-}++module LiterateX.Test.SourceFormat.Hash (tests) where++-- https://hackage.haskell.org/package/tasty+import Test.Tasty (TestTree, testGroup)++-- https://hackage.haskell.org/package/tasty-hunit+import Test.Tasty.HUnit ((@=?), testCase)++-- https://hackage.haskell.org/package/text+import qualified Data.Text.Lazy as TL+import Data.Text.Lazy (Text)++-- (literatex)+import qualified LiterateX+import qualified LiterateX.Renderer as Renderer+import qualified LiterateX.Types.SourceFormat as SourceFormat+import qualified LiterateX.Types.TargetFormat as TargetFormat++------------------------------------------------------------------------------++rendererOpts :: Renderer.Options+rendererOpts = Renderer.Options+    { Renderer.targetFormat    = TargetFormat.PandocMarkdown+    , Renderer.codeLanguage    = Just "python"+    , Renderer.ignoreShebang   = True+    , Renderer.renderCode      = True+    , Renderer.numberCodeLines = True+    }++run' :: Text -> Renderer.Options -> Text+run' = flip $ LiterateX.transformTextToText SourceFormat.Hash++run :: Text -> Text+run source = run' source rendererOpts++------------------------------------------------------------------------------++sourceStartShebangDoc :: Text+sourceStartShebangDoc = TL.unlines+    [ "#!/usr/bin/env python"+    , ""+    , "# Test"+    , ""+    , "print('Hello!')"+    ]++targetStartShebangDocIgnore :: Text+targetStartShebangDocIgnore = TL.unlines+    [ "Test"+    , ""+    , "``` {.python .numberSource startFrom=\"5\"}"+    , "print('Hello!')"+    , "```"+    ]++targetStartShebangDocNoIgnore :: Text+targetStartShebangDocNoIgnore = TL.unlines+    [ "``` {.numberSource startFrom=\"1\"}"+    , "#!/usr/bin/env python"+    , "```"+    , ""+    , "Test"+    , ""+    , "``` {.python .numberSource startFrom=\"5\"}"+    , "print('Hello!')"+    , "```"+    ]++targetStartShebangDocNoCode :: Text+targetStartShebangDocNoCode = TL.unlines+    [ "Test"+    ]++testStartShebangDoc :: TestTree+testStartShebangDoc = testGroup "startShebangDoc"+    [ testCase "Ignore" $+        targetStartShebangDocIgnore @=? run sourceStartShebangDoc+    , testCase "NoIgnore" $+        targetStartShebangDocNoIgnore @=?+          run' sourceStartShebangDoc rendererOpts+            { Renderer.ignoreShebang = False+            }+    , testCase "NoCode" $+        targetStartShebangDocNoCode @=?+          run' sourceStartShebangDoc rendererOpts+            { Renderer.ignoreShebang = False+            , Renderer.renderCode    = False+            }+    ]++------------------------------------------------------------------------------++sourceStartShebangCode :: Text+sourceStartShebangCode = TL.unlines+    [ "#!/usr/bin/env python"+    , ""+    , "print('Hello!')"+    , ""+    , "# Test"+    ]++targetStartShebangCodeIgnore :: Text+targetStartShebangCodeIgnore = TL.unlines+    [ "``` {.python .numberSource startFrom=\"3\"}"+    , "print('Hello!')"+    , "```"+    , ""+    , "Test"+    ]++targetStartShebangCodeNoIgnore :: Text+targetStartShebangCodeNoIgnore = TL.unlines+    [ "``` {.numberSource startFrom=\"1\"}"+    , "#!/usr/bin/env python"+    , "```"+    , ""+    , "``` {.python .numberSource startFrom=\"3\"}"+    , "print('Hello!')"+    , "```"+    , ""+    , "Test"+    ]++targetStartShebangCodeNoCode :: Text+targetStartShebangCodeNoCode = TL.unlines+    [ "Test"+    ]++testStartShebangCode :: TestTree+testStartShebangCode = testGroup "startShebangCode"+    [ testCase "Ignore" $+        targetStartShebangCodeIgnore @=? run sourceStartShebangCode+    , testCase "NoIgnore" $+        targetStartShebangCodeNoIgnore @=?+          run' sourceStartShebangCode rendererOpts+            { Renderer.ignoreShebang = False+            }+    , testCase "NoCode" $+        targetStartShebangCodeNoCode @=?+          run' sourceStartShebangCode rendererOpts+            { Renderer.ignoreShebang = False+            , Renderer.renderCode    = False+            }+    ]++------------------------------------------------------------------------------++sourceStartDoc :: Text+sourceStartDoc = TL.unlines+    [ "# Test"+    , ""+    , "print('Hello!')"+    ]++targetStartDoc :: Text+targetStartDoc = TL.unlines+    [ "Test"+    , ""+    , "``` {.python .numberSource startFrom=\"3\"}"+    , "print('Hello!')"+    , "```"+    ]++testStartDoc :: TestTree+testStartDoc = testCase "startDoc" $+    targetStartDoc @=? run sourceStartDoc++------------------------------------------------------------------------------++sourceStartCode :: Text+sourceStartCode = TL.unlines+    [ "print('Hello!')"+    , ""+    , "# Test"+    ]++targetStartCode :: Text+targetStartCode = TL.unlines+    [ "``` {.python .numberSource startFrom=\"1\"}"+    , "print('Hello!')"+    , "```"+    , ""+    , "Test"+    ]++testStartCode :: TestTree+testStartCode = testCase "startCode" $+    targetStartCode @=? run sourceStartCode++------------------------------------------------------------------------------++sourceStartDocBlank :: Text+sourceStartDocBlank = TL.unlines+    [ "#"+    , "# Test"+    , ""+    , "print('Hello!')"+    ]++targetStartDocBlank :: Text+targetStartDocBlank = TL.unlines+    [ "Test"+    , ""+    , "``` {.python .numberSource startFrom=\"4\"}"+    , "print('Hello!')"+    , "```"+    ]++testStartDocBlank :: TestTree+testStartDocBlank = testCase "startDocBlank" $+    targetStartDocBlank @=? run sourceStartDocBlank++------------------------------------------------------------------------------++sourceStartCodeBlank :: Text+sourceStartCodeBlank = TL.unlines+    [ ""+    , "print('Hello!')"+    , ""+    , "# Test"+    ]++targetStartCodeBlank :: Text+targetStartCodeBlank = TL.unlines+    [ "``` {.python .numberSource startFrom=\"2\"}"+    , "print('Hello!')"+    , "```"+    , ""+    , "Test"+    ]++testStartCodeBlank :: TestTree+testStartCodeBlank = testCase "startCodeBlank" $+    targetStartCodeBlank @=? run sourceStartCodeBlank++------------------------------------------------------------------------------++sourceStartRuleDoc :: Text+sourceStartRuleDoc = TL.unlines+    [ "######################################################################"+    , "# Test"+    , ""+    , "print('Hello!')"+    ]++targetStartRuleDoc :: Text+targetStartRuleDoc = TL.unlines+    [ "Test"+    , ""+    , "``` {.python .numberSource startFrom=\"4\"}"+    , "print('Hello!')"+    , "```"+    ]++testStartRuleDoc :: TestTree+testStartRuleDoc = testCase "startRuleDoc" $+    targetStartRuleDoc @=? run sourceStartRuleDoc++------------------------------------------------------------------------------++sourceStartRuleCode :: Text+sourceStartRuleCode = TL.unlines+    [ "######################################################################"+    , ""+    , "print('Hello!')"+    , ""+    , "# Test"+    ]++targetStartRuleCode :: Text+targetStartRuleCode = TL.unlines+    [ "``` {.python .numberSource startFrom=\"3\"}"+    , "print('Hello!')"+    , "```"+    , ""+    , "Test"+    ]++testStartRuleCode :: TestTree+testStartRuleCode = testCase "startRuleCode" $+    targetStartRuleCode @=? run sourceStartRuleCode++------------------------------------------------------------------------------++sourceCodeDoc :: Text+sourceCodeDoc = TL.unlines+    [ "print('one')"+    , "# Test"+    ]++targetCodeDoc :: Text+targetCodeDoc = TL.unlines+    [ "``` {.python .numberSource startFrom=\"1\"}"+    , "print('one')"+    , "```"+    , ""+    , "Test"+    ]++testCodeDoc :: TestTree+testCodeDoc = testCase "codeDoc" $+    targetCodeDoc @=? run sourceCodeDoc++------------------------------------------------------------------------------++sourceCodeCodeBlanksCode :: Text+sourceCodeCodeBlanksCode = TL.unlines+    [ "print('one')"+    , ""+    , ""+    , "print('two')"+    ]++targetCodeCodeBlanksCode :: Text+targetCodeCodeBlanksCode = TL.unlines+    [ "``` {.python .numberSource startFrom=\"1\"}"+    , "print('one')"+    , ""+    , ""+    , "print('two')"+    , "```"+    ]++testCodeCodeBlanksCode :: TestTree+testCodeCodeBlanksCode = testCase "codeCodeBlanksCode" $+    targetCodeCodeBlanksCode @=? run sourceCodeCodeBlanksCode++------------------------------------------------------------------------------++sourceCodeCodeBlanksDoc :: Text+sourceCodeCodeBlanksDoc = TL.unlines+    [ "print('one')"+    , ""+    , ""+    , "# Test"+    ]++targetCodeCodeBlanksDoc :: Text+targetCodeCodeBlanksDoc = TL.unlines+    [ "``` {.python .numberSource startFrom=\"1\"}"+    , "print('one')"+    , "```"+    , ""+    , "Test"+    ]++testCodeCodeBlanksDoc :: TestTree+testCodeCodeBlanksDoc = testCase "codeCodeBlanksDoc" $+    targetCodeCodeBlanksDoc @=? run sourceCodeCodeBlanksDoc++------------------------------------------------------------------------------++sourceCodeDocBlanksDoc :: Text+sourceCodeDocBlanksDoc = TL.unlines+    [ "print('one')"+    , "#"+    , "#"+    , "# Test"+    ]++targetCodeDocBlanksDoc :: Text+targetCodeDocBlanksDoc = TL.unlines+    [ "``` {.python .numberSource startFrom=\"1\"}"+    , "print('one')"+    , "```"+    , ""+    , "Test"+    ]++testCodeDocBlanksDoc :: TestTree+testCodeDocBlanksDoc = testCase "codeDocBlanksDoc" $+    targetCodeDocBlanksDoc @=? run sourceCodeDocBlanksDoc++------------------------------------------------------------------------------++sourceCodeDocBlanksCode :: Text+sourceCodeDocBlanksCode = TL.unlines+    [ "print('one')"+    , "#"+    , "#"+    , "print('two')"+    ]++targetCodeDocBlanksCode :: Text+targetCodeDocBlanksCode = TL.unlines+    [ "``` {.python .numberSource startFrom=\"1\"}"+    , "print('one')"+    , "```"+    , ""+    , "``` {.python .numberSource startFrom=\"4\"}"+    , "print('two')"+    , "```"+    ]++testCodeDocBlanksCode :: TestTree+testCodeDocBlanksCode = testCase "codeDocBlanksCode" $+    targetCodeDocBlanksCode @=? run sourceCodeDocBlanksCode++------------------------------------------------------------------------------++sourceCodeRuleCode :: Text+sourceCodeRuleCode = TL.unlines+    [ "print('one')"+    , "######################################################################"+    , "print('two')"+    ]++targetCodeRuleCode :: Text+targetCodeRuleCode = TL.unlines+    [ "``` {.python .numberSource startFrom=\"1\"}"+    , "print('one')"+    , "```"+    , ""+    , "``` {.python .numberSource startFrom=\"3\"}"+    , "print('two')"+    , "```"+    ]++testCodeRuleCode :: TestTree+testCodeRuleCode = testCase "codeRuleCode" $+    targetCodeRuleCode @=? run sourceCodeRuleCode++------------------------------------------------------------------------------++sourceCodeRuleDoc :: Text+sourceCodeRuleDoc = TL.unlines+    [ "print('one')"+    , "######################################################################"+    , "# Test"+    ]++targetCodeRuleDoc :: Text+targetCodeRuleDoc = TL.unlines+    [ "``` {.python .numberSource startFrom=\"1\"}"+    , "print('one')"+    , "```"+    , ""+    , "Test"+    ]++testCodeRuleDoc :: TestTree+testCodeRuleDoc = testCase "codeRuleDoc" $+    targetCodeRuleDoc @=? run sourceCodeRuleDoc++------------------------------------------------------------------------------++sourceDocCode :: Text+sourceDocCode = TL.unlines+    [ "# Test"+    , "print('one')"+    ]++targetDocCode :: Text+targetDocCode = TL.unlines+    [ "Test"+    , ""+    , "``` {.python .numberSource startFrom=\"2\"}"+    , "print('one')"+    , "```"+    ]++testDocCode :: TestTree+testDocCode = testCase "docCode" $+    targetDocCode @=? run sourceDocCode++------------------------------------------------------------------------------++sourceDocDocBlanksDoc :: Text+sourceDocDocBlanksDoc = TL.unlines+    [ "# one"+    , "#"+    , "#"+    , "# two"+    ]++targetDocDocBlanksDoc :: Text+targetDocDocBlanksDoc = TL.unlines+    [ "one"+    , ""+    , "two"+    ]++testDocDocBlanksDoc :: TestTree+testDocDocBlanksDoc = testCase "docDocBlanksDoc" $+    targetDocDocBlanksDoc @=? run sourceDocDocBlanksDoc++------------------------------------------------------------------------------++sourceDocDocBlanksCode :: Text+sourceDocDocBlanksCode = TL.unlines+    [ "# Test"+    , "#"+    , "#"+    , "print('Hello!')"+    ]++targetDocDocBlanksCode :: Text+targetDocDocBlanksCode = TL.unlines+    [ "Test"+    , ""+    , "``` {.python .numberSource startFrom=\"4\"}"+    , "print('Hello!')"+    , "```"+    ]++testDocDocBlanksCode :: TestTree+testDocDocBlanksCode = testCase "docDocBlanksCode" $+    targetDocDocBlanksCode @=? run sourceDocDocBlanksCode++------------------------------------------------------------------------------++sourceDocCodeBlanksCode :: Text+sourceDocCodeBlanksCode = TL.unlines+    [ "# Test"+    , ""+    , ""+    , "print('Hello!')"+    ]++targetDocCodeBlanksCode :: Text+targetDocCodeBlanksCode = TL.unlines+    [ "Test"+    , ""+    , "``` {.python .numberSource startFrom=\"4\"}"+    , "print('Hello!')"+    , "```"+    ]++testDocCodeBlanksCode :: TestTree+testDocCodeBlanksCode = testCase "docCodeBlanksCode" $+    targetDocCodeBlanksCode @=? run sourceDocCodeBlanksCode++------------------------------------------------------------------------------++sourceDocCodeBlanksDoc :: Text+sourceDocCodeBlanksDoc = TL.unlines+    [ "# one"+    , ""+    , ""+    , "# two"+    ]++targetDocCodeBlanksDoc :: Text+targetDocCodeBlanksDoc = TL.unlines+    [ "one"+    , ""+    , "two"+    ]++testDocCodeBlanksDoc :: TestTree+testDocCodeBlanksDoc = testCase "docCodeBlanksDoc" $+    targetDocCodeBlanksDoc @=? run sourceDocCodeBlanksDoc++------------------------------------------------------------------------------++sourceDocRuleDoc :: Text+sourceDocRuleDoc = TL.unlines+    [ "# one"+    , "######################################################################"+    , "# two"+    ]++targetDocRuleDoc :: Text+targetDocRuleDoc = TL.unlines+    [ "one"+    , ""+    , "two"+    ]++testDocRuleDoc :: TestTree+testDocRuleDoc = testCase "docRuleDoc" $+    targetDocRuleDoc @=? run sourceDocRuleDoc++------------------------------------------------------------------------------++sourceDocRuleCode :: Text+sourceDocRuleCode = TL.unlines+    [ "# Test"+    , "######################################################################"+    , "print('Hello!')"+    ]++targetDocRuleCode :: Text+targetDocRuleCode = TL.unlines+    [ "Test"+    , ""+    , "``` {.python .numberSource startFrom=\"3\"}"+    , "print('Hello!')"+    , "```"+    ]++testDocRuleCode :: TestTree+testDocRuleCode = testCase "docRuleCode" $+    targetDocRuleCode @=? run sourceDocRuleCode++------------------------------------------------------------------------------++sourceEndDoc :: Text+sourceEndDoc = TL.unlines+    [ "# Test"+    ]++targetEndDoc :: Text+targetEndDoc = TL.unlines+    [ "Test"+    ]++testEndDoc :: TestTree+testEndDoc = testCase "endDoc" $+    targetEndDoc @=? run sourceEndDoc++------------------------------------------------------------------------------++sourceEndDocBlanks :: Text+sourceEndDocBlanks = TL.unlines+    [ "#"+    , "#"+    ]++targetEndDocBlanks :: Text+targetEndDocBlanks = ""++testEndDocBlanks :: TestTree+testEndDocBlanks = testCase "endDocBlanks" $+    targetEndDocBlanks @=? run sourceEndDocBlanks++------------------------------------------------------------------------------++sourceEndCode :: Text+sourceEndCode = TL.unlines+    [ "print('Hello!')"+    ]++targetEndCode :: Text+targetEndCode = TL.unlines+    [ "``` {.python .numberSource startFrom=\"1\"}"+    , "print('Hello!')"+    , "```"+    ]++testEndCode :: TestTree+testEndCode = testCase "endCode" $+    targetEndCode @=? run sourceEndCode++------------------------------------------------------------------------------++sourceEndCodeBlanks :: Text+sourceEndCodeBlanks = TL.unlines+    [ "print('Hello!')"+    , ""+    , ""+    ]++targetEndCodeBlanks :: Text+targetEndCodeBlanks = TL.unlines+    [ "``` {.python .numberSource startFrom=\"1\"}"+    , "print('Hello!')"+    , "```"+    ]++testEndCodeBlanks :: TestTree+testEndCodeBlanks = testCase "endCodeBlanks" $+    targetEndCodeBlanks @=? run sourceEndCodeBlanks++------------------------------------------------------------------------------++sourceEmpty :: Text+sourceEmpty = ""++sourceOnlyRule :: Text+sourceOnlyRule = "###########################################################"++sourceOnlyDocBlanks :: Text+sourceOnlyDocBlanks = TL.unlines+    [ "#"+    , "#"+    ]++sourceOnlyCodeBlanks :: Text+sourceOnlyCodeBlanks = TL.unlines+    [ ""+    , ""+    ]++targetEmpty :: Text+targetEmpty = ""++testEmpty :: TestTree+testEmpty = testCase "empty" $+    targetEmpty @=? run sourceEmpty++testOnlyRule :: TestTree+testOnlyRule = testCase "onlyRule" $+    targetEmpty @=? run sourceOnlyRule++testOnlyDocBlanks :: TestTree+testOnlyDocBlanks = testCase "onlyDocBlanks" $+    targetEmpty @=? run sourceOnlyDocBlanks++testOnlyCodeBlanks :: TestTree+testOnlyCodeBlanks = testCase "onlyCodeBlanks" $+    targetEmpty @=? run sourceOnlyCodeBlanks++------------------------------------------------------------------------------++tests :: TestTree+tests = testGroup "Hash"+    [ testStartShebangDoc+    , testStartShebangCode+    , testStartDoc+    , testStartCode+    , testStartDocBlank+    , testStartCodeBlank+    , testStartRuleDoc+    , testStartRuleCode+    , testCodeDoc+    , testCodeCodeBlanksCode+    , testCodeCodeBlanksDoc+    , testCodeDocBlanksDoc+    , testCodeDocBlanksCode+    , testCodeRuleCode+    , testCodeRuleDoc+    , testDocCode+    , testDocDocBlanksDoc+    , testDocDocBlanksCode+    , testDocCodeBlanksCode+    , testDocCodeBlanksDoc+    , testDocRuleDoc+    , testDocRuleCode+    , testEndDoc+    , testEndDocBlanks+    , testEndCode+    , testEndCodeBlanks+    , testEmpty+    , testOnlyRule+    , testOnlyDocBlanks+    , testOnlyCodeBlanks+    ]
+ test/LiterateX/Test/SourceFormat/LispSemicolons.hs view
@@ -0,0 +1,837 @@+{-# LANGUAGE OverloadedStrings #-}++module LiterateX.Test.SourceFormat.LispSemicolons (tests) where++-- https://hackage.haskell.org/package/tasty+import Test.Tasty (TestTree, testGroup)++-- https://hackage.haskell.org/package/tasty-hunit+import Test.Tasty.HUnit ((@=?), testCase)++-- https://hackage.haskell.org/package/text+import qualified Data.Text.Lazy as TL+import Data.Text.Lazy (Text)++-- (literatex)+import qualified LiterateX+import qualified LiterateX.Renderer as Renderer+import qualified LiterateX.Types.SourceFormat as SourceFormat+import qualified LiterateX.Types.TargetFormat as TargetFormat++------------------------------------------------------------------------------++rendererOpts :: Renderer.Options+rendererOpts = Renderer.Options+    { Renderer.targetFormat    = TargetFormat.PandocMarkdown+    , Renderer.codeLanguage    = Just "scheme"+    , Renderer.ignoreShebang   = True+    , Renderer.renderCode      = True+    , Renderer.numberCodeLines = True+    }++run' :: Text -> Renderer.Options -> Text+run' = flip $ LiterateX.transformTextToText SourceFormat.LispSemicolons++run :: Text -> Text+run source = run' source rendererOpts++------------------------------------------------------------------------------++sourceStartShebangDoc :: Text+sourceStartShebangDoc = TL.unlines+    [ "#!/usr/bin/env racket"+    , ""+    , "; Test"+    , ""+    , "#lang racket/base"+    , ""+    , "(println \"Hello!\")"+    ]++targetStartShebangDocIgnore :: Text+targetStartShebangDocIgnore = TL.unlines+    [ "Test"+    , ""+    , "``` {.scheme .numberSource startFrom=\"5\"}"+    , "#lang racket/base"+    , ""+    , "(println \"Hello!\")"+    , "```"+    ]++targetStartShebangDocNoIgnore :: Text+targetStartShebangDocNoIgnore = TL.unlines+    [ "``` {.numberSource startFrom=\"1\"}"+    , "#!/usr/bin/env racket"+    , "```"+    , ""+    , "Test"+    , ""+    , "``` {.scheme .numberSource startFrom=\"5\"}"+    , "#lang racket/base"+    , ""+    , "(println \"Hello!\")"+    , "```"+    ]++targetStartShebangDocNoCode :: Text+targetStartShebangDocNoCode = TL.unlines+    [ "Test"+    ]++testStartShebangDoc :: TestTree+testStartShebangDoc = testGroup "startShebangDoc"+    [ testCase "Ignore" $+        targetStartShebangDocIgnore @=? run sourceStartShebangDoc+    , testCase "NoIgnore" $+        targetStartShebangDocNoIgnore @=?+          run' sourceStartShebangDoc rendererOpts+            { Renderer.ignoreShebang = False+            }+    , testCase "NoCode" $+        targetStartShebangDocNoCode @=?+          run' sourceStartShebangDoc rendererOpts+            { Renderer.ignoreShebang = False+            , Renderer.renderCode    = False+            }+    ]++------------------------------------------------------------------------------++sourceStartShebangCode :: Text+sourceStartShebangCode = TL.unlines+    [ "#!/usr/bin/env racket"+    , "#lang racket/base"+    , ""+    , "(println \"Hello!\")"+    , ""+    , "; Test"+    ]++targetStartShebangCodeIgnore :: Text+targetStartShebangCodeIgnore = TL.unlines+    [ "``` {.scheme .numberSource startFrom=\"2\"}"+    , "#lang racket/base"+    , ""+    , "(println \"Hello!\")"+    , "```"+    , ""+    , "Test"+    ]++targetStartShebangCodeNoIgnore :: Text+targetStartShebangCodeNoIgnore = TL.unlines+    [ "``` {.numberSource startFrom=\"1\"}"+    , "#!/usr/bin/env racket"+    , "```"+    , ""+    , "``` {.scheme .numberSource startFrom=\"2\"}"+    , "#lang racket/base"+    , ""+    , "(println \"Hello!\")"+    , "```"+    , ""+    , "Test"+    ]++targetStartShebangCodeNoCode :: Text+targetStartShebangCodeNoCode = TL.unlines+    [ "Test"+    ]++testStartShebangCode :: TestTree+testStartShebangCode = testGroup "startShebangCode"+    [ testCase "Ignore" $+        targetStartShebangCodeIgnore @=? run sourceStartShebangCode+    , testCase "NoIgnore" $+        targetStartShebangCodeNoIgnore @=?+          run' sourceStartShebangCode rendererOpts+            { Renderer.ignoreShebang = False+            }+    , testCase "NoCode" $+        targetStartShebangCodeNoCode @=?+          run' sourceStartShebangCode rendererOpts+            { Renderer.ignoreShebang = False+            , Renderer.renderCode    = False+            }+    ]++------------------------------------------------------------------------------++sourceStartDoc :: Text+sourceStartDoc = TL.unlines+    [ "; Test"+    , ""+    , "#lang racket/base"+    , ""+    , "(println \"Hello!\")"+    ]++targetStartDoc :: Text+targetStartDoc = TL.unlines+    [ "Test"+    , ""+    , "``` {.scheme .numberSource startFrom=\"3\"}"+    , "#lang racket/base"+    , ""+    , "(println \"Hello!\")"+    , "```"+    ]++testStartDoc :: TestTree+testStartDoc = testCase "startDoc" $+    targetStartDoc @=? run sourceStartDoc++------------------------------------------------------------------------------++sourceStartCode :: Text+sourceStartCode = TL.unlines+    [ "#lang racket/base"+    , ""+    , "(println \"Hello!\")"+    , ""+    , "; Test"+    ]++targetStartCode :: Text+targetStartCode = TL.unlines+    [ "``` {.scheme .numberSource startFrom=\"1\"}"+    , "#lang racket/base"+    , ""+    , "(println \"Hello!\")"+    , "```"+    , ""+    , "Test"+    ]++testStartCode :: TestTree+testStartCode = testCase "startCode" $+    targetStartCode @=? run sourceStartCode++------------------------------------------------------------------------------++sourceStartDocBlank :: Text+sourceStartDocBlank = TL.unlines+    [ ";"+    , "; Test"+    , ""+    , "#lang racket/base"+    , ""+    , "(println \"Hello!\")"+    ]++targetStartDocBlank :: Text+targetStartDocBlank = TL.unlines+    [ "Test"+    , ""+    , "``` {.scheme .numberSource startFrom=\"4\"}"+    , "#lang racket/base"+    , ""+    , "(println \"Hello!\")"+    , "```"+    ]++testStartDocBlank :: TestTree+testStartDocBlank = testCase "startDocBlank" $+    targetStartDocBlank @=? run sourceStartDocBlank++------------------------------------------------------------------------------++sourceStartCodeBlank :: Text+sourceStartCodeBlank = TL.unlines+    [ ""+    , "#lang racket/base"+    , ""+    , "(println \"Hello!\")"+    , ""+    , "; Test"+    ]++targetStartCodeBlank :: Text+targetStartCodeBlank = TL.unlines+    [ "``` {.scheme .numberSource startFrom=\"2\"}"+    , "#lang racket/base"+    , ""+    , "(println \"Hello!\")"+    , "```"+    , ""+    , "Test"+    ]++testStartCodeBlank :: TestTree+testStartCodeBlank = testCase "startCodeBlank" $+    targetStartCodeBlank @=? run sourceStartCodeBlank++------------------------------------------------------------------------------++sourceStartRuleDoc :: Text+sourceStartRuleDoc = TL.unlines+    [ ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"+    , "; Test"+    , ""+    , "#lang racket/base"+    , ""+    , "(println \"Hello!\")"+    ]++targetStartRuleDoc :: Text+targetStartRuleDoc = TL.unlines+    [ "Test"+    , ""+    , "``` {.scheme .numberSource startFrom=\"4\"}"+    , "#lang racket/base"+    , ""+    , "(println \"Hello!\")"+    , "```"+    ]++testStartRuleDoc :: TestTree+testStartRuleDoc = testCase "startRuleDoc" $+    targetStartRuleDoc @=? run sourceStartRuleDoc++------------------------------------------------------------------------------++sourceStartRuleCode :: Text+sourceStartRuleCode = TL.unlines+    [ ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"+    , ""+    , "#lang racket/base"+    , ""+    , "(println \"Hello!\")"+    , ""+    , "; Test"+    ]++targetStartRuleCode :: Text+targetStartRuleCode = TL.unlines+    [ "``` {.scheme .numberSource startFrom=\"3\"}"+    , "#lang racket/base"+    , ""+    , "(println \"Hello!\")"+    , "```"+    , ""+    , "Test"+    ]++testStartRuleCode :: TestTree+testStartRuleCode = testCase "startRuleCode" $+    targetStartRuleCode @=? run sourceStartRuleCode++------------------------------------------------------------------------------++sourceCodeDoc :: Text+sourceCodeDoc = TL.unlines+    [ "#lang racket/base"+    , ""+    , "print('one')"+    , "; Test"+    ]++targetCodeDoc :: Text+targetCodeDoc = TL.unlines+    [ "``` {.scheme .numberSource startFrom=\"1\"}"+    , "#lang racket/base"+    , ""+    , "print('one')"+    , "```"+    , ""+    , "Test"+    ]++testCodeDoc :: TestTree+testCodeDoc = testCase "codeDoc" $+    targetCodeDoc @=? run sourceCodeDoc++------------------------------------------------------------------------------++sourceCodeCodeBlanksCode :: Text+sourceCodeCodeBlanksCode = TL.unlines+    [ "#lang racket/base"+    , ""+    , ""+    , "print('one')"+    ]++targetCodeCodeBlanksCode :: Text+targetCodeCodeBlanksCode = TL.unlines+    [ "``` {.scheme .numberSource startFrom=\"1\"}"+    , "#lang racket/base"+    , ""+    , ""+    , "print('one')"+    , "```"+    ]++testCodeCodeBlanksCode :: TestTree+testCodeCodeBlanksCode = testCase "codeCodeBlanksCode" $+    targetCodeCodeBlanksCode @=? run sourceCodeCodeBlanksCode++------------------------------------------------------------------------------++sourceCodeCodeBlanksDoc :: Text+sourceCodeCodeBlanksDoc = TL.unlines+    [ "#lang racket/base"+    , ""+    , "print('one')"+    , ""+    , ""+    , "; Test"+    ]++targetCodeCodeBlanksDoc :: Text+targetCodeCodeBlanksDoc = TL.unlines+    [ "``` {.scheme .numberSource startFrom=\"1\"}"+    , "#lang racket/base"+    , ""+    , "print('one')"+    , "```"+    , ""+    , "Test"+    ]++testCodeCodeBlanksDoc :: TestTree+testCodeCodeBlanksDoc = testCase "codeCodeBlanksDoc" $+    targetCodeCodeBlanksDoc @=? run sourceCodeCodeBlanksDoc++------------------------------------------------------------------------------++sourceCodeDocBlanksDoc :: Text+sourceCodeDocBlanksDoc = TL.unlines+    [ "#lang racket/base"+    , ""+    , "print('one')"+    , ";"+    , ";"+    , "; Test"+    ]++targetCodeDocBlanksDoc :: Text+targetCodeDocBlanksDoc = TL.unlines+    [ "``` {.scheme .numberSource startFrom=\"1\"}"+    , "#lang racket/base"+    , ""+    , "print('one')"+    , "```"+    , ""+    , "Test"+    ]++testCodeDocBlanksDoc :: TestTree+testCodeDocBlanksDoc = testCase "codeDocBlanksDoc" $+    targetCodeDocBlanksDoc @=? run sourceCodeDocBlanksDoc++------------------------------------------------------------------------------++sourceCodeDocBlanksCode :: Text+sourceCodeDocBlanksCode = TL.unlines+    [ "#lang racket/base"+    , ";"+    , ";"+    , "print('one')"+    ]++targetCodeDocBlanksCode :: Text+targetCodeDocBlanksCode = TL.unlines+    [ "``` {.scheme .numberSource startFrom=\"1\"}"+    , "#lang racket/base"+    , "```"+    , ""+    , "``` {.scheme .numberSource startFrom=\"4\"}"+    , "print('one')"+    , "```"+    ]++testCodeDocBlanksCode :: TestTree+testCodeDocBlanksCode = testCase "codeDocBlanksCode" $+    targetCodeDocBlanksCode @=? run sourceCodeDocBlanksCode++------------------------------------------------------------------------------++sourceCodeRuleCode :: Text+sourceCodeRuleCode = TL.unlines+    [ "#lang racket/base"+    , ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"+    , "print('one')"+    ]++targetCodeRuleCode :: Text+targetCodeRuleCode = TL.unlines+    [ "``` {.scheme .numberSource startFrom=\"1\"}"+    , "#lang racket/base"+    , "```"+    , ""+    , "``` {.scheme .numberSource startFrom=\"3\"}"+    , "print('one')"+    , "```"+    ]++testCodeRuleCode :: TestTree+testCodeRuleCode = testCase "codeRuleCode" $+    targetCodeRuleCode @=? run sourceCodeRuleCode++------------------------------------------------------------------------------++sourceCodeRuleDoc :: Text+sourceCodeRuleDoc = TL.unlines+    [ "#lang racket/base"+    , ""+    , "print('one')"+    , ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"+    , "; Test"+    ]++targetCodeRuleDoc :: Text+targetCodeRuleDoc = TL.unlines+    [ "``` {.scheme .numberSource startFrom=\"1\"}"+    , "#lang racket/base"+    , ""+    , "print('one')"+    , "```"+    , ""+    , "Test"+    ]++testCodeRuleDoc :: TestTree+testCodeRuleDoc = testCase "codeRuleDoc" $+    targetCodeRuleDoc @=? run sourceCodeRuleDoc++------------------------------------------------------------------------------++sourceDocCode :: Text+sourceDocCode = TL.unlines+    [ "; Test"+    , "#lang racket/base"+    , ""+    , "print('one')"+    ]++targetDocCode :: Text+targetDocCode = TL.unlines+    [ "Test"+    , ""+    , "``` {.scheme .numberSource startFrom=\"2\"}"+    , "#lang racket/base"+    , ""+    , "print('one')"+    , "```"+    ]++testDocCode :: TestTree+testDocCode = testCase "docCode" $+    targetDocCode @=? run sourceDocCode++------------------------------------------------------------------------------++sourceDocDocBlanksDoc :: Text+sourceDocDocBlanksDoc = TL.unlines+    [ "; one"+    , ";"+    , ";"+    , "; two"+    ]++targetDocDocBlanksDoc :: Text+targetDocDocBlanksDoc = TL.unlines+    [ "one"+    , ""+    , "two"+    ]++testDocDocBlanksDoc :: TestTree+testDocDocBlanksDoc = testCase "docDocBlanksDoc" $+    targetDocDocBlanksDoc @=? run sourceDocDocBlanksDoc++------------------------------------------------------------------------------++sourceDocDocBlanksCode :: Text+sourceDocDocBlanksCode = TL.unlines+    [ "; Test"+    , ";"+    , ";"+    , "#lang racket/base"+    , ""+    , "(println \"Hello!\")"+    ]++targetDocDocBlanksCode :: Text+targetDocDocBlanksCode = TL.unlines+    [ "Test"+    , ""+    , "``` {.scheme .numberSource startFrom=\"4\"}"+    , "#lang racket/base"+    , ""+    , "(println \"Hello!\")"+    , "```"+    ]++testDocDocBlanksCode :: TestTree+testDocDocBlanksCode = testCase "docDocBlanksCode" $+    targetDocDocBlanksCode @=? run sourceDocDocBlanksCode++------------------------------------------------------------------------------++sourceDocCodeBlanksCode :: Text+sourceDocCodeBlanksCode = TL.unlines+    [ "; Test"+    , ""+    , ""+    , "#lang racket/base"+    , ""+    , "(println \"Hello!\")"+    ]++targetDocCodeBlanksCode :: Text+targetDocCodeBlanksCode = TL.unlines+    [ "Test"+    , ""+    , "``` {.scheme .numberSource startFrom=\"4\"}"+    , "#lang racket/base"+    , ""+    , "(println \"Hello!\")"+    , "```"+    ]++testDocCodeBlanksCode :: TestTree+testDocCodeBlanksCode = testCase "docCodeBlanksCode" $+    targetDocCodeBlanksCode @=? run sourceDocCodeBlanksCode++------------------------------------------------------------------------------++sourceDocCodeBlanksDoc :: Text+sourceDocCodeBlanksDoc = TL.unlines+    [ "; one"+    , ""+    , ""+    , "; two"+    ]++targetDocCodeBlanksDoc :: Text+targetDocCodeBlanksDoc = TL.unlines+    [ "one"+    , ""+    , "two"+    ]++testDocCodeBlanksDoc :: TestTree+testDocCodeBlanksDoc = testCase "docCodeBlanksDoc" $+    targetDocCodeBlanksDoc @=? run sourceDocCodeBlanksDoc++------------------------------------------------------------------------------++sourceDocRuleDoc :: Text+sourceDocRuleDoc = TL.unlines+    [ "; one"+    , ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"+    , "; two"+    ]++targetDocRuleDoc :: Text+targetDocRuleDoc = TL.unlines+    [ "one"+    , ""+    , "two"+    ]++testDocRuleDoc :: TestTree+testDocRuleDoc = testCase "docRuleDoc" $+    targetDocRuleDoc @=? run sourceDocRuleDoc++------------------------------------------------------------------------------++sourceDocRuleCode :: Text+sourceDocRuleCode = TL.unlines+    [ "; Test"+    , ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"+    , "#lang racket/base"+    , ""+    , "(println \"Hello!\")"+    ]++targetDocRuleCode :: Text+targetDocRuleCode = TL.unlines+    [ "Test"+    , ""+    , "``` {.scheme .numberSource startFrom=\"3\"}"+    , "#lang racket/base"+    , ""+    , "(println \"Hello!\")"+    , "```"+    ]++testDocRuleCode :: TestTree+testDocRuleCode = testCase "docRuleCode" $+    targetDocRuleCode @=? run sourceDocRuleCode++------------------------------------------------------------------------------++sourceEndDoc :: Text+sourceEndDoc = TL.unlines+    [ "; Test"+    ]++targetEndDoc :: Text+targetEndDoc = TL.unlines+    [ "Test"+    ]++testEndDoc :: TestTree+testEndDoc = testCase "endDoc" $+    targetEndDoc @=? run sourceEndDoc++------------------------------------------------------------------------------++sourceEndDocBlanks :: Text+sourceEndDocBlanks = TL.unlines+    [ ";"+    , ";"+    ]++targetEndDocBlanks :: Text+targetEndDocBlanks = ""++testEndDocBlanks :: TestTree+testEndDocBlanks = testCase "endDocBlanks" $+    targetEndDocBlanks @=? run sourceEndDocBlanks++------------------------------------------------------------------------------++sourceEndCode :: Text+sourceEndCode = TL.unlines+    [ "#lang racket/base"+    , ""+    , "(println \"Hello!\")"+    ]++targetEndCode :: Text+targetEndCode = TL.unlines+    [ "``` {.scheme .numberSource startFrom=\"1\"}"+    , "#lang racket/base"+    , ""+    , "(println \"Hello!\")"+    , "```"+    ]++testEndCode :: TestTree+testEndCode = testCase "endCode" $+    targetEndCode @=? run sourceEndCode++------------------------------------------------------------------------------++sourceEndCodeBlanks :: Text+sourceEndCodeBlanks = TL.unlines+    [ "#lang racket/base"+    , ""+    , "(println \"Hello!\")"+    , ""+    , ""+    ]++targetEndCodeBlanks :: Text+targetEndCodeBlanks = TL.unlines+    [ "``` {.scheme .numberSource startFrom=\"1\"}"+    , "#lang racket/base"+    , ""+    , "(println \"Hello!\")"+    , "```"+    ]++testEndCodeBlanks :: TestTree+testEndCodeBlanks = testCase "endCodeBlanks" $+    targetEndCodeBlanks @=? run sourceEndCodeBlanks++------------------------------------------------------------------------------++sourceEmpty :: Text+sourceEmpty = ""++sourceOnlyRule :: Text+sourceOnlyRule = ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"++sourceOnlyDocBlanks :: Text+sourceOnlyDocBlanks = TL.unlines+    [ ";"+    , ";"+    ]++sourceOnlyCodeBlanks :: Text+sourceOnlyCodeBlanks = TL.unlines+    [ ""+    , ""+    ]++targetEmpty :: Text+targetEmpty = ""++testEmpty :: TestTree+testEmpty = testCase "empty" $+    targetEmpty @=? run sourceEmpty++testOnlyRule :: TestTree+testOnlyRule = testCase "onlyRule" $+    targetEmpty @=? run sourceOnlyRule++testOnlyDocBlanks :: TestTree+testOnlyDocBlanks = testCase "onlyDocBlanks" $+    targetEmpty @=? run sourceOnlyDocBlanks++testOnlyCodeBlanks :: TestTree+testOnlyCodeBlanks = testCase "onlyCodeBlanks" $+    targetEmpty @=? run sourceOnlyCodeBlanks++------------------------------------------------------------------------------++testLispSemicolons :: TestTree+testLispSemicolons = testGroup "lispSemicolons"+    [ testCase "oneSemicolonDoc" $+        target @=? run (sourceSemicolonDoc 1)+    , testCase "twoSemicolonDoc" $+        target @=? run (sourceSemicolonDoc 2)+    , testCase "threeSemicolonDoc" $+        target @=? run (sourceSemicolonDoc 3)+    , testCase "fourSemicolonDoc" $+        target @=? run (sourceSemicolonDoc 4)+    ]+  where+    sourceSemicolonDoc :: Int -> Text+    sourceSemicolonDoc count =+      TL.concat [TL.pack (replicate count ';'), " ", target]++    target :: Text+    target = TL.unlines ["Test"]++------------------------------------------------------------------------------++tests :: TestTree+tests = testGroup "LispSemicolons"+    [ testStartShebangDoc+    , testStartShebangCode+    , testStartDoc+    , testStartCode+    , testStartDocBlank+    , testStartCodeBlank+    , testStartRuleDoc+    , testStartRuleCode+    , testCodeDoc+    , testCodeCodeBlanksCode+    , testCodeCodeBlanksDoc+    , testCodeDocBlanksDoc+    , testCodeDocBlanksCode+    , testCodeRuleCode+    , testCodeRuleDoc+    , testDocCode+    , testDocDocBlanksDoc+    , testDocDocBlanksCode+    , testDocCodeBlanksCode+    , testDocCodeBlanksDoc+    , testDocRuleDoc+    , testDocRuleCode+    , testEndDoc+    , testEndDocBlanks+    , testEndCode+    , testEndCodeBlanks+    , testEmpty+    , testOnlyRule+    , testOnlyDocBlanks+    , testOnlyCodeBlanks+    , testLispSemicolons+    ]
+ test/LiterateX/Test/SourceFormat/LiterateHaskell.hs view
@@ -0,0 +1,696 @@+{-# LANGUAGE OverloadedStrings #-}++module LiterateX.Test.SourceFormat.LiterateHaskell (tests) where++-- https://hackage.haskell.org/package/tasty+import Test.Tasty (TestTree, testGroup)++-- https://hackage.haskell.org/package/tasty-hunit+import Test.Tasty.HUnit ((@=?), testCase)++-- https://hackage.haskell.org/package/text+import qualified Data.Text.Lazy as TL+import Data.Text.Lazy (Text)++-- (literatex)+import qualified LiterateX+import qualified LiterateX.Renderer as Renderer+import qualified LiterateX.Types.SourceFormat as SourceFormat+import qualified LiterateX.Types.TargetFormat as TargetFormat++------------------------------------------------------------------------------++rendererOpts :: Renderer.Options+rendererOpts = Renderer.Options+    { Renderer.targetFormat    = TargetFormat.PandocMarkdown+    , Renderer.codeLanguage    = Just "haskell"+    , Renderer.ignoreShebang   = True+    , Renderer.renderCode      = True+    , Renderer.numberCodeLines = True+    }++run' :: Text -> Renderer.Options -> Text+run' = flip $ LiterateX.transformTextToText SourceFormat.LiterateHaskell++run :: Text -> Text+run source = run' source rendererOpts++------------------------------------------------------------------------------++sourceStartShebangDoc :: Text+sourceStartShebangDoc = TL.unlines+    [ "#!/usr/bin/env stack"+    , ""+    , "This results in warnings!"+    , ""+    , "> {- stack script --resolver lts-17.11 -}"+    , ">"+    , "> module Main (main) where"+    , ">"+    , "> main :: IO ()"+    , "> main = putStrLn \"Hello!\""+    ]++targetStartShebangDocIgnore :: Text+targetStartShebangDocIgnore = TL.unlines+    [ "This results in warnings!"+    , ""+    , "``` {.haskell .numberSource startFrom=\"5\"}"+    , "{- stack script --resolver lts-17.11 -}"+    , ""+    , "module Main (main) where"+    , ""+    , "main :: IO ()"+    , "main = putStrLn \"Hello!\""+    , "```"+    ]++targetStartShebangDocNoIgnore :: Text+targetStartShebangDocNoIgnore = TL.unlines+    [ "``` {.numberSource startFrom=\"1\"}"+    , "#!/usr/bin/env stack"+    , "```"+    , ""+    , "This results in warnings!"+    , ""+    , "``` {.haskell .numberSource startFrom=\"5\"}"+    , "{- stack script --resolver lts-17.11 -}"+    , ""+    , "module Main (main) where"+    , ""+    , "main :: IO ()"+    , "main = putStrLn \"Hello!\""+    , "```"+    ]++targetStartShebangDocNoCode :: Text+targetStartShebangDocNoCode = TL.unlines+    [ "This results in warnings!"+    ]++testStartShebangDoc :: TestTree+testStartShebangDoc = testGroup "startShebangDoc"+    [ testCase "Ignore" $+        targetStartShebangDocIgnore @=? run sourceStartShebangDoc+    , testCase "NoIgnore" $+        targetStartShebangDocNoIgnore @=?+          run' sourceStartShebangDoc rendererOpts+            { Renderer.ignoreShebang = False+            }+    , testCase "NoCode" $+        targetStartShebangDocNoCode @=?+          run' sourceStartShebangDoc rendererOpts+            { Renderer.ignoreShebang = False+            , Renderer.renderCode    = False+            }+    ]++------------------------------------------------------------------------------++sourceStartShebangCode :: Text+sourceStartShebangCode = TL.unlines+    [ "#!/usr/bin/env stack"+    , "> {- stack script --resolver lts-17.11 -}"+    , ">"+    , "> module Main (main) where"+    , ">"+    , "> main :: IO ()"+    , "> main = putStrLn \"Hello!\""+    , ""+    , "Test"+    ]++targetStartShebangCodeIgnore :: Text+targetStartShebangCodeIgnore = TL.unlines+    [ "``` {.haskell .numberSource startFrom=\"2\"}"+    , "{- stack script --resolver lts-17.11 -}"+    , ""+    , "module Main (main) where"+    , ""+    , "main :: IO ()"+    , "main = putStrLn \"Hello!\""+    , "```"+    , ""+    , "Test"+    ]++targetStartShebangCodeNoIgnore :: Text+targetStartShebangCodeNoIgnore = TL.unlines+    [ "``` {.numberSource startFrom=\"1\"}"+    , "#!/usr/bin/env stack"+    , "```"+    , ""+    , "``` {.haskell .numberSource startFrom=\"2\"}"+    , "{- stack script --resolver lts-17.11 -}"+    , ""+    , "module Main (main) where"+    , ""+    , "main :: IO ()"+    , "main = putStrLn \"Hello!\""+    , "```"+    , ""+    , "Test"+    ]++targetStartShebangCodeNoCode :: Text+targetStartShebangCodeNoCode = TL.unlines+    [ "Test"+    ]++testStartShebangCode :: TestTree+testStartShebangCode = testGroup "startShebangCode"+    [ testCase "Ignore" $+        targetStartShebangCodeIgnore @=? run sourceStartShebangCode+    , testCase "NoIgnore" $+        targetStartShebangCodeNoIgnore @=?+          run' sourceStartShebangCode rendererOpts+            { Renderer.ignoreShebang = False+            }+    , testCase "NoCode" $+        targetStartShebangCodeNoCode @=?+          run' sourceStartShebangCode rendererOpts+            { Renderer.ignoreShebang = False+            , Renderer.renderCode    = False+            }+    ]++------------------------------------------------------------------------------++sourceStartDoc :: Text+sourceStartDoc = TL.unlines+    [ "Test"+    , ""+    , "> module Main (main) where"+    , ">"+    , "> main :: IO ()"+    , "> main = putStrLn \"Hello!\""+    ]++targetStartDoc :: Text+targetStartDoc = TL.unlines+    [ "Test"+    , ""+    , "``` {.haskell .numberSource startFrom=\"3\"}"+    , "module Main (main) where"+    , ""+    , "main :: IO ()"+    , "main = putStrLn \"Hello!\""+    , "```"+    ]++testStartDoc :: TestTree+testStartDoc = testCase "startDoc" $+    targetStartDoc @=? run sourceStartDoc++------------------------------------------------------------------------------++sourceStartCode :: Text+sourceStartCode = TL.unlines+    [ "> module Main (main) where"+    , ">"+    , "> main :: IO ()"+    , "> main = putStrLn \"Hello!\""+    , ""+    , "Test"+    ]++targetStartCode :: Text+targetStartCode = TL.unlines+    [ "``` {.haskell .numberSource startFrom=\"1\"}"+    , "module Main (main) where"+    , ""+    , "main :: IO ()"+    , "main = putStrLn \"Hello!\""+    , "```"+    , ""+    , "Test"+    ]++testStartCode :: TestTree+testStartCode = testCase "startCode" $+    targetStartCode @=? run sourceStartCode++------------------------------------------------------------------------------++sourceStartDocBlank :: Text+sourceStartDocBlank = TL.unlines+    [ ""+    , "Test"+    , ""+    , "> module Main (main) where"+    , ">"+    , "> main :: IO ()"+    , "> main = putStrLn \"Hello!\""+    ]++targetStartDocBlank :: Text+targetStartDocBlank = TL.unlines+    [ "Test"+    , ""+    , "``` {.haskell .numberSource startFrom=\"4\"}"+    , "module Main (main) where"+    , ""+    , "main :: IO ()"+    , "main = putStrLn \"Hello!\""+    , "```"+    ]++testStartDocBlank :: TestTree+testStartDocBlank = testCase "startDocBlank" $+    targetStartDocBlank @=? run sourceStartDocBlank++------------------------------------------------------------------------------++sourceStartCodeBlank :: Text+sourceStartCodeBlank = TL.unlines+    [ ">"+    , "> module Main (main) where"+    , ">"+    , "> main :: IO ()"+    , "> main = putStrLn \"Hello!\""+    , ""+    , "Test"+    ]++targetStartCodeBlank :: Text+targetStartCodeBlank = TL.unlines+    [ "``` {.haskell .numberSource startFrom=\"2\"}"+    , "module Main (main) where"+    , ""+    , "main :: IO ()"+    , "main = putStrLn \"Hello!\""+    , "```"+    , ""+    , "Test"+    ]++testStartCodeBlank :: TestTree+testStartCodeBlank = testCase "startCodeBlank" $+    targetStartCodeBlank @=? run sourceStartCodeBlank++------------------------------------------------------------------------------++sourceCodeDoc :: Text+sourceCodeDoc = TL.unlines+    [ "> module Main (main) where"+    , ">"+    , "> main :: IO ()"+    , "> main = putStrLn \"Hello!\""+    , "Test"+    ]++targetCodeDoc :: Text+targetCodeDoc = TL.unlines+    [ "``` {.haskell .numberSource startFrom=\"1\"}"+    , "module Main (main) where"+    , ""+    , "main :: IO ()"+    , "main = putStrLn \"Hello!\""+    , "```"+    , ""+    , "Test"+    ]++testCodeDoc :: TestTree+testCodeDoc = testCase "codeDoc" $+    targetCodeDoc @=? run sourceCodeDoc++------------------------------------------------------------------------------++sourceCodeCodeBlanksCode :: Text+sourceCodeCodeBlanksCode = TL.unlines+    [ "> module Main (main) where"+    , ">"+    , ">"+    , "> main :: IO ()"+    , "> main = putStrLn \"Hello!\""+    ]++targetCodeCodeBlanksCode :: Text+targetCodeCodeBlanksCode = TL.unlines+    [ "``` {.haskell .numberSource startFrom=\"1\"}"+    , "module Main (main) where"+    , ""+    , ""+    , "main :: IO ()"+    , "main = putStrLn \"Hello!\""+    , "```"+    ]++testCodeCodeBlanksCode :: TestTree+testCodeCodeBlanksCode = testCase "codeCodeBlanksCode" $+    targetCodeCodeBlanksCode @=? run sourceCodeCodeBlanksCode++------------------------------------------------------------------------------++sourceCodeCodeBlanksDoc :: Text+sourceCodeCodeBlanksDoc = TL.unlines+    [ "> module Main (main) where"+    , ">"+    , "> main :: IO ()"+    , "> main = putStrLn \"Hello!\""+    , ">"+    , ">"+    , "Test"+    ]++targetCodeCodeBlanksDoc :: Text+targetCodeCodeBlanksDoc = TL.unlines+    [ "``` {.haskell .numberSource startFrom=\"1\"}"+    , "module Main (main) where"+    , ""+    , "main :: IO ()"+    , "main = putStrLn \"Hello!\""+    , "```"+    , ""+    , "Test"+    ]++testCodeCodeBlanksDoc :: TestTree+testCodeCodeBlanksDoc = testCase "codeCodeBlanksDoc" $+    targetCodeCodeBlanksDoc @=? run sourceCodeCodeBlanksDoc++------------------------------------------------------------------------------++sourceCodeDocBlanksDoc :: Text+sourceCodeDocBlanksDoc = TL.unlines+    [ "> module Main (main) where"+    , ">"+    , "> main :: IO ()"+    , "> main = putStrLn \"Hello!\""+    , ""+    , ""+    , "Test"+    ]++targetCodeDocBlanksDoc :: Text+targetCodeDocBlanksDoc = TL.unlines+    [ "``` {.haskell .numberSource startFrom=\"1\"}"+    , "module Main (main) where"+    , ""+    , "main :: IO ()"+    , "main = putStrLn \"Hello!\""+    , "```"+    , ""+    , "Test"+    ]++testCodeDocBlanksDoc :: TestTree+testCodeDocBlanksDoc = testCase "codeDocBlanksDoc" $+    targetCodeDocBlanksDoc @=? run sourceCodeDocBlanksDoc++------------------------------------------------------------------------------++sourceCodeDocBlanksCode :: Text+sourceCodeDocBlanksCode = TL.unlines+    [ "> module Main (main) where"+    , ""+    , ""+    , "> main :: IO ()"+    , "> main = putStrLn \"Hello!\""+    ]++targetCodeDocBlanksCode :: Text+targetCodeDocBlanksCode = TL.unlines+    [ "``` {.haskell .numberSource startFrom=\"1\"}"+    , "module Main (main) where"+    , "```"+    , ""+    , "``` {.haskell .numberSource startFrom=\"4\"}"+    , "main :: IO ()"+    , "main = putStrLn \"Hello!\""+    , "```"+    ]++testCodeDocBlanksCode :: TestTree+testCodeDocBlanksCode = testCase "codeDocBlanksCode" $+    targetCodeDocBlanksCode @=? run sourceCodeDocBlanksCode++------------------------------------------------------------------------------++sourceDocCode :: Text+sourceDocCode = TL.unlines+    [ "Test"+    , "> module Main (main) where"+    , ">"+    , "> main :: IO ()"+    , "> main = putStrLn \"Hello!\""+    ]++targetDocCode :: Text+targetDocCode = TL.unlines+    [ "Test"+    , ""+    , "``` {.haskell .numberSource startFrom=\"2\"}"+    , "module Main (main) where"+    , ""+    , "main :: IO ()"+    , "main = putStrLn \"Hello!\""+    , "```"+    ]++testDocCode :: TestTree+testDocCode = testCase "docCode" $+    targetDocCode @=? run sourceDocCode++------------------------------------------------------------------------------++sourceDocDocBlanksDoc :: Text+sourceDocDocBlanksDoc = TL.unlines+    [ "one"+    , ""+    , ""+    , "two"+    ]++targetDocDocBlanksDoc :: Text+targetDocDocBlanksDoc = TL.unlines+    [ "one"+    , ""+    , "two"+    ]++testDocDocBlanksDoc :: TestTree+testDocDocBlanksDoc = testCase "docDocBlanksDoc" $+    targetDocDocBlanksDoc @=? run sourceDocDocBlanksDoc++------------------------------------------------------------------------------++sourceDocDocBlanksCode :: Text+sourceDocDocBlanksCode = TL.unlines+    [ "Test"+    , ""+    , ""+    , "> module Main (main) where"+    , ">"+    , "> main :: IO ()"+    , "> main = putStrLn \"Hello!\""+    ]++targetDocDocBlanksCode :: Text+targetDocDocBlanksCode = TL.unlines+    [ "Test"+    , ""+    , "``` {.haskell .numberSource startFrom=\"4\"}"+    , "module Main (main) where"+    , ""+    , "main :: IO ()"+    , "main = putStrLn \"Hello!\""+    , "```"+    ]++testDocDocBlanksCode :: TestTree+testDocDocBlanksCode = testCase "docDocBlanksCode" $+    targetDocDocBlanksCode @=? run sourceDocDocBlanksCode++------------------------------------------------------------------------------++sourceDocCodeBlanksCode :: Text+sourceDocCodeBlanksCode = TL.unlines+    [ "Test"+    , ">"+    , ">"+    , "> module Main (main) where"+    , ">"+    , "> main :: IO ()"+    , "> main = putStrLn \"Hello!\""+    ]++targetDocCodeBlanksCode :: Text+targetDocCodeBlanksCode = TL.unlines+    [ "Test"+    , ""+    , "``` {.haskell .numberSource startFrom=\"4\"}"+    , "module Main (main) where"+    , ""+    , "main :: IO ()"+    , "main = putStrLn \"Hello!\""+    , "```"+    ]++testDocCodeBlanksCode :: TestTree+testDocCodeBlanksCode = testCase "docCodeBlanksCode" $+    targetDocCodeBlanksCode @=? run sourceDocCodeBlanksCode++------------------------------------------------------------------------------++sourceDocCodeBlanksDoc :: Text+sourceDocCodeBlanksDoc = TL.unlines+    [ "one"+    , ">"+    , ">"+    , "two"+    ]++targetDocCodeBlanksDoc :: Text+targetDocCodeBlanksDoc = TL.unlines+    [ "one"+    , ""+    , "two"+    ]++testDocCodeBlanksDoc :: TestTree+testDocCodeBlanksDoc = testCase "docCodeBlanksDoc" $+    targetDocCodeBlanksDoc @=? run sourceDocCodeBlanksDoc++------------------------------------------------------------------------------++sourceEndDoc :: Text+sourceEndDoc = TL.unlines+    [ "Test"+    ]++targetEndDoc :: Text+targetEndDoc = TL.unlines+    [ "Test"+    ]++testEndDoc :: TestTree+testEndDoc = testCase "endDoc" $+    targetEndDoc @=? run sourceEndDoc++------------------------------------------------------------------------------++sourceEndDocBlanks :: Text+sourceEndDocBlanks = TL.unlines+    [ ""+    , ""+    ]++targetEndDocBlanks :: Text+targetEndDocBlanks = ""++testEndDocBlanks :: TestTree+testEndDocBlanks = testCase "endDocBlanks" $+    targetEndDocBlanks @=? run sourceEndDocBlanks++------------------------------------------------------------------------------++sourceEndCode :: Text+sourceEndCode = TL.unlines+    [ "> module Main (main) where"+    , ">"+    , "> main :: IO ()"+    , "> main = putStrLn \"Hello!\""+    ]++targetEndCode :: Text+targetEndCode = TL.unlines+    [ "``` {.haskell .numberSource startFrom=\"1\"}"+    , "module Main (main) where"+    , ""+    , "main :: IO ()"+    , "main = putStrLn \"Hello!\""+    , "```"+    ]++testEndCode :: TestTree+testEndCode = testCase "endCode" $+    targetEndCode @=? run sourceEndCode++------------------------------------------------------------------------------++sourceEndCodeBlanks :: Text+sourceEndCodeBlanks = TL.unlines+    [ "> module Main (main) where"+    , ">"+    , "> main :: IO ()"+    , "> main = putStrLn \"Hello!\""+    , ">"+    , ">"+    ]++targetEndCodeBlanks :: Text+targetEndCodeBlanks = TL.unlines+    [ "``` {.haskell .numberSource startFrom=\"1\"}"+    , "module Main (main) where"+    , ""+    , "main :: IO ()"+    , "main = putStrLn \"Hello!\""+    , "```"+    ]++testEndCodeBlanks :: TestTree+testEndCodeBlanks = testCase "endCodeBlanks" $+    targetEndCodeBlanks @=? run sourceEndCodeBlanks++------------------------------------------------------------------------------++sourceEmpty :: Text+sourceEmpty = ""++sourceOnlyDocBlanks :: Text+sourceOnlyDocBlanks = TL.unlines+    [ ""+    , ""+    ]++sourceOnlyCodeBlanks :: Text+sourceOnlyCodeBlanks = TL.unlines+    [ ">"+    , ">"+    ]++targetEmpty :: Text+targetEmpty = ""++testEmpty :: TestTree+testEmpty = testCase "empty" $+    targetEmpty @=? run sourceEmpty++testOnlyDocBlanks :: TestTree+testOnlyDocBlanks = testCase "onlyDocBlanks" $+    targetEmpty @=? run sourceOnlyDocBlanks++testOnlyCodeBlanks :: TestTree+testOnlyCodeBlanks = testCase "onlyCodeBlanks" $+    targetEmpty @=? run sourceOnlyCodeBlanks++------------------------------------------------------------------------------++tests :: TestTree+tests = testGroup "LiterateHaskell"+    [ testStartShebangDoc+    , testStartShebangCode+    , testStartDoc+    , testStartCode+    , testStartDocBlank+    , testStartCodeBlank+    , testCodeDoc+    , testCodeCodeBlanksCode+    , testCodeCodeBlanksDoc+    , testCodeDocBlanksDoc+    , testCodeDocBlanksCode+    , testDocCode+    , testDocDocBlanksDoc+    , testDocDocBlanksCode+    , testDocCodeBlanksCode+    , testDocCodeBlanksDoc+    , testEndDoc+    , testEndDocBlanks+    , testEndCode+    , testEndCodeBlanks+    , testEmpty+    , testOnlyDocBlanks+    , testOnlyCodeBlanks+    ]
+ test/LiterateX/Test/SourceFormat/Percent.hs view
@@ -0,0 +1,860 @@+{-# LANGUAGE OverloadedStrings #-}++module LiterateX.Test.SourceFormat.Percent (tests) where++-- https://hackage.haskell.org/package/tasty+import Test.Tasty (TestTree, testGroup)++-- https://hackage.haskell.org/package/tasty-hunit+import Test.Tasty.HUnit ((@=?), testCase)++-- https://hackage.haskell.org/package/text+import qualified Data.Text.Lazy as TL+import Data.Text.Lazy (Text)++-- (literatex)+import qualified LiterateX+import qualified LiterateX.Renderer as Renderer+import qualified LiterateX.Types.SourceFormat as SourceFormat+import qualified LiterateX.Types.TargetFormat as TargetFormat++------------------------------------------------------------------------------++rendererOpts :: Renderer.Options+rendererOpts = Renderer.Options+    { Renderer.targetFormat    = TargetFormat.PandocMarkdown+    , Renderer.codeLanguage    = Just "erlang"+    , Renderer.ignoreShebang   = True+    , Renderer.renderCode      = True+    , Renderer.numberCodeLines = True+    }++run' :: Text -> Renderer.Options -> Text+run' = flip $ LiterateX.transformTextToText SourceFormat.Percent++run :: Text -> Text+run source = run' source rendererOpts++------------------------------------------------------------------------------++sourceStartShebangDoc :: Text+sourceStartShebangDoc = TL.unlines+    [ "#!/usr/bin/env escript"+    , ""+    , "% Test"+    , ""+    , "-module(main)."+    , "-export([start/0])."+    , ""+    , "start() -> io:fwrite(\"Hello!\\n\")."+    ]++targetStartShebangDocIgnore :: Text+targetStartShebangDocIgnore = TL.unlines+    [ "Test"+    , ""+    , "``` {.erlang .numberSource startFrom=\"5\"}"+    , "-module(main)."+    , "-export([start/0])."+    , ""+    , "start() -> io:fwrite(\"Hello!\\n\")."+    , "```"+    ]++targetStartShebangDocNoIgnore :: Text+targetStartShebangDocNoIgnore = TL.unlines+    [ "``` {.numberSource startFrom=\"1\"}"+    , "#!/usr/bin/env escript"+    , "```"+    , ""+    , "Test"+    , ""+    , "``` {.erlang .numberSource startFrom=\"5\"}"+    , "-module(main)."+    , "-export([start/0])."+    , ""+    , "start() -> io:fwrite(\"Hello!\\n\")."+    , "```"+    ]++targetStartShebangDocNoCode :: Text+targetStartShebangDocNoCode = TL.unlines+    [ "Test"+    ]++testStartShebangDoc :: TestTree+testStartShebangDoc = testGroup "startShebangDoc"+    [ testCase "Ignore" $+        targetStartShebangDocIgnore @=? run sourceStartShebangDoc+    , testCase "NoIgnore" $+        targetStartShebangDocNoIgnore @=?+          run' sourceStartShebangDoc rendererOpts+            { Renderer.ignoreShebang = False+            }+    , testCase "NoCode" $+        targetStartShebangDocNoCode @=?+          run' sourceStartShebangDoc rendererOpts+            { Renderer.ignoreShebang = False+            , Renderer.renderCode    = False+            }+    ]++------------------------------------------------------------------------------++sourceStartShebangCode :: Text+sourceStartShebangCode = TL.unlines+    [ "#!/usr/bin/env escript"+    , ""+    , "-module(main)."+    , "-export([start/0])."+    , ""+    , "start() -> io:fwrite(\"Hello!\\n\")."+    , ""+    , "% Test"+    ]++targetStartShebangCodeIgnore :: Text+targetStartShebangCodeIgnore = TL.unlines+    [ "``` {.erlang .numberSource startFrom=\"3\"}"+    , "-module(main)."+    , "-export([start/0])."+    , ""+    , "start() -> io:fwrite(\"Hello!\\n\")."+    , "```"+    , ""+    , "Test"+    ]++targetStartShebangCodeNoIgnore :: Text+targetStartShebangCodeNoIgnore = TL.unlines+    [ "``` {.numberSource startFrom=\"1\"}"+    , "#!/usr/bin/env escript"+    , "```"+    , ""+    , "``` {.erlang .numberSource startFrom=\"3\"}"+    , "-module(main)."+    , "-export([start/0])."+    , ""+    , "start() -> io:fwrite(\"Hello!\\n\")."+    , "```"+    , ""+    , "Test"+    ]++targetStartShebangCodeNoCode :: Text+targetStartShebangCodeNoCode = TL.unlines+    [ "Test"+    ]++testStartShebangCode :: TestTree+testStartShebangCode = testGroup "startShebangCode"+    [ testCase "Ignore" $+        targetStartShebangCodeIgnore @=? run sourceStartShebangCode+    , testCase "NoIgnore" $+        targetStartShebangCodeNoIgnore @=?+          run' sourceStartShebangCode rendererOpts+            { Renderer.ignoreShebang = False+            }+    , testCase "NoCode" $+        targetStartShebangCodeNoCode @=?+          run' sourceStartShebangCode rendererOpts+            { Renderer.ignoreShebang = False+            , Renderer.renderCode    = False+            }+    ]++------------------------------------------------------------------------------++sourceStartDoc :: Text+sourceStartDoc = TL.unlines+    [ "% Test"+    , ""+    , "-module(main)."+    , "-export([start/0])."+    , ""+    , "start() -> io:fwrite(\"Hello!\\n\")."+    ]++targetStartDoc :: Text+targetStartDoc = TL.unlines+    [ "Test"+    , ""+    , "``` {.erlang .numberSource startFrom=\"3\"}"+    , "-module(main)."+    , "-export([start/0])."+    , ""+    , "start() -> io:fwrite(\"Hello!\\n\")."+    , "```"+    ]++testStartDoc :: TestTree+testStartDoc = testCase "startDoc" $+    targetStartDoc @=? run sourceStartDoc++------------------------------------------------------------------------------++sourceStartCode :: Text+sourceStartCode = TL.unlines+    [ "-module(main)."+    , "-export([start/0])."+    , ""+    , "start() -> io:fwrite(\"Hello!\\n\")."+    , ""+    , "% Test"+    ]++targetStartCode :: Text+targetStartCode = TL.unlines+    [ "``` {.erlang .numberSource startFrom=\"1\"}"+    , "-module(main)."+    , "-export([start/0])."+    , ""+    , "start() -> io:fwrite(\"Hello!\\n\")."+    , "```"+    , ""+    , "Test"+    ]++testStartCode :: TestTree+testStartCode = testCase "startCode" $+    targetStartCode @=? run sourceStartCode++------------------------------------------------------------------------------++sourceStartDocBlank :: Text+sourceStartDocBlank = TL.unlines+    [ "%"+    , "% Test"+    , ""+    , "-module(main)."+    , "-export([start/0])."+    , ""+    , "start() -> io:fwrite(\"Hello!\\n\")."+    ]++targetStartDocBlank :: Text+targetStartDocBlank = TL.unlines+    [ "Test"+    , ""+    , "``` {.erlang .numberSource startFrom=\"4\"}"+    , "-module(main)."+    , "-export([start/0])."+    , ""+    , "start() -> io:fwrite(\"Hello!\\n\")."+    , "```"+    ]++testStartDocBlank :: TestTree+testStartDocBlank = testCase "startDocBlank" $+    targetStartDocBlank @=? run sourceStartDocBlank++------------------------------------------------------------------------------++sourceStartCodeBlank :: Text+sourceStartCodeBlank = TL.unlines+    [ ""+    , "-module(main)."+    , "-export([start/0])."+    , ""+    , "start() -> io:fwrite(\"Hello!\\n\")."+    , ""+    , "% Test"+    ]++targetStartCodeBlank :: Text+targetStartCodeBlank = TL.unlines+    [ "``` {.erlang .numberSource startFrom=\"2\"}"+    , "-module(main)."+    , "-export([start/0])."+    , ""+    , "start() -> io:fwrite(\"Hello!\\n\")."+    , "```"+    , ""+    , "Test"+    ]++testStartCodeBlank :: TestTree+testStartCodeBlank = testCase "startCodeBlank" $+    targetStartCodeBlank @=? run sourceStartCodeBlank++------------------------------------------------------------------------------++sourceStartRuleDoc :: Text+sourceStartRuleDoc = TL.unlines+    [ "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%"+    , "% Test"+    , ""+    , "-module(main)."+    , "-export([start/0])."+    , ""+    , "start() -> io:fwrite(\"Hello!\\n\")."+    ]++targetStartRuleDoc :: Text+targetStartRuleDoc = TL.unlines+    [ "Test"+    , ""+    , "``` {.erlang .numberSource startFrom=\"4\"}"+    , "-module(main)."+    , "-export([start/0])."+    , ""+    , "start() -> io:fwrite(\"Hello!\\n\")."+    , "```"+    ]++testStartRuleDoc :: TestTree+testStartRuleDoc = testCase "startRuleDoc" $+    targetStartRuleDoc @=? run sourceStartRuleDoc++------------------------------------------------------------------------------++sourceStartRuleCode :: Text+sourceStartRuleCode = TL.unlines+    [ "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%"+    , ""+    , "-module(main)."+    , "-export([start/0])."+    , ""+    , "start() -> io:fwrite(\"Hello!\\n\")."+    , ""+    , "% Test"+    ]++targetStartRuleCode :: Text+targetStartRuleCode = TL.unlines+    [ "``` {.erlang .numberSource startFrom=\"3\"}"+    , "-module(main)."+    , "-export([start/0])."+    , ""+    , "start() -> io:fwrite(\"Hello!\\n\")."+    , "```"+    , ""+    , "Test"+    ]++testStartRuleCode :: TestTree+testStartRuleCode = testCase "startRuleCode" $+    targetStartRuleCode @=? run sourceStartRuleCode++------------------------------------------------------------------------------++sourceCodeDoc :: Text+sourceCodeDoc = TL.unlines+    [ "-module(main)."+    , "-export([start/0])."+    , ""+    , "start() -> io:fwrite(\"Hello!\\n\")."+    , "% Test"+    ]++targetCodeDoc :: Text+targetCodeDoc = TL.unlines+    [ "``` {.erlang .numberSource startFrom=\"1\"}"+    , "-module(main)."+    , "-export([start/0])."+    , ""+    , "start() -> io:fwrite(\"Hello!\\n\")."+    , "```"+    , ""+    , "Test"+    ]++testCodeDoc :: TestTree+testCodeDoc = testCase "codeDoc" $+    targetCodeDoc @=? run sourceCodeDoc++------------------------------------------------------------------------------++sourceCodeCodeBlanksCode :: Text+sourceCodeCodeBlanksCode = TL.unlines+    [ "-module(main)."+    , "-export([start/0])."+    , ""+    , ""+    , "start() -> io:fwrite(\"Hello!\\n\")."+    ]++targetCodeCodeBlanksCode :: Text+targetCodeCodeBlanksCode = TL.unlines+    [ "``` {.erlang .numberSource startFrom=\"1\"}"+    , "-module(main)."+    , "-export([start/0])."+    , ""+    , ""+    , "start() -> io:fwrite(\"Hello!\\n\")."+    , "```"+    ]++testCodeCodeBlanksCode :: TestTree+testCodeCodeBlanksCode = testCase "codeCodeBlanksCode" $+    targetCodeCodeBlanksCode @=? run sourceCodeCodeBlanksCode++------------------------------------------------------------------------------++sourceCodeCodeBlanksDoc :: Text+sourceCodeCodeBlanksDoc = TL.unlines+    [ "-module(main)."+    , "-export([start/0])."+    , ""+    , "start() -> io:fwrite(\"Hello!\\n\")."+    , ""+    , ""+    , "% Test"+    ]++targetCodeCodeBlanksDoc :: Text+targetCodeCodeBlanksDoc = TL.unlines+    [ "``` {.erlang .numberSource startFrom=\"1\"}"+    , "-module(main)."+    , "-export([start/0])."+    , ""+    , "start() -> io:fwrite(\"Hello!\\n\")."+    , "```"+    , ""+    , "Test"+    ]++testCodeCodeBlanksDoc :: TestTree+testCodeCodeBlanksDoc = testCase "codeCodeBlanksDoc" $+    targetCodeCodeBlanksDoc @=? run sourceCodeCodeBlanksDoc++------------------------------------------------------------------------------++sourceCodeDocBlanksDoc :: Text+sourceCodeDocBlanksDoc = TL.unlines+    [ "-module(main)."+    , "-export([start/0])."+    , ""+    , "start() -> io:fwrite(\"Hello!\\n\")."+    , "%"+    , "%"+    , "% Test"+    ]++targetCodeDocBlanksDoc :: Text+targetCodeDocBlanksDoc = TL.unlines+    [ "``` {.erlang .numberSource startFrom=\"1\"}"+    , "-module(main)."+    , "-export([start/0])."+    , ""+    , "start() -> io:fwrite(\"Hello!\\n\")."+    , "```"+    , ""+    , "Test"+    ]++testCodeDocBlanksDoc :: TestTree+testCodeDocBlanksDoc = testCase "codeDocBlanksDoc" $+    targetCodeDocBlanksDoc @=? run sourceCodeDocBlanksDoc++------------------------------------------------------------------------------++sourceCodeDocBlanksCode :: Text+sourceCodeDocBlanksCode = TL.unlines+    [ "-module(main)."+    , "-export([start/0])."+    , "%"+    , "%"+    , "start() -> io:fwrite(\"Hello!\\n\")."+    ]++targetCodeDocBlanksCode :: Text+targetCodeDocBlanksCode = TL.unlines+    [ "``` {.erlang .numberSource startFrom=\"1\"}"+    , "-module(main)."+    , "-export([start/0])."+    , "```"+    , ""+    , "``` {.erlang .numberSource startFrom=\"5\"}"+    , "start() -> io:fwrite(\"Hello!\\n\")."+    , "```"+    ]++testCodeDocBlanksCode :: TestTree+testCodeDocBlanksCode = testCase "codeDocBlanksCode" $+    targetCodeDocBlanksCode @=? run sourceCodeDocBlanksCode++------------------------------------------------------------------------------++sourceCodeRuleCode :: Text+sourceCodeRuleCode = TL.unlines+    [ "-module(main)."+    , "-export([start/0])."+    , "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%"+    , "start() -> io:fwrite(\"Hello!\\n\")."+    ]++targetCodeRuleCode :: Text+targetCodeRuleCode = TL.unlines+    [ "``` {.erlang .numberSource startFrom=\"1\"}"+    , "-module(main)."+    , "-export([start/0])."+    , "```"+    , ""+    , "``` {.erlang .numberSource startFrom=\"4\"}"+    , "start() -> io:fwrite(\"Hello!\\n\")."+    , "```"+    ]++testCodeRuleCode :: TestTree+testCodeRuleCode = testCase "codeRuleCode" $+    targetCodeRuleCode @=? run sourceCodeRuleCode++------------------------------------------------------------------------------++sourceCodeRuleDoc :: Text+sourceCodeRuleDoc = TL.unlines+    [ "-module(main)."+    , "-export([start/0])."+    , ""+    , "start() -> io:fwrite(\"Hello!\\n\")."+    , "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%"+    , "% Test"+    ]++targetCodeRuleDoc :: Text+targetCodeRuleDoc = TL.unlines+    [ "``` {.erlang .numberSource startFrom=\"1\"}"+    , "-module(main)."+    , "-export([start/0])."+    , ""+    , "start() -> io:fwrite(\"Hello!\\n\")."+    , "```"+    , ""+    , "Test"+    ]++testCodeRuleDoc :: TestTree+testCodeRuleDoc = testCase "codeRuleDoc" $+    targetCodeRuleDoc @=? run sourceCodeRuleDoc++------------------------------------------------------------------------------++sourceDocCode :: Text+sourceDocCode = TL.unlines+    [ "% Test"+    , "-module(main)."+    , "-export([start/0])."+    , ""+    , "start() -> io:fwrite(\"Hello!\\n\")."+    ]++targetDocCode :: Text+targetDocCode = TL.unlines+    [ "Test"+    , ""+    , "``` {.erlang .numberSource startFrom=\"2\"}"+    , "-module(main)."+    , "-export([start/0])."+    , ""+    , "start() -> io:fwrite(\"Hello!\\n\")."+    , "```"+    ]++testDocCode :: TestTree+testDocCode = testCase "docCode" $+    targetDocCode @=? run sourceDocCode++------------------------------------------------------------------------------++sourceDocDocBlanksDoc :: Text+sourceDocDocBlanksDoc = TL.unlines+    [ "% one"+    , "%"+    , "%"+    , "% two"+    ]++targetDocDocBlanksDoc :: Text+targetDocDocBlanksDoc = TL.unlines+    [ "one"+    , ""+    , "two"+    ]++testDocDocBlanksDoc :: TestTree+testDocDocBlanksDoc = testCase "docDocBlanksDoc" $+    targetDocDocBlanksDoc @=? run sourceDocDocBlanksDoc++------------------------------------------------------------------------------++sourceDocDocBlanksCode :: Text+sourceDocDocBlanksCode = TL.unlines+    [ "% Test"+    , "%"+    , "%"+    , "-module(main)."+    , "-export([start/0])."+    , ""+    , "start() -> io:fwrite(\"Hello!\\n\")."+    ]++targetDocDocBlanksCode :: Text+targetDocDocBlanksCode = TL.unlines+    [ "Test"+    , ""+    , "``` {.erlang .numberSource startFrom=\"4\"}"+    , "-module(main)."+    , "-export([start/0])."+    , ""+    , "start() -> io:fwrite(\"Hello!\\n\")."+    , "```"+    ]++testDocDocBlanksCode :: TestTree+testDocDocBlanksCode = testCase "docDocBlanksCode" $+    targetDocDocBlanksCode @=? run sourceDocDocBlanksCode++------------------------------------------------------------------------------++sourceDocCodeBlanksCode :: Text+sourceDocCodeBlanksCode = TL.unlines+    [ "% Test"+    , ""+    , ""+    , "-module(main)."+    , "-export([start/0])."+    , ""+    , "start() -> io:fwrite(\"Hello!\\n\")."+    ]++targetDocCodeBlanksCode :: Text+targetDocCodeBlanksCode = TL.unlines+    [ "Test"+    , ""+    , "``` {.erlang .numberSource startFrom=\"4\"}"+    , "-module(main)."+    , "-export([start/0])."+    , ""+    , "start() -> io:fwrite(\"Hello!\\n\")."+    , "```"+    ]++testDocCodeBlanksCode :: TestTree+testDocCodeBlanksCode = testCase "docCodeBlanksCode" $+    targetDocCodeBlanksCode @=? run sourceDocCodeBlanksCode++------------------------------------------------------------------------------++sourceDocCodeBlanksDoc :: Text+sourceDocCodeBlanksDoc = TL.unlines+    [ "% one"+    , ""+    , ""+    , "% two"+    ]++targetDocCodeBlanksDoc :: Text+targetDocCodeBlanksDoc = TL.unlines+    [ "one"+    , ""+    , "two"+    ]++testDocCodeBlanksDoc :: TestTree+testDocCodeBlanksDoc = testCase "docCodeBlanksDoc" $+    targetDocCodeBlanksDoc @=? run sourceDocCodeBlanksDoc++------------------------------------------------------------------------------++sourceDocRuleDoc :: Text+sourceDocRuleDoc = TL.unlines+    [ "% one"+    , "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%"+    , "% two"+    ]++targetDocRuleDoc :: Text+targetDocRuleDoc = TL.unlines+    [ "one"+    , ""+    , "two"+    ]++testDocRuleDoc :: TestTree+testDocRuleDoc = testCase "docRuleDoc" $+    targetDocRuleDoc @=? run sourceDocRuleDoc++------------------------------------------------------------------------------++sourceDocRuleCode :: Text+sourceDocRuleCode = TL.unlines+    [ "% Test"+    , "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%"+    , "-module(main)."+    , "-export([start/0])."+    , ""+    , "start() -> io:fwrite(\"Hello!\\n\")."+    ]++targetDocRuleCode :: Text+targetDocRuleCode = TL.unlines+    [ "Test"+    , ""+    , "``` {.erlang .numberSource startFrom=\"3\"}"+    , "-module(main)."+    , "-export([start/0])."+    , ""+    , "start() -> io:fwrite(\"Hello!\\n\")."+    , "```"+    ]++testDocRuleCode :: TestTree+testDocRuleCode = testCase "docRuleCode" $+    targetDocRuleCode @=? run sourceDocRuleCode++------------------------------------------------------------------------------++sourceEndDoc :: Text+sourceEndDoc = TL.unlines+    [ "% Test"+    ]++targetEndDoc :: Text+targetEndDoc = TL.unlines+    [ "Test"+    ]++testEndDoc :: TestTree+testEndDoc = testCase "endDoc" $+    targetEndDoc @=? run sourceEndDoc++------------------------------------------------------------------------------++sourceEndDocBlanks :: Text+sourceEndDocBlanks = TL.unlines+    [ "%"+    , "%"+    ]++targetEndDocBlanks :: Text+targetEndDocBlanks = ""++testEndDocBlanks :: TestTree+testEndDocBlanks = testCase "endDocBlanks" $+    targetEndDocBlanks @=? run sourceEndDocBlanks++------------------------------------------------------------------------------++sourceEndCode :: Text+sourceEndCode = TL.unlines+    [ "-module(main)."+    , "-export([start/0])."+    , ""+    , "start() -> io:fwrite(\"Hello!\\n\")."+    ]++targetEndCode :: Text+targetEndCode = TL.unlines+    [ "``` {.erlang .numberSource startFrom=\"1\"}"+    , "-module(main)."+    , "-export([start/0])."+    , ""+    , "start() -> io:fwrite(\"Hello!\\n\")."+    , "```"+    ]++testEndCode :: TestTree+testEndCode = testCase "endCode" $+    targetEndCode @=? run sourceEndCode++------------------------------------------------------------------------------++sourceEndCodeBlanks :: Text+sourceEndCodeBlanks = TL.unlines+    [ "-module(main)."+    , "-export([start/0])."+    , ""+    , "start() -> io:fwrite(\"Hello!\\n\")."+    , ""+    , ""+    ]++targetEndCodeBlanks :: Text+targetEndCodeBlanks = TL.unlines+    [ "``` {.erlang .numberSource startFrom=\"1\"}"+    , "-module(main)."+    , "-export([start/0])."+    , ""+    , "start() -> io:fwrite(\"Hello!\\n\")."+    , "```"+    ]++testEndCodeBlanks :: TestTree+testEndCodeBlanks = testCase "endCodeBlanks" $+    targetEndCodeBlanks @=? run sourceEndCodeBlanks++------------------------------------------------------------------------------++sourceEmpty :: Text+sourceEmpty = ""++sourceOnlyRule :: Text+sourceOnlyRule = "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%"++sourceOnlyDocBlanks :: Text+sourceOnlyDocBlanks = TL.unlines+    [ "%"+    , "%"+    ]++sourceOnlyCodeBlanks :: Text+sourceOnlyCodeBlanks = TL.unlines+    [ ""+    , ""+    ]++targetEmpty :: Text+targetEmpty = ""++testEmpty :: TestTree+testEmpty = testCase "empty" $+    targetEmpty @=? run sourceEmpty++testOnlyRule :: TestTree+testOnlyRule = testCase "onlyRule" $+    targetEmpty @=? run sourceOnlyRule++testOnlyDocBlanks :: TestTree+testOnlyDocBlanks = testCase "onlyDocBlanks" $+    targetEmpty @=? run sourceOnlyDocBlanks++testOnlyCodeBlanks :: TestTree+testOnlyCodeBlanks = testCase "onlyCodeBlanks" $+    targetEmpty @=? run sourceOnlyCodeBlanks++------------------------------------------------------------------------------++tests :: TestTree+tests = testGroup "Percent"+    [ testStartShebangDoc+    , testStartShebangCode+    , testStartDoc+    , testStartCode+    , testStartDocBlank+    , testStartCodeBlank+    , testStartRuleDoc+    , testStartRuleCode+    , testCodeDoc+    , testCodeCodeBlanksCode+    , testCodeCodeBlanksDoc+    , testCodeDocBlanksDoc+    , testCodeDocBlanksCode+    , testCodeRuleCode+    , testCodeRuleDoc+    , testDocCode+    , testDocDocBlanksDoc+    , testDocDocBlanksCode+    , testDocCodeBlanksCode+    , testDocCodeBlanksDoc+    , testDocRuleDoc+    , testDocRuleCode+    , testEndDoc+    , testEndDocBlanks+    , testEndCode+    , testEndCodeBlanks+    , testEmpty+    , testOnlyRule+    , testOnlyDocBlanks+    , testOnlyCodeBlanks+    ]
+ test/LiterateX/Test/TargetFormat.hs view
@@ -0,0 +1,167 @@+{-# LANGUAGE OverloadedStrings #-}++module LiterateX.Test.TargetFormat (tests) where++-- https://hackage.haskell.org/package/tasty+import Test.Tasty (TestTree, testGroup)++-- https://hackage.haskell.org/package/tasty-hunit+import Test.Tasty.HUnit ((@=?), testCase)++-- https://hackage.haskell.org/package/text+import qualified Data.Text.Lazy as TL+import Data.Text.Lazy (Text)++-- (literatex)+import qualified LiterateX+import qualified LiterateX.Renderer as Renderer+import qualified LiterateX.Types.SourceFormat as SourceFormat+import qualified LiterateX.Types.TargetFormat as TargetFormat++------------------------------------------------------------------------------++sourceTL :: Text+sourceTL = TL.unlines+    [ "#!/usr/bin/env python"+    , ""+    , "# Test"+    , ""+    , "print('Hello!')"+    ]++------------------------------------------------------------------------------++pandocLangNum :: Text+pandocLangNum = TL.unlines+    [ "Test"+    , ""+    , "``` {.python .numberSource startFrom=\"5\"}"+    , "print('Hello!')"+    , "```"+    ]++pandocLangNoNum :: Text+pandocLangNoNum = TL.unlines+    [ "Test"+    , ""+    , "```python"+    , "print('Hello!')"+    , "```"+    ]++pandocNoLangNum :: Text+pandocNoLangNum = TL.unlines+    [ "Test"+    , ""+    , "``` {.numberSource startFrom=\"5\"}"+    , "print('Hello!')"+    , "```"+    ]++pandocNoLangNoNum :: Text+pandocNoLangNoNum = TL.unlines+    [ "Test"+    , ""+    , "```"+    , "print('Hello!')"+    , "```"+    ]++------------------------------------------------------------------------------++githubLangNum :: Text+githubLangNum = TL.unlines+    [ "Test"+    , ""+    , "``` python startline=5"+    , "print('Hello!')"+    , "```"+    ]++githubLangNoNum :: Text+githubLangNoNum = TL.unlines+    [ "Test"+    , ""+    , "``` python"+    , "print('Hello!')"+    , "```"+    ]++githubNoLangNum :: Text+githubNoLangNum = TL.unlines+    [ "Test"+    , ""+    , "``` startline=5"+    , "print('Hello!')"+    , "```"+    ]++githubNoLangNoNum :: Text+githubNoLangNoNum = TL.unlines+    [ "Test"+    , ""+    , "```"+    , "print('Hello!')"+    , "```"+    ]++------------------------------------------------------------------------------++rendererOpts :: Renderer.Options+rendererOpts = Renderer.Options+    { Renderer.targetFormat    = TargetFormat.PandocMarkdown+    , Renderer.codeLanguage    = Just "python"+    , Renderer.ignoreShebang   = True+    , Renderer.renderCode      = True+    , Renderer.numberCodeLines = True+    }++rendererOptsGFM :: Renderer.Options+rendererOptsGFM = rendererOpts+    { Renderer.targetFormat = TargetFormat.GitHubFlavoredMarkdown+    }++run' :: Renderer.Options -> Text+run' opts = LiterateX.transformTextToText SourceFormat.Hash opts sourceTL++------------------------------------------------------------------------------++testPandocMarkdown :: TestTree+testPandocMarkdown = testGroup "PandocMarkdown"+    [ testCase "LangNum" $ pandocLangNum @=? run' rendererOpts+    , testCase "LangNoNum" $ pandocLangNoNum @=? run' rendererOpts+        { Renderer.numberCodeLines = False+        }+    , testCase "NoLangNum" $ pandocNoLangNum @=? run' rendererOpts+        { Renderer.codeLanguage = Nothing+        }+    , testCase "NoLangNoNum" $ pandocNoLangNoNum @=? run' rendererOpts+        { Renderer.codeLanguage = Nothing+        , Renderer.numberCodeLines = False+        }+    ]++------------------------------------------------------------------------------++testGitHubFlavoredMarkdown :: TestTree+testGitHubFlavoredMarkdown = testGroup "GitHubFlavoredMarkdown"+    [ testCase "LangNum" $ githubLangNum @=? run' rendererOptsGFM+    , testCase "LangNoNum" $ githubLangNoNum @=? run' rendererOptsGFM+        { Renderer.numberCodeLines = False+        }+    , testCase "NoLangNum" $ githubNoLangNum @=? run' rendererOptsGFM+        { Renderer.codeLanguage = Nothing+        }+    , testCase "NoLangNoNum" $ githubNoLangNoNum @=? run' rendererOptsGFM+        { Renderer.codeLanguage = Nothing+        , Renderer.numberCodeLines = False+        }+    ]++------------------------------------------------------------------------------++tests :: TestTree+tests = testGroup "LiterateX.Test.TargetFormat"+    [ testPandocMarkdown+    , testGitHubFlavoredMarkdown+    ]
+ test/Spec.hs view
@@ -0,0 +1,18 @@+module Main (main) where++-- https://hackage.haskell.org/package/tasty+import Test.Tasty (defaultMain, testGroup)++-- (literatex)+import qualified LiterateX.Test.API+import qualified LiterateX.Test.SourceFormat+import qualified LiterateX.Test.TargetFormat++------------------------------------------------------------------------------++main :: IO ()+main = defaultMain $ testGroup "test"+    [ LiterateX.Test.API.tests+    , LiterateX.Test.SourceFormat.tests+    , LiterateX.Test.TargetFormat.tests+    ]