diff --git a/src/Text/Pandoc/Writers/ZimWiki.hs b/src/Text/Pandoc/Writers/ZimWiki.hs index 42b168418..d01ce0e8b 100644 --- a/src/Text/Pandoc/Writers/ZimWiki.hs +++ b/src/Text/Pandoc/Writers/ZimWiki.hs @@ -18,11 +18,11 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.ZimWiki - Copyright : Copyright (C) 2008-2015 John MacFarlane, 2016 Alex Ivkin + Copyright : Copyright (C) 2008-2015 John MacFarlane, 2017 Alex Ivkin License : GNU GPL, version 2 or above Maintainer : Alex Ivkin <alex@ivkin.net> - Stability : alpha + Stability : beta Portability : portable Conversion of 'Pandoc' documents to ZimWiki markup. @@ -44,20 +44,22 @@ import Data.Default (Default(..)) import Network.URI ( isURI ) import Control.Monad ( zipWithM ) import Control.Monad.State ( modify, State, get, evalState ) ---import Control.Monad.Reader ( ReaderT, runReaderT, ask, local ) import Text.Pandoc.Class ( PandocMonad ) +import qualified Data.Map as Map data WriterState = WriterState { stItemNum :: Int, - stIndent :: String -- Indent after the marker at the beginning of list items + stIndent :: String, -- Indent after the marker at the beginning of list items + stInTable :: Bool, -- Inside a table + stInLink :: Bool -- Inside a link description } instance Default WriterState where - def = WriterState { stItemNum = 1, stIndent = "" } + def = WriterState { stItemNum = 1, stIndent = "", stInTable = False, stInLink = False } -- | Convert Pandoc to ZimWiki. writeZimWiki :: PandocMonad m => WriterOptions -> Pandoc -> m String -writeZimWiki opts document = return $ evalState (pandocToZimWiki opts document) (WriterState 1 "") +writeZimWiki opts document = return $ evalState (pandocToZimWiki opts document) def -- | Return ZimWiki representation of document. pandocToZimWiki :: WriterOptions -> Pandoc -> State WriterState String @@ -129,9 +131,15 @@ blockToZimWiki opts (Header level _ inlines) = do return $ eqs ++ " " ++ contents ++ " " ++ eqs ++ "\n" blockToZimWiki _ (CodeBlock (_,classes,_) str) = do + -- Remap languages into the gtksourceview2 convention that ZimWiki source code plugin is using + let langal = [("javascript", "js"), ("bash", "sh"), ("winbatch", "dosbatch")] + let langmap = Map.fromList langal return $ case classes of - [] -> "'''\n" ++ cleanupCode str ++ "\n'''\n" -- no lang block is a quote block - (x:_) -> "{{{code: lang=\"" ++ x ++ "\" linenumbers=\"True\"\n" ++ str ++ "\n}}}\n" -- for zim's code plugin, go verbatim on the lang spec + [] -> "'''\n" ++ cleanupCode str ++ "\n'''\n" -- turn no lang block into a quote block + (x:_) -> "{{{code: lang=\"" ++ + (case Map.lookup x langmap of + Nothing -> x + Just y -> y) ++ "\" linenumbers=\"True\"\n" ++ str ++ "\n}}}\n" -- for zim's code plugin, go verbatim on the lang spec blockToZimWiki opts (BlockQuote blocks) = do contents <- blockListToZimWiki opts blocks @@ -145,7 +153,7 @@ blockToZimWiki opts (Table capt aligns _ headers rows) = do return $ "" ++ c ++ "\n" headers' <- if all null headers then zipWithM (tableItemToZimWiki opts) aligns (rows !! 0) - else zipWithM (tableItemToZimWiki opts) aligns headers + else mapM (inlineListToZimWiki opts) (map removeFormatting headers) -- emphasis, links etc. are not allowed in table headers rows' <- mapM (zipWithM (tableItemToZimWiki opts) aligns) rows let widths = map (maximum . map length) $ transpose (headers':rows') let padTo (width, al) s = @@ -167,10 +175,10 @@ blockToZimWiki opts (Table capt aligns _ headers rows) = do then replicate (width-1) '-' ++ ":" else ":" ++ replicate (width-2) '-' ++ ":" let underheader = "|" ++ intercalate "|" (zipWith borderCell (zip widths aligns) headers') ++ "|" - let renderRow sep cells = sep ++ intercalate sep (zipWith padTo (zip widths aligns) cells) ++ sep + let renderRow cells = "|" ++ intercalate "|" (zipWith padTo (zip widths aligns) cells) ++ "|" return $ captionDoc ++ - (if null headers' then "" else renderRow "|" headers' ++ "\n") ++ underheader ++ "\n" ++ - unlines (map (renderRow "|") rows') + (if null headers' then "" else renderRow headers' ++ "\n") ++ underheader ++ "\n" ++ + unlines (map renderRow rows') blockToZimWiki opts (BulletList items) = do indent <- stIndent <$> get @@ -255,7 +263,9 @@ tableItemToZimWiki opts align' item = do (if align' == AlignLeft || align' == AlignCenter then " " else "") - contents <- blockListToZimWiki opts item -- local (\s -> s { stBackSlashLB = True }) $ + modify $ \s -> s { stInTable = True } + contents <- blockListToZimWiki opts item + modify $ \s -> s { stInTable = False } return $ mkcell contents -- | Convert list of Pandoc block elements to ZimWiki. @@ -305,7 +315,15 @@ inlineToZimWiki opts (Cite _ lst) = inlineListToZimWiki opts lst inlineToZimWiki _ (Code _ str) = return $ "''" ++ str ++ "''" -inlineToZimWiki _ (Str str) = return $ escapeString str +inlineToZimWiki _ (Str str) = do + inTable <- stInTable <$> get + inLink <- stInLink <$> get + if inTable + then return $ substitute "|" "\\|" . escapeString $ str + else + if inLink + then return $ str + else return $ escapeString str inlineToZimWiki _ (Math mathType str) = return $ delim ++ str ++ delim -- note: str should NOT be escaped where delim = case mathType of @@ -318,7 +336,11 @@ inlineToZimWiki opts (RawInline f str) | f == Format "html" = do cont <- indentFromHTML opts str; return cont | otherwise = return "" -inlineToZimWiki _ LineBreak = return "\n" -- was \\\\ +inlineToZimWiki _ LineBreak = do + inTable <- stInTable <$> get + if inTable + then return "\\n" + else return "\n" inlineToZimWiki opts SoftBreak = case writerWrapText opts of @@ -329,30 +351,38 @@ inlineToZimWiki opts SoftBreak = inlineToZimWiki _ Space = return " " inlineToZimWiki opts (Link _ txt (src, _)) = do - label <- inlineListToZimWiki opts txt + inTable <- stInTable <$> get + modify $ \s -> s { stInLink = True } + label <- inlineListToZimWiki opts $ removeFormatting txt -- zim does not allow formatting in link text, it takes the text verbatim, no need to escape it + modify $ \s -> s { stInLink = False } + let label'= if inTable + then "" -- no label is allowed in a table + else "|"++label case txt of [Str s] | "mailto:" `isPrefixOf` src -> return $ "<" ++ s ++ ">" | escapeURI s == src -> return src _ -> if isURI src - then return $ "[[" ++ src ++ "|" ++ label ++ "]]" - else return $ "[[" ++ src' ++ "|" ++ label ++ "]]" + then return $ "[[" ++ src ++ label' ++ "]]" + else return $ "[[" ++ src' ++ label' ++ "]]" where src' = case src of '/':xs -> xs -- with leading / it's a _ -> src -- link to a help page inlineToZimWiki opts (Image attr alt (source, tit)) = do alt' <- inlineListToZimWiki opts alt - let txt = case (tit, alt) of - ("", []) -> "" - ("", _ ) -> "|" ++ alt' - (_ , _ ) -> "|" ++ tit + inTable <- stInTable <$> get + let txt = case (tit, alt, inTable) of + ("",[], _) -> "" + ("", _, False ) -> "|" ++ alt' + (_ , _, False ) -> "|" ++ tit + (_ , _, True ) -> "" -- Relative links fail isURI and receive a colon prefix = if isURI source then "" else ":" return $ "{{" ++ prefix ++ source ++ imageDims opts attr ++ txt ++ "}}" inlineToZimWiki opts (Note contents) = do + -- no concept of notes in zim wiki, use a text block contents' <- blockListToZimWiki opts contents - return $ "((" ++ contents' ++ "))" - -- note - may not work for notes with multiple blocks + return $ " **{Note:** " ++ trimr contents' ++ "**}**" imageDims :: WriterOptions -> Attr -> String imageDims opts attr = go (toPx $ dimension Width attr) (toPx $ dimension Height attr) diff --git a/test/tables.zimwiki b/test/tables.zimwiki index 6da1f7f2c..2757055f6 100644 --- a/test/tables.zimwiki +++ b/test/tables.zimwiki @@ -1,43 +1,43 @@ Simple table with caption: Demonstration of simple table syntax. -| Right|Left | Center |Default| -|------:|:-----|:--------:|-------| -| 12|12 | 12 |12 | -| 123|123 | 123 |123 | -| 1|1 | 1 |1 | +|Right|Left |Center |Default| +|----:|:----|:-----:|-------| +| 12|12 | 12 |12 | +| 123|123 | 123 |123 | +| 1|1 | 1 |1 | Simple table without caption: -| Right|Left | Center |Default| -|------:|:-----|:--------:|-------| -| 12|12 | 12 |12 | -| 123|123 | 123 |123 | -| 1|1 | 1 |1 | +|Right|Left |Center |Default| +|----:|:----|:-----:|-------| +| 12|12 | 12 |12 | +| 123|123 | 123 |123 | +| 1|1 | 1 |1 | Simple table indented two spaces: Demonstration of simple table syntax. -| Right|Left | Center |Default| -|------:|:-----|:--------:|-------| -| 12|12 | 12 |12 | -| 123|123 | 123 |123 | -| 1|1 | 1 |1 | +|Right|Left |Center |Default| +|----:|:----|:-----:|-------| +| 12|12 | 12 |12 | +| 123|123 | 123 |123 | +| 1|1 | 1 |1 | Multiline table with caption: Here’s the caption. It may span multiple lines. -| Centered Header |Left Aligned | Right Aligned|Default aligned | -|:-----------------:|:-------------|--------------:|:------------------------------------------------------| -| First |row | 12.0|Example of a row that spans multiple lines. | -| Second |row | 5.0|Here’s another one. Note the blank line between rows. | +|Centered Header|Left Aligned|Right Aligned|Default aligned | +|:-------------:|:-----------|------------:|:------------------------------------------------------| +| First |row | 12.0|Example of a row that spans multiple lines. | +| Second |row | 5.0|Here’s another one. Note the blank line between rows. | Multiline table without caption: -| Centered Header |Left Aligned | Right Aligned|Default aligned | -|:-----------------:|:-------------|--------------:|:------------------------------------------------------| -| First |row | 12.0|Example of a row that spans multiple lines. | -| Second |row | 5.0|Here’s another one. Note the blank line between rows. | +|Centered Header|Left Aligned|Right Aligned|Default aligned | +|:-------------:|:-----------|------------:|:------------------------------------------------------| +| First |row | 12.0|Example of a row that spans multiple lines. | +| Second |row | 5.0|Here’s another one. Note the blank line between rows. | Table without column headers: diff --git a/test/writer.zimwiki b/test/writer.zimwiki index 848ca955e..7a15bad9d 100644 --- a/test/writer.zimwiki +++ b/test/writer.zimwiki @@ -606,8 +606,7 @@ Here is a movie {{:movie.jpg|movie}} icon. ====== Footnotes ====== -Here is a footnote reference,((Here is the footnote. It can go anywhere after the footnote reference. It need not be placed at the end of the document. -)) and another.((Here’s the long note. This one contains multiple blocks. +Here is a footnote reference, **{Note:** Here is the footnote. It can go anywhere after the footnote reference. It need not be placed at the end of the document.**}** and another. **{Note:** Here’s the long note. This one contains multiple blocks. Subsequent blocks are indented to show that they belong to the footnote (as with list items). @@ -615,13 +614,10 @@ Subsequent blocks are indented to show that they belong to the footnote (as with { <code> } ''' -If you want, you can indent every line, but you can also be lazy and just indent the first line of each block. -)) This should //not// be a footnote reference, because it contains a space.[^my note] Here is an inline note.((This is //easier// to type. Inline notes may contain [[http://google.com|links]] and '']'' verbatim characters, as well as [bracketed text]. -)) +If you want, you can indent every line, but you can also be lazy and just indent the first line of each block.**}** This should //not// be a footnote reference, because it contains a space.[^my note] Here is an inline note. **{Note:** This is //easier// to type. Inline notes may contain [[http://google.com|links]] and '']'' verbatim characters, as well as [bracketed text].**}** -> Notes can go in quotes.((In quote. -> )) +> Notes can go in quotes. **{Note:** In quote.**}** - 1. And in list items.((In list.)) + 1. And in list items. **{Note:** In list.**}** This paragraph should not be part of the note, as it is not indented.