packages feed

pandoc-filter-indent 0.2.0.0 → 0.2.1.0

raw patch · 15 files changed

+120/−40 lines, 15 files

Files

ChangeLog.md view
@@ -1,9 +1,13 @@ # Changelog for pandoc-filter-indent  ## Unreleased changes-0.0.1 Dec 1 2020+0.1.0.0 Dec 1 2020   - Initial release supporting LaTeX and HTML output -0.0.2 Dec 2 2020+0.2.0.0 Dec 2 2020   - Preliminary support for Tikzmarks   - Updated README++0.2.1.0 Dec 2 2020+  - Extended code documentation+  - Cleanups
app/Main.hs view
@@ -1,14 +1,16 @@ {-# LANGUAGE OverloadedStrings #-}-+{-# LANGUAGE ViewPatterns      #-} module Main where  import           Text.Pandoc.JSON import           Text.Pandoc.Definition () import           Data.String (fromString, IsString)-import           Data.Text (Text)+import           Data.Maybe  (fromMaybe)+import           Data.Text   (Text) import qualified Data.Text as T---import Debug.Trace(trace) +import Debug.Trace(trace)+ import Token.Haskell import GHC.SyntaxHighlighter import Filter@@ -19,19 +21,26 @@  -- | Select the desired format output then process it. blockFormatter :: Maybe Format -> Block -> Block-blockFormatter  Nothing               (CodeBlock attrs content) = -- debugging mode-    codeFormatter "text" attrs content-blockFormatter (Just (Format format)) (CodeBlock attrs content)-    | isHaskell attrs = codeFormatter format attrs content+blockFormatter format (CodeBlock attrs content) =+    codeFormatter (fromMaybe (Format "text") format) attrs content -- Do not touch other blocks than 'CodeBlock'-blockFormatter _ x = x+blockFormatter _       x                        = x --- | Select formatter-codeFormatter :: Text -> Attr -> Text -> Block+-- | Run tokenizer, analysis, and formatter.+--   Fallback to original input, if failed to tokenize.+codeFormatter :: Format -- ^ Output format, defaults to "text" if not found+              -> Attr -- ^ Code block attributes+              -> Text -- ^ Code block content+              -> Block codeFormatter format attrs content =-        render format attrs (filterCodeBlock content)+  case fmap findColumns $ aTokenizer content of+    Just processed -> render format attrs processed+    Nothing        -> CodeBlock attrs content -- fallback+  where+    aTokenizer | isHaskell attrs = Token.Haskell.tokenizer+               | otherwise       = \_ -> Nothing ---  (Text, [Text], [(Text, Text)])+-- | Check if the code block is tagged as Haskell. isHaskell :: (Foldable t, Eq a1, IsString a1) =>                    (a2, t a1, c) -> Bool isHaskell (_, classes, _) = "haskell" `elem` classes
pandoc-filter-indent.cabal view
@@ -4,10 +4,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: 3a5612ddfa28673f5b83117e2fde59d631d99c9de58e1b04aaddaf53c7559d58+-- hash: 556363e65a61ebed2e66c3756278177ca371c24b119a21fbad59d9b26059d56e  name:           pandoc-filter-indent-version:        0.2.0.0+version:        0.2.1.0 synopsis:       Pandoc filter formatting Haskell code fragments using GHC lexer. description:    Formats marked code fragments, and allows `pandoc` to safely process rest of your literate program:                 .
src/Alignment.hs view
@@ -1,4 +1,7 @@ {-# LANGUAGE FlexibleContexts #-}+-- | Alignment options,+--   and extracting common columns+--   from tuples used in analysis. module Alignment where  import Data.Text (Text)
src/Filter.hs view
@@ -4,6 +4,7 @@ {-# LANGUAGE ViewPatterns          #-} {-# LANGUAGE FlexibleInstances     #-} {-# LANGUAGE FlexibleContexts      #-}+-- | Filtering a single code block. module Filter where  import Text.Pandoc.JSON@@ -21,28 +22,24 @@  import Debug.Trace(trace) -filterCodeBlock = withTokens findColumns ("haskell", tokenizer)---- | Apply function if tokenization succeeded, otherwise return same output-withTokens :: Show a => (t -> p) -> (String, a -> Maybe t) -> a -> p-withTokens f (tokenizerName, tokenizer) src@(tokenizer -> Nothing) = error $ mconcat ["Tokenizer ", tokenizerName, " failed for ", show src]-withTokens f (tokenizerName, tokenizer)     (tokenizer -> Just tokens) = f tokens--render ::  Text       -- ^ Format string+-- | Render a list of `Processed` token records into the target output format.+render ::  Format     -- ^ Format string        ->  Attr       -- ^ Attributes        -> [Processed] -- ^ Data about alignment        ->  Block --render "text" attrs aligned = RawBlock (Format "latex") $ processLatex aligned -- debug-render "text"  attrs = CodeBlock attrs           . Render.Debug.render-render "latex" attrs = RawBlock (Format "latex") . processLatex-render "html"  attrs = RawBlock (Format "html" ) . processHTML+render (Format "text" ) attrs = CodeBlock attrs           . Render.Debug.render+render (Format "latex") attrs = RawBlock (Format "latex") . processLatex+render (Format "html" ) attrs = RawBlock (Format "html" ) . processHTML -- Debugging option render other   attrs = CodeBlock attrs . T.pack . show +-- | Convert a list of input token records to raw LaTeX. processLatex :: [Processed] -> T.Text processLatex processed = Render.Latex.latexFromColSpans (length $ tableColumns processed)                        $ colspans processed +-- | Convert a list of input token records to raw HTML. processHTML :: [Processed] -> T.Text-processHTML processed = Render.HTML.htmlFromColSpans (length $ tableColumns processed)-                      $ colspans processed+processHTML  = Render.HTML.htmlFromColSpans+             . colspans
src/FindColumns.hs view
@@ -4,6 +4,7 @@ {-# LANGUAGE TypeSynonymInstances  #-} {-# LANGUAGE FlexibleInstances     #-} {-# LANGUAGE FlexibleContexts      #-}+-- | Find alignment columns. module FindColumns(findColumns, getLine, getCol, getLineCol, alignPos, tableColumns) where  import Text.Pandoc.JSON@@ -42,6 +43,7 @@ align :: Field5 a a (Maybe b) (Maybe b) => Lens' a (Maybe b) align  = _5 +-- | From a record with alignment option, select this option. alignPos :: Field5 a a (Maybe (Align, Int)) (Maybe (Align, Int)) => Lens' a (Maybe (Align, Int)) alignPos  = _5 @@ -58,8 +60,10 @@     . map addLineIndent     . grouping getLine +-- | Extract both line and column where a token starts. getLineCol x = (getLine x, getCol x) +-- | Mark alignment boundaries. markBoundaries :: [Unanalyzed] -> [Aligned] markBoundaries = map markIndent                . concat@@ -79,9 +83,12 @@            (myTok,  myLoc              , txt, Just indent, Just ALeft) markIndent other                                                               = other +-- | Append alignment option to unanalyzed token record. withAlign :: Maybe Align -> Unanalyzed -> Aligned withAlign  = flip annex +-- | Given a list of unanalyzed token records in a single column,+--   check the alignment option that applies to this column. alignBlock :: [Unanalyzed] -> [Aligned] alignBlock [a]                            = withAlign  Nothing       <$> [a] alignBlock opList | all isOperator opList = withAlign (Just ACenter) <$> opList@@ -120,6 +127,7 @@     hasAlignment (_, Nothing) = False     hasAlignment (_, Just _)  = True +-- | Extract information about depth of indent in a line where token is. getIndent = view _4  -- | Detect uninterrupted stretches that cover consecutive columns.@@ -150,6 +158,8 @@     extractColumn  []                           = Nothing     extractColumn  ((_,   MyLoc line col, _):_) = Just col +-- | Given a list of aligned records, and a list of marker columns,+--   add table column indices to each record. columnIndices :: ([Aligned], [(Int, b)]) -> [Processed] columnIndices (allAligned, map fst -> markerColumns) = map addIndex allAligned   where
src/Render/ColSpan.hs view
@@ -2,6 +2,9 @@ {-# LANGUAGE PartialTypeSignatures #-} {-# LANGUAGE FlexibleInstances     #-} {-# LANGUAGE FlexibleContexts      #-}+-- | Helper functions for taking a list of+--   processed records, and returning+--   the same list splitted into separate colspans. module Render.ColSpan where  import Data.Function(on)@@ -16,6 +19,8 @@ import Token(MyTok) import Util ( maybeLens ) +-- | A list of tokens to be put into a single colspan,+--   Number of table columns in this colspan, and alignment option. type TokensWithColSpan = ([(MyTok, Text)], Int, Align)  -- | Find colspan parameters@@ -44,6 +49,7 @@     extractToken tok = (view tokenType tok, view textContent tok) -- FIXME: split lines before colspans! +-- | Given a `Processed` record, extract number of table columns. getAlignCol :: Processed -> Int getAlignCol = view (alignPos % maybeLens (ALeft, 0) % _2) 
src/Render/Common.hs view
@@ -1,8 +1,9 @@+-- | Common definitions shared by Render modules. module Render.Common where  import Data.Text(Text) import Alignment import Token -type TextWithColSpan = (Text, Int, Align)+-- | A single colspan with its tokens. type TokensWithColSpan = ([(MyTok,Text)], Int, Align)
src/Render/Debug.hs view
@@ -16,16 +16,21 @@ import Alignment ( textContent, Align(..), Processed ) import Util ( safeTail ) +-- | Insert element at a given index of the list. insertAt       :: Show a => Int -> a -> [a] -> [a] insertAt i e ls = case maybeInsertAt i e ls of                     Nothing -> error $ "Failed in insertAt " <> show i <> " " <> show e <> " " <> show ls                     Just r  -> r +-- | Insert element at a given index of the list, or return error. maybeInsertAt :: Int -> a -> [a] -> Maybe [a] maybeInsertAt 0 e    ls  = pure $ e:ls maybeInsertAt i e (l:ls) = (l:) <$> maybeInsertAt (i-1) e ls maybeInsertAt i e    []  = Nothing +-- | Render text of code blocks+--   with marker columns inserted+--   to indicate alignment boundaries. render :: [Processed] -> Text render ps = T.concat $ go ps   where
src/Render/HTML.hs view
@@ -1,5 +1,6 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ViewPatterns      #-}+-- | Render analyzed tokens into HTML table. module Render.HTML(htmlFromColSpans) where  import Prelude hiding(span, id)@@ -15,19 +16,22 @@ import Token        (MyTok(..)) import Util ( preformatTokens, unbrace ) -htmlFromColSpans ::   p-                 -> [[TokensWithColSpan]]+-- | Given a list of lists of colspans in each table row, return an HTML text.+htmlFromColSpans :: [[TokensWithColSpan]]                  ->   Text-htmlFromColSpans cols =+htmlFromColSpans =     LT.toStrict   . renderHtml   . table   . tbody   . mapM_ renderTr +-- | Given a list of colspans within a single table row, render it to HTML tree+--   of `<tr/>` element renderTr :: [TokensWithColSpan] -> Html renderTr colspans = tr (mapM_ renderColSpan colspans) +-- | Render single colspan as a single `<td/>` cell. renderColSpan :: TokensWithColSpan -> Html renderColSpan ([(TBlank, txt)], colSpan, AIndent) = -- indentation     td (toHtml txt)@@ -45,6 +49,7 @@     alignMark ALeft   = "left"  -- TODO: braced operators+-- | Given a list of tokens in a colspan, render HTML fragment. formatTokens :: [(MyTok, Text)] -> Html formatTokens  = mapM_ formatToken               . preformatTokens
src/Render/Latex.hs view
@@ -1,5 +1,6 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ViewPatterns      #-}+-- | Render analyzed input into LaTeX table. module Render.Latex(latexFromColSpans) where  import Data.Text(Text)@@ -11,8 +12,9 @@ import Token(MyTok(..)) import Util(unbrace) ---import Debug.Trace(trace)-+-- | Given a number of table columns,+--   and a list of lists of colspans for each table row,+--   return raw LaTeX code. latexFromColSpans :: Int -> [[TokensWithColSpan]] -> Text latexFromColSpans cols =     wrapTable cols@@ -21,6 +23,7 @@          . T.intercalate " & "          . fmap renderColSpan ) +-- | Render a single colspan as LaTeX \multicolumn. renderColSpan :: TokensWithColSpan -> Text renderColSpan ([(TBlank, txt)], colSpan, AIndent) = -- indentation     T.concat [ "\\multicolumn{",    T.pack $ show colSpan@@ -36,6 +39,7 @@     alignMark ACenter = "c"     alignMark ALeft   = "l" +-- | Wrap a LaTeX table content into \begin{tabular} environment. wrapTable :: Int -> Text -> Text wrapTable cols txt =   mconcat [-- "\\newlength{\\tabcolsepBACKUP}\n"@@ -50,10 +54,14 @@  -- Decrease column spacing: \\setlength{\\tabcolsep}{1ex} -- TODO: braced operators+-- | Preprocesses functions converted to operator syntax and joins them into a single token.+-- FIXME: deduplicate preformatTokens []                                                     = [] preformatTokens ((TOperator,"`"):(TVar, "elem"):(TOperator, "`"):rest) = (TOperator, "elem"):preformatTokens rest preformatTokens (a                                              :rest) =  a                 :preformatTokens rest +-- | Format a list of tokens within a colspan.+--   Preprocesses then and calls `formatToken` for each. formatTokens :: [(MyTok, Text)] -> Text formatTokens  = T.concat               . fmap formatToken@@ -61,6 +69,8 @@               -- . (\t -> trace ("Tokens: " <> show t) t) -- debug  -- Workaround with joinEscapedOperators til w consider spaces only.+-- | Render a simple token.+formatToken :: (MyTok, Text) -> Text formatToken (TOperator,unbrace -> Just op) = "(" <> formatToken (TOperator, op) <> ")" formatToken (TKeyword, "forall") = mathop "forall" formatToken (TVar,     "mempty") = mathop "emptyset"
src/Token.hs view
@@ -13,8 +13,11 @@ import           Optics.TH  -- * Common tokens and locations--- Location is just line and column-data MyLoc = MyLoc { _line, _col :: Int }+--   We keep them here, so we can translate output from tokenizers to common format.+-- | Location is just line and column (not a slice.)+data MyLoc = MyLoc { _line :: Int -- ^ Line number starting from 1+                   , _col  :: Int -- ^ Column number starting from 1+                   }   deriving (Eq, Ord, Show)  makeLenses ''MyLoc@@ -34,13 +37,20 @@ -- | Records tokenized and converted to common token format. type Tokenized = (MyTok, MyLoc, Text) +-- | Unpack a Haskell comment with a TikZ mark indicator. unTikzMark    :: Text -> Maybe Text unTikzMark txt =   unwrap "{->" "-}" txt >>= \case     ""   -> Nothing     mark -> Just mark -unwrap :: Text -> Text -> Text -> Maybe Text+-- | Given opening text, and closing text,+--   check that input is "braced" by these, and strip them.+--   Return `Nothing` if input text does not match.+unwrap :: Text -- ^ Opening text+       -> Text -- ^ Closing text+       -> Text -- ^ Input to match+       -> Maybe Text unwrap starter trailer   txt  |   starter `T.isPrefixOf` txt &&   trailer `T.isPrefixOf` txt  =
src/Token/Haskell.hs view
@@ -21,19 +21,27 @@ import Token  -- * Haskell tokenizer frontend-tokenizer :: Text -> Maybe [(MyTok, MyLoc, Text)]+-- | Attempt to tokenize input,+--   returns `Nothing` if unsuccessful,+--   so the processor can just pass input+--   further when tokenizer fails.+tokenizer :: Text -- ^ Input text of code block+          -> Maybe [(MyTok, MyLoc, Text)] tokenizer  = fmap ( joinEscapedOperators                   . splitTokens                   . restoreLocations                   . fmap recognizeToken)            . tokenizeHaskell -+-- | Recognize token using both token type from `ghc-lib`,+--   and text content.+--   Only TikZ marks are recognized by looking up text content. recognizeToken (CommentTok, tokText@(unTikzMark -> Just mark)) =   (TTikz mark,           tokText) recognizeToken (tokType, tokText) =   (haskellTok   tokType, tokText) +-- | Convert token type of `ghc-lib` into tokens recognized by the filter. haskellTok SpaceTok       = TBlank haskellTok CommentTok     = TBlank haskellTok PragmaTok      = TBlank@@ -45,7 +53,9 @@ haskellTok IntegerTok     = TNum haskellTok t              = TOther +-- | Extract line number from `ghc-lib` slice location. locLine (Loc startLineNo startColNo _ _) = startLineNo+-- | Extract column number from `ghc-lib` slice location. locCol  (Loc startLineNo startColNo _ _) = startColNo  -- | Split tokens into one blank per line.
src/Tuples.hs view
@@ -1,7 +1,10 @@ {-# LANGUAGE FunctionalDependencies #-} {-# LANGUAGE FlexibleInstances      #-}+-- | Tuple utilities. module Tuples where +-- | Class of tuples that can be expanded+--   by a single element. class Annex c b a | c -> a, c -> b where   annex :: b -> a -> c 
src/Util.hs view
@@ -6,6 +6,7 @@ {-# LANGUAGE TypeSynonymInstances  #-} {-# LANGUAGE FlexibleInstances     #-} {-# LANGUAGE FlexibleContexts      #-}+-- | Shared utilities that may be moved to upstream libraries. module Util where  import           Data.Function(on)@@ -46,9 +47,15 @@ safeTail []     = [] safeTail (_:ls) = ls +-- | Take text in braces, and return its inner part.+--   Fail if the given text does not start with opening brace,+--   and end in closing brace. unbrace txt | T.head txt == '(' && T.last txt == ')' && T.length txt > 2 = Just $ T.tail $ T.init txt unbrace _ = Nothing +-- | Preprocess tokens before formatting+--   in order to detect tokens like functions converted to operator syntax.+--   These are merged into a single token. preformatTokens []                                                     = [] preformatTokens ((TOperator,"`"):(TVar, "elem"):(TOperator, "`"):rest) = (TOperator, "elem"):preformatTokens rest preformatTokens (a                                              :rest) =  a                 :preformatTokens rest