jira-wiki-markup 1.2.1 → 1.3.0
raw patch · 7 files changed
+156/−24 lines, 7 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ Text.Jira.Markup: Attachment :: LinkType
+ Text.Jira.Markup: Citation :: [Inline] -> Inline
+ Text.Jira.Markup: Email :: LinkType
+ Text.Jira.Markup: External :: LinkType
+ Text.Jira.Markup: User :: LinkType
+ Text.Jira.Markup: data LinkType
+ Text.Jira.Markup: instance GHC.Classes.Eq Text.Jira.Markup.LinkType
+ Text.Jira.Markup: instance GHC.Classes.Ord Text.Jira.Markup.LinkType
+ Text.Jira.Markup: instance GHC.Show.Show Text.Jira.Markup.LinkType
+ Text.Jira.Parser.Inline: citation :: JiraParser Inline
- Text.Jira.Markup: Link :: [Inline] -> URL -> Inline
+ Text.Jira.Markup: Link :: LinkType -> [Inline] -> URL -> Inline
Files
- CHANGELOG.md +21/−0
- jira-wiki-markup.cabal +1/−1
- src/Text/Jira/Markup.hs +11/−1
- src/Text/Jira/Parser/Inline.hs +26/−9
- src/Text/Jira/Printer.hs +22/−7
- test/Text/Jira/Parser/InlineTests.hs +33/−5
- test/Text/Jira/PrinterTests.hs +42/−1
CHANGELOG.md view
@@ -4,6 +4,27 @@ `jira-wiki-markup` uses [PVP Versioning][1]. The changelog is available [on GitHub][2]. +1.3.0+-----++Released 2020-04-04++* Support was added for additional syntax constructs:++ - citation markup (`??citation??`),+ - links to attachments (`[title^attachment.ext]`), and+ - user links (`[~username]`).++* Changes to module `Text.Jira.Markup`:++ * A new data type `LinkType` is exported from the module.++ * Changes to type `Inline`:++ - a new constructor `Citation` has been added;+ - the `Link` constructor now takes an additional+ parameter of type `LinkType`.+ 1.2.1 -----
jira-wiki-markup.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.0 name: jira-wiki-markup-version: 1.2.1+version: 1.3.0 synopsis: Handle Jira wiki markup description: Parse jira wiki text into an abstract syntax tree for easy transformation to other formats.
src/Text/Jira/Markup.hs view
@@ -15,6 +15,7 @@ , Block (..) , Inline (..) , InlineStyle (..)+ , LinkType (..) , ListStyle (..) , URL (..) , ColorName (..)@@ -37,12 +38,13 @@ data Inline = Anchor Text -- ^ anchor for internal links | AutoLink URL -- ^ URL which is also a link+ | Citation [Inline] -- ^ source of a citation | ColorInline ColorName [Inline] -- ^ colored inline text | Emoji Icon -- ^ emoticon | Entity Text -- ^ named or numeric HTML entity | Image [Parameter] URL -- ^ an image | Linebreak -- ^ hard linebreak- | Link [Inline] URL -- ^ hyperlink with alias+ | Link LinkType [Inline] URL -- ^ hyperlink with alias | Monospaced [Inline] -- ^ text rendered with monospaced font | Space -- ^ space between words | SpecialChar Char -- ^ single char with special meaning@@ -58,6 +60,14 @@ | Strong -- ^ strongly emphasized text | Subscript -- ^ subscript text | Superscript -- ^ superscript text+ deriving (Eq, Ord, Show)++-- | Type of a link.+data LinkType+ = Attachment -- ^ link to an attachment+ | Email -- ^ link to an email address+ | External -- ^ external resource, like a website+ | User -- ^ link to a user deriving (Eq, Ord, Show) -- | Blocks of text.
src/Text/Jira/Parser/Inline.hs view
@@ -1,5 +1,6 @@ {-# LANGUAGE CPP #-} {-# LANGUAGE LambdaCase #-}+{-# LANGUAGE TupleSections #-} {-| Module : Text.Jira.Parser.Inline Copyright : © 2019–2020 Albert Krewinkel@@ -16,6 +17,7 @@ -- * Inline component parsers , anchor , autolink+ , citation , colorInline , dash , emoji@@ -60,6 +62,7 @@ , colorInline , monospaced , anchor+ , citation , entity , specialChar ] <?> "inline"@@ -68,7 +71,7 @@ -- | Characters which, depending on context, can have a special meaning. specialChars :: String-specialChars = "_+-*^~|[]{}(!&\\:;"+specialChars = "_+-*^~|[]{}(?!&\\:;" -- | Parses an in-paragraph newline as a @Linebreak@ element. Both newline -- characters and double-backslash are recognized as line-breaks.@@ -130,7 +133,7 @@ return $ if b then All . (/= '|') else mempty inLinkPred <- do b <- stateInLink <$> getState- return $ if b then All . (`notElem` ("]|\n" :: String)) else mempty+ return $ if b then All . (`notElem` ("]^|\n" :: String)) else mempty oneOf $ filter (getAll . (inTablePred <> inLinkPred)) specialChars @@ -164,13 +167,19 @@ guard . not . stateInLink =<< getState withStateFlag (\b st -> st { stateInLink = b }) $ do _ <- char '['- alias <- option [] $ try (many inline <* char '|')- linkUrl <- email <|> url+ (alias, sep) <- option ([], '|') . try $ (,) <$> many inline <*> oneOf "^|"+ (linkType, linkURL) <- if sep == '|'+ then (Email,) <$> email <|>+ (External,) <$> url <|>+ (User,) <$> userLink+ else (Attachment,) . URL . pack <$> many1 urlChar _ <- char ']'- return $ Link alias linkUrl+ return $ Link linkType alias linkURL +-- | Parse a plain URL or mail address as @'AutoLink'@ element. autolink :: JiraParser Inline-autolink = AutoLink <$> (email <|> url) <?> "email or other URL"+autolink = AutoLink <$> (email' <|> url) <?> "email or other URL"+ where email' = (\(URL e) -> URL ("mailto:" <> e)) <$> email -- | Parse a URL with scheme @file@, @ftp@, @http@, @https@, @irc@, @nntp@, or -- @news@.@@ -190,11 +199,14 @@ 'n' -> ("nntp" <$ string "ntp") <|> ("news" <$ string "ews") _ -> fail "not looking at a known scheme" --- | Parses an E-mail URL.+-- | Parses an email URI, returns the mail address without schema. email :: JiraParser URL-email = URL . pack <$> try- ((++) <$> string "mailto:" <*> many1 urlChar)+email = URL . pack <$> try (string "mailto:" *> many1 urlChar) +-- | Parses a user-identifying resource name+userLink :: JiraParser URL+userLink = URL . pack <$> (char '~' *> many (noneOf "|]\n\r"))+ -- | Parses a character which is allowed in URLs urlChar :: JiraParser Char urlChar = satisfy $ \c ->@@ -248,6 +260,11 @@ monospaced = Monospaced <$> enclosed (try $ string "{{") (try $ string "}}") inline <?> "monospaced"++citation :: JiraParser Inline+citation = Citation+ <$> enclosed (try $ string "??") (try $ string "??") inline+ <?> "citation" -- -- Helpers
src/Text/Jira/Printer.hs view
@@ -62,6 +62,9 @@ T.singleton c <> prettyInlines rest [SpecialChar c] | c `elem` [':', ';'] -> T.singleton c+ -- Questionmarks don't have to be escaped unless in groups of two+ SpecialChar '?' : rest | not (startsWithQuestionMark rest) ->+ "?" <> prettyInlines rest (x:xs) -> renderInline x <> prettyInlines xs @@ -73,6 +76,10 @@ Str x | x `elem` ["D", ")", "(", "P"] -> True _ -> False + startsWithQuestionMark = \case+ SpecialChar '?' : _ -> True+ _ -> False+ -- | Internal state used by the printer. data PrinterState = PrinterState { stateInTable :: Bool@@ -221,14 +228,15 @@ renderInline :: Inline -> Text renderInline = \case Anchor name -> "{anchor:" <> name <> "}"- AutoLink url -> urlText url+ AutoLink url -> fromURL url+ Citation ils -> "??" <> prettyInlines ils <> "??" ColorInline color ils -> "{color:" <> colorText color <> "}" <> prettyInlines ils <> "{color}" Emoji icon -> iconText icon Entity entity -> "&" <> entity <> ";"- Image ps url -> "!" <> urlText url <> renderImageParams ps <> "!"+ Image ps url -> "!" <> fromURL url <> renderImageParams ps <> "!" Linebreak -> "\n"- Link inlines (URL url) -> "[" <> prettyInlines inlines <> "|" <> url <> "]"+ Link lt ils url -> renderLink lt ils url Monospaced inlines -> "{{" <> prettyInlines inlines <> "}}" Space -> " " SpecialChar c -> case c of@@ -243,6 +251,17 @@ let delim = T.pack ['{', delimiterChar style, '}'] in (delim <>) . (<> delim) . prettyInlines +renderLink :: LinkType -> [Inline] -> URL -> Text+renderLink linkType inlines url = case linkType of+ Attachment -> "[" <> prettyInlines inlines <> "^" <> fromURL url <> "]"+ Email -> link' $ "mailto:" <> fromURL url+ External -> link' $ fromURL url+ User -> link' $ "~" <> fromURL url+ where+ link' urlText = case inlines of+ [] -> "[" <> urlText <> "]"+ _ -> "[" <> prettyInlines inlines <> "|" <> urlText <> "]"+ delimiterChar :: InlineStyle -> Char delimiterChar = \case Emphasis -> '_'@@ -251,10 +270,6 @@ Strikeout -> '-' Subscript -> '~' Superscript -> '^'---- | Text rendering of an URL.-urlText :: URL -> Text-urlText (URL url) = url -- | Render image parameters (i.e., separate by comma). renderImageParams :: [Parameter] -> Text
test/Text/Jira/Parser/InlineTests.hs view
@@ -228,24 +228,52 @@ Right (AutoLink (URL "mailto:nobody@test.invalid")) ] + , testGroup "citation"+ [ testCase "name" $+ parseJira citation "??John Doe??" @?=+ Right (Citation [Str "John", Space, Str "Doe"])++ , testCase "with markup" $+ parseJira citation "??Jane *Example* Doe??" @?=+ Right (Citation [ Str "Jane", Space, Styled Strong [Str "Example"]+ , Space, Str "Doe"])+ ]+ , testGroup "link" [ testCase "unaliased link" $ parseJira link "[https://example.org]" @?=- Right (Link [] (URL "https://example.org"))+ Right (Link External [] (URL "https://example.org")) , testCase "aliased link" $ parseJira link "[Example|https://example.org]" @?=- Right (Link [Str "Example"] (URL "https://example.org"))+ Right (Link External [Str "Example"] (URL "https://example.org")) , testCase "alias with emphasis" $ parseJira link "[_important_ example|https://example.org]" @?=- Right (Link [Styled Emphasis [Str "important"], Space, Str "example"]+ Right (Link External+ [Styled Emphasis [Str "important"], Space, Str "example"] (URL "https://example.org")) , testCase "mail address" $ parseJira link "[send mail|mailto:me@nope.invalid]" @?=- Right (Link [Str "send", Space, Str "mail"]- (URL "mailto:me@nope.invalid"))+ Right (Link Email [Str "send", Space, Str "mail"]+ (URL "me@nope.invalid"))++ , testCase "attachment link" $+ parseJira link "[testing^test.xml]" @?=+ Right (Link Attachment [Str "testing"] (URL "test.xml"))++ , testCase "attachment without description" $+ parseJira link "[^results.txt]" @?=+ Right (Link Attachment [] (URL "results.txt"))++ , testCase "user link" $+ parseJira link "[testing|~account-id:something]" @?=+ Right (Link User [Str "testing"] (URL "account-id:something"))++ , testCase "user without description" $+ parseJira link "[~username]" @?=+ Right (Link User [] (URL "username")) ] , testGroup "image"
test/Text/Jira/PrinterTests.hs view
@@ -1,3 +1,4 @@+{-# OPTIONS_GHC -fno-warn-orphans #-} {-| Module : Text.Jira.PrinterTests Copyright : © 2019–2020 Albert Krewinkel@@ -12,7 +13,8 @@ module Text.Jira.PrinterTests (tests) where import Prelude hiding (unlines)-import Data.Text (Text, unlines)+import Data.String (IsString (fromString))+import Data.Text (Text, pack, unlines) import Test.Tasty (TestTree, testGroup) import Test.Tasty.HUnit (testCase, (@?=)) import Text.Jira.Markup@@ -116,6 +118,10 @@ renderInline (AutoLink (URL "https://example.org")) @?= "https://example.org" + , testCase "citation" $+ renderInline (Citation [Str "John", Space, Str "Doe"]) @?=+ "??John Doe??"+ , testCase "Emoji" $ renderInline (Emoji IconSmiling) @?= ":D" @@ -128,6 +134,28 @@ in renderInline (Image params (URL "example.jpg")) @?= "!example.jpg|align=right, vspace=4!" + , testGroup "link"+ [ testCase "external link" $+ renderInline (Link External [Str "example"] "https://example.org") @?=+ "[example|https://example.org]"++ , testCase "email link" $+ renderInline (Link Email [Str "example"] "me@example.org") @?=+ "[example|mailto:me@example.org]"++ , testCase "attachment" $+ renderInline (Link Attachment [Str "a", Space, Str "b"] "test.txt") @?=+ "[a b^test.txt]"++ , testCase "attachment without description" $+ renderInline (Link Attachment [] "something.txt") @?=+ "[^something.txt]"++ , testCase "user" $+ renderInline (Link User [Str "John", Space, Str "Doe"] "ab34-cdef") @?=+ "[John Doe|~ab34-cdef]"+ ]+ , testCase "Styled Emphasis" $ renderInline (Styled Emphasis [Str "Hello,", Space, Str "World!"]) @?= "_Hello, World!_"@@ -176,8 +204,21 @@ -- escaping the paren is enough prettyInlines [SpecialChar ':', SpecialChar '('] @?= ":\\("++ , testGroup "question marks"+ [ testCase "escaped if followed by question mark" $+ prettyInlines [SpecialChar '?', SpecialChar '?'] @?=+ "\\??"++ , testCase "unescaped before space" $+ prettyInlines [SpecialChar '?', Space, Str "foo"] @?=+ "? foo"+ ] ] ] renderBlock' :: Block -> Text renderBlock' = withDefault . renderBlock++instance IsString URL where+ fromString = URL . pack