packages feed

scripths 0.1.0.2 → 0.2.0.0

raw patch · 6 files changed

+121/−31 lines, 6 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

+ ScriptHs.Markdown: CodeOutput :: MimeType -> Text -> CodeOutput
+ ScriptHs.Markdown: MimeHtml :: MimeType
+ ScriptHs.Markdown: MimeImage :: Text -> MimeType
+ ScriptHs.Markdown: MimeJson :: MimeType
+ ScriptHs.Markdown: MimeLatex :: MimeType
+ ScriptHs.Markdown: MimeMarkdown :: MimeType
+ ScriptHs.Markdown: MimePlain :: MimeType
+ ScriptHs.Markdown: MimeSvg :: MimeType
+ ScriptHs.Markdown: data CodeOutput
+ ScriptHs.Markdown: data MimeType
+ ScriptHs.Markdown: instance GHC.Classes.Eq ScriptHs.Markdown.CodeOutput
+ ScriptHs.Markdown: instance GHC.Classes.Eq ScriptHs.Markdown.MimeType
+ ScriptHs.Markdown: instance GHC.Show.Show ScriptHs.Markdown.CodeOutput
+ ScriptHs.Markdown: instance GHC.Show.Show ScriptHs.Markdown.MimeType
- ScriptHs.Markdown: CodeBlock :: Text -> Text -> Maybe Text -> Segment
+ ScriptHs.Markdown: CodeBlock :: Text -> Text -> Maybe CodeOutput -> Segment

Files

CHANGELOG.md view
@@ -1,5 +1,11 @@ # Revision history for scripths +## 0.2.0.0 -- 2026-03-01++* Add mime type to output.++* Make blocks out of single lines of code as well.+ ## 0.1.0.2 -- 2026-02-28  * Make blocks out of single lines of code as well.
scripths.cabal view
@@ -1,6 +1,6 @@ cabal-version:      3.0 name:               scripths-version:            0.1.0.2+version:            0.2.0.0 synopsis:           GHCi scripts for standalone execution and Markdown documentation. description:        GHCi scripts for standalone execution (with dependency resolution) and Markdown documentation (produces inline output). homepage:           https://www.datahaskell.org/
src/ScriptHs/Markdown.hs view
@@ -2,14 +2,28 @@     Segment (..),     parseMarkdown,     reassemble,+    MimeType (..),+    CodeOutput (..), ) where  import Data.Text (Text) import qualified Data.Text as T +data MimeType+    = MimeHtml+    | MimeMarkdown+    | MimeSvg+    | MimeLatex+    | MimeJson+    | MimeImage Text -- image type+    | MimePlain+    deriving (Show, Eq)++data CodeOutput = CodeOutput MimeType Text deriving (Show, Eq)+ data Segment     = Prose Text-    | CodeBlock Text Text (Maybe Text)+    | CodeBlock Text Text (Maybe CodeOutput) -- Language, Code, Output     deriving (Show, Eq)  parseMarkdown :: Text -> [Segment]@@ -25,11 +39,24 @@         let             prose = T.unlines acc             (codeLines, rest') = fmap (drop 1) (break ((== fence) . T.strip) rest)+            (output, rest'') = case (dropWhile ((== "") . T.strip) rest') of+                (x : xs) ->+                    if not (T.isPrefixOf "> <!-- sabela:mime" x)+                        then (Nothing, rest')+                        else+                            let+                                (cOutput, afterOutput) = span (T.isPrefixOf "> ") xs+                                mType = mimeFromTag x+                             in+                                ( Just (CodeOutput mType (T.unlines (map (T.drop (T.length "> ")) cOutput)))+                                , afterOutput+                                )+                [] -> (Nothing, rest')             segments =                 [Prose prose | not (T.null prose)]-                    ++ [CodeBlock lang (T.unlines codeLines) Nothing]+                    ++ [CodeBlock lang (T.unlines codeLines) output]          in-            segments ++ parseMarkdown' [] rest'+            segments ++ parseMarkdown' [] rest''  fence :: Text fence = "```"@@ -49,16 +76,34 @@ renderSegment :: Segment -> Text renderSegment (Prose t) = t renderSegment (CodeBlock lang code Nothing) = fenceCodeSegment lang code-renderSegment (CodeBlock lang code (Just output)) =-    if T.null (T.strip output)-        then fenceCodeSegment lang code-        else fenceCodeSegment lang code <> blockQuote output+renderSegment (CodeBlock lang code (Just output)) = fenceCodeSegment lang code <> blockQuote output -blockQuote :: Text -> Text-blockQuote t =+blockQuote :: CodeOutput -> Text+blockQuote (CodeOutput mimeType t) =     let-        ls = T.lines t+        ls = ["<!-- sabela:mime " <> mimeIndicator mimeType <> " -->"] ++ T.lines t         trimmed = reverse $ dropWhile T.null $ reverse ls-        quoted = T.unlines $ map (\l -> if T.null l then ">" else "> " <> l) trimmed+        quoted = T.unlines $ map (\l -> if T.null l then "> " else "> " <> l) trimmed      in         quoted <> "\n"++mimeIndicator :: MimeType -> Text+mimeIndicator m = case m of+    MimeHtml -> "text/html"+    MimeMarkdown -> "text/markdown"+    MimeSvg -> "image/svg+xml"+    MimeLatex -> "text/latex"+    MimeJson -> "application/json"+    MimeImage t -> t <> ";base64"+    MimePlain -> "text/plain"++mimeFromTag :: Text -> MimeType+mimeFromTag t+    | T.isInfixOf "text/html" t = MimeHtml+    | T.isInfixOf "text/markdown" t = MimeMarkdown+    | T.isInfixOf "image/svg+html" t = MimeSvg+    | T.isInfixOf "text/latex" t = MimeLatex+    | T.isInfixOf "application/json" t = MimeJson+    | T.isInfixOf "base64" t =+        MimeImage (T.takeWhile (/= ';') (T.drop (T.length "<!-- sabela:mime ") t))+    | otherwise = MimePlain
src/ScriptHs/Notebook.hs view
@@ -5,7 +5,13 @@ import qualified Data.Text as T import qualified Data.Text.IO as TIO -import ScriptHs.Markdown (Segment (..), parseMarkdown, reassemble)+import ScriptHs.Markdown (+    CodeOutput (..),+    MimeType (..),+    Segment (..),+    parseMarkdown,+    reassemble,+ ) import ScriptHs.Parser (     CabalMeta (..),     Line (..),@@ -47,7 +53,7 @@ addOutputToSegments outputs = map addOutput   where     addOutput :: (Int, Segment) -> Segment-    addOutput (i, CodeBlock lang code _) = CodeBlock lang code (lookup i outputs)+    addOutput (i, CodeBlock lang code _) = CodeBlock lang code (fmap (CodeOutput MimePlain) (lookup i outputs))     addOutput (_, seg) = seg  mkIndexedCodeSegments :: IndexedSegments -> IndexedSegments
test/Test/Markdown.hs view
@@ -11,6 +11,8 @@ import Data.Text (Text) import qualified Data.Text as T import ScriptHs.Markdown (+    CodeOutput (..),+    MimeType (..),     Segment (CodeBlock, Prose),     parseMarkdown,     reassemble,@@ -72,6 +74,23 @@                 case segs of                     [CodeBlock{}, Prose _] -> pure ()                     other -> assertFailure $ "expected [CodeBlock, Prose], got: " ++ show other+            , testCase "latex mimetype" $ do+                let input =+                        T.unlines+                            [ "```haskell"+                            , "print 42"+                            , "```"+                            , ""+                            , "> <!-- sabela:mime text/latex -->"+                            , "> 2"+                            , ""+                            , "Some text after."+                            ]+                let segs = parseMarkdown input+                length segs @?= 2+                case segs of+                    [(CodeBlock _ _ (Just (CodeOutput m _))), Prose _] -> assertBool "mime is latex" (m == MimeLatex)+                    other -> assertFailure $ "expected [CodeBlock, Prose], got: " ++ show other             , testCase "multiple code blocks" $ do                 let input =                         T.unlines@@ -81,15 +100,26 @@                             , "print 1"                             , "```"                             , ""+                            , "> <!-- sabela:mime text/plain -->"+                            , "> 1"+                            , ""                             , "Middle text."                             , ""                             , "```haskell"                             , "print 2"                             , "```"+                            , ""+                            , "> <!-- sabela:mime text/plain -->"+                            , "> 2"                             ]                 let segs = parseMarkdown input                 let codeBlocks = [c | c@(CodeBlock{}) <- segs]+                let outputBlocks = [o | (CodeBlock _ _ o@(Just _)) <- segs]                 length codeBlocks @?= 2+                length outputBlocks @?= 2+                assertBool+                    "all mimeType plain"+                    (all (== MimePlain) [m | (Just (CodeOutput m _)) <- outputBlocks])             , testCase "non-haskell code block preserved" $ do                 let input =                         T.unlines@@ -142,26 +172,22 @@                 assertBool "has code" (T.isInfixOf "print 42" result)                 assertBool "no blockquote" (not $ T.isInfixOf "> " result)             , testCase "code block with output" $ do-                let result = reassemble [CodeBlock "haskell" "print 42\n" (Just "42")]+                let result =+                        reassemble [CodeBlock "haskell" "print 42\n" (Just $ CodeOutput MimePlain "42")]                 assertBool "has fence" (T.isInfixOf "```haskell" result)                 assertBool "has code" (T.isInfixOf "print 42" result)                 assertBool "has blockquote" (T.isInfixOf "> 42" result)             , testCase "code block with multi-line output" $ do-                let result = reassemble [CodeBlock "hs" "print [1,2]\n" (Just "1\n2")]+                let result =+                        reassemble [CodeBlock "hs" "print [1,2]\n" (Just $ CodeOutput MimePlain "1\n2")]                 assertBool "has > 1" (T.isInfixOf "> 1" result)                 assertBool "has > 2" (T.isInfixOf "> 2" result)-            , testCase "empty output is omitted" $ do-                let result = reassemble [CodeBlock "haskell" "import X\n" (Just "")]-                assertBool "no blockquote" (not $ T.isInfixOf "> " result)-            , testCase "whitespace-only output is omitted" $ do-                let result = reassemble [CodeBlock "haskell" "import X\n" (Just "  \n  \n")]-                assertBool "no blockquote" (not $ T.isInfixOf "> " result)             , testCase "full document roundtrip" $ do                 let segs =                         [ Prose "# Title\n\n"-                        , CodeBlock "haskell" "print 42\n" (Just "42")+                        , CodeBlock "haskell" "print 42\n" (Just $ CodeOutput MimePlain "42")                         , Prose "\nSome text.\n"-                        , CodeBlock "haskell" "print 99\n" (Just "99")+                        , CodeBlock "haskell" "print 99\n" (Just $ CodeOutput MimePlain "99")                         ]                 let result = reassemble segs @@ -181,11 +207,11 @@         , testGroup             "blockquote"             [ testCase "single line" $ do-                let result = reassemble [CodeBlock "hs" "x\n" (Just "hello")]+                let result = reassemble [CodeBlock "hs" "x\n" (Just $ CodeOutput MimePlain "hello")]                 assertBool "blockquoted" (T.isInfixOf "> hello" result)             , testCase "empty lines in output get bare >" $ do-                let result = reassemble [CodeBlock "hs" "x\n" (Just "a\n\nb")]-                assertBool "has bare >" (T.isInfixOf "\n>\n" result)+                let result = reassemble [CodeBlock "hs" "x\n" (Just $ CodeOutput MimePlain "a\n\nb")]+                assertBool "has bare >" (T.isInfixOf "\n> \n" result)             ]         ] 
test/Test/Notebook.hs view
@@ -5,7 +5,7 @@  import qualified Data.Text as T -import ScriptHs.Markdown (Segment (..))+import ScriptHs.Markdown (CodeOutput (..), MimeType (..), Segment (..)) import ScriptHs.Notebook (     addOutputToSegments,     generatedMarkedScript,@@ -110,7 +110,13 @@                         [ (0, Prose "intro\n")                         , (1, CodeBlock "haskell" "print 1\n" Nothing)                         , (2, CodeBlock "python" "print('x')\n" Nothing)-                        , (3, CodeBlock "hs" "print 3\n" (Just "old-should-be-replaced"))+                        ,+                            ( 3+                            , CodeBlock+                                "hs"+                                "print 3\n"+                                (Just $ CodeOutput MimePlain "old-should-be-replaced")+                            )                         , (4, Prose "outro\n")                         ] @@ -121,8 +127,9 @@                 head result @?= Prose "intro\n"                 result !! 4 @?= Prose "outro\n" -                result !! 1 @?= CodeBlock "haskell" "print 1\n" (Just "out-1")-                result !! 3 @?= CodeBlock "hs" "print 3\n" (Just "out-3")+                result !! 1+                    @?= CodeBlock "haskell" "print 1\n" (Just $ CodeOutput MimePlain "out-1")+                result !! 3 @?= CodeBlock "hs" "print 3\n" (Just $ CodeOutput MimePlain "out-3")                  result !! 2 @?= CodeBlock "python" "print('x')\n" Nothing             ]