git.iko.soy/typeable/pandoc
on master help sign in
RST reader: implement csv-table directive.

Most attributes are supported, including `:file:` and `:url:`. A (probably insufficient) test case has been added. Closes #3533.

commitdee4cbc8549d782d9c3f2e9072b2c141ea4f18ad authorJohn MacFarlane <jgm@berkeley.edu> dateThu 10 Aug 2017, 12:04 (9 years ago) parenta5790dd
4 files changed+120−48
src/Text/Pandoc/CSV.hs+5−3 src/Text/Pandoc/Readers/RST.hs+56−45 test/command/3533-rst-csv-tables.csv+4 test/command/3533-rst-csv-tables.md+55
src/Text/Pandoc/CSV.hs+5−3
@@ -28,7 +28,7 @@ Simple CSV parser.
2828-}
2929
3030module Text.Pandoc.CSV (
31− CSVOptions,
31+ CSVOptions(..),
3232 defaultCSVOptions,
3333 parseCSV,
3434 ParseError
@@ -74,7 +74,8 @@ pCSVCell opts = pCSVQuotedCell opts <|> pCSVUnquotedCell opts
7474pCSVQuotedCell :: CSVOptions -> Parser Text
7575pCSVQuotedCell opts = do
7676 char (csvQuote opts)
77− res <- many (satisfy (\c -> c /= csvQuote opts) <|> escaped opts)
77+ res <- many (satisfy (\c -> c /= csvQuote opts &&
78+ Just c /= csvEscape opts) <|> escaped opts)
7879 char (csvQuote opts)
7980 return $ T.pack res
8081
@@ -86,7 +87,8 @@ escaped opts = do
8687
8788pCSVUnquotedCell :: CSVOptions -> Parser Text
8889pCSVUnquotedCell opts = T.pack <$>
89− many (satisfy $ \c -> c /= csvDelim opts && c /= '\r' && c /= '\n')
90+ many (satisfy (\c -> c /= csvDelim opts && c /= '\r' && c /= '\n'
91+ && c /= csvQuote opts))
9092
9193pCSVDelim :: CSVOptions -> Parser ()
9294pCSVDelim opts = do
src/Text/Pandoc/Readers/RST.hs+56−45
@@ -31,7 +31,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
3131Conversion from reStructuredText to 'Pandoc' document.
3232-}
3333module Text.Pandoc.Readers.RST ( readRST ) where
34−import Control.Monad (guard, liftM, mzero, when, forM_)
34+import Control.Monad (guard, liftM, mzero, when, forM_, mplus)
3535import Control.Monad.Identity (Identity(..))
3636import Control.Monad.Except (throwError)
3737import Data.Char (isHexDigit, isSpace, toLower, toUpper)
@@ -44,7 +44,7 @@ import Data.Sequence (ViewR (..), viewr)
4444import Text.Pandoc.Builder (fromList, setMeta)
4545import Text.Pandoc.Builder (Blocks, Inlines, trimInlines)
4646import qualified Text.Pandoc.Builder as B
47−import Text.Pandoc.Class (PandocMonad, readFileFromDirs)
47+import Text.Pandoc.Class (PandocMonad, readFileFromDirs, fetchItem)
4848import Text.Pandoc.CSV (CSVOptions(..), defaultCSVOptions, parseCSV)
4949import Text.Pandoc.Definition
5050import Text.Pandoc.Error
@@ -53,15 +53,13 @@ import Text.Pandoc.Logging
5353import Text.Pandoc.Options
5454import Text.Pandoc.Parsing
5555import Text.Pandoc.Shared
56+import qualified Text.Pandoc.UTF8 as UTF8
5657import Text.Printf (printf)
5758import Data.Text (Text)
5859import qualified Data.Text as T
5960
60−import Debug.Trace
61−
6261-- TODO:
6362-- [ ] .. parsed-literal
64−-- [ ] .. csv-table
6563
6664-- | Parse reStructuredText string and return Pandoc document.
6765readRST :: PandocMonad m
@@ -824,53 +822,66 @@ listTableDirective top fields body = do
824822 takeCells _ = []
825823 normWidths ws = map (/ max 1 (sum ws)) ws
826824
827− -- TODO
828− -- [ ] delim:
829− -- [ ] quote:
830− -- [ ] keepspace:
831− -- [ ] escape:
832− -- [ ] widths:
833− -- [ ] header-rows:
834− -- [ ] header:
835− -- [ ] url:
836− -- [ ] file:
837− -- [ ] encoding:
838825csvTableDirective :: PandocMonad m
839826 => String -> [(String, String)] -> String
840827 -> RSTParser m Blocks
841828csvTableDirective top fields rawcsv = do
842− let res = parseCSV defaultCSVOptions (T.pack rawcsv)
829+ let explicitHeader = trim <$> lookup "header" fields
830+ let opts = defaultCSVOptions{
831+ csvDelim = case trim <$> lookup "delim" fields of
832+ Just "tab" -> '\t'
833+ Just "space" -> ' '
834+ Just [c] -> c
835+ _ -> ','
836+ , csvQuote = case trim <$> lookup "quote" fields of
837+ Just [c] -> c
838+ _ -> '"'
839+ , csvEscape = case trim <$> lookup "escape" fields of
840+ Just [c] -> Just c
841+ _ -> Nothing
842+ , csvKeepSpace = case trim <$> lookup "keepspace" fields of
843+ Just "true" -> True
844+ _ -> False
845+ }
846+ let headerRowsNum = fromMaybe (case explicitHeader of
847+ Just _ -> 1 :: Int
848+ Nothing -> 0 :: Int) $
849+ lookup "header-rows" fields >>= safeRead
850+ rawcsv' <- case trim <$>
851+ lookup "file" fields `mplus` lookup "url" fields of
852+ Just u -> do
853+ (bs, _) <- fetchItem Nothing u
854+ return $ UTF8.toString bs
855+ Nothing -> return rawcsv
856+ let res = parseCSV opts (T.pack $ case explicitHeader of
857+ Just h -> h ++ "\n" ++ rawcsv'
858+ Nothing -> rawcsv')
843859 case res of
844860 Left e -> do
845861 throwError $ PandocParsecError "csv table" e
846− Right rows -> do
847− return $ B.rawBlock "rst" $ show rows
848−{-
849− bs <- parseFromString' parseBlocks body
850− title <- parseFromString' (trimInlines . mconcat <$> many inline) top
851− let rows = takeRows $ B.toList bs
852− headerRowsNum = fromMaybe (0 :: Int) $
853− lookup "header-rows" fields >>= safeRead
854− (headerRow,bodyRows,numOfCols) = case rows of
855− x:xs -> if headerRowsNum > 0
856− then (x, xs, length x)
857− else ([], rows, length x)
858− _ -> ([],[],0)
859− widths = case trim <$> lookup "widths" fields of
860− Just "auto" -> replicate numOfCols 0
861− Just specs -> normWidths $ map (fromMaybe (0 :: Double) . safeRead) $
862− splitBy (`elem` (" ," :: String)) specs
863− _ -> replicate numOfCols 0
864− return $ B.table title
865− (zip (replicate numOfCols AlignDefault) widths)
866− headerRow
867− bodyRows
868− where takeRows [BulletList rows] = map takeCells rows
869− takeRows _ = []
870− takeCells [BulletList cells] = map B.fromList cells
871− takeCells _ = []
872− normWidths ws = map (/ max 1 (sum ws)) ws
873−-}
862+ Right rawrows -> do
863+ let parseCell = parseFromString' (plain <|> return mempty) . T.unpack
864+ let parseRow = mapM parseCell
865+ rows <- mapM parseRow rawrows
866+ let (headerRow,bodyRows,numOfCols) =
867+ case rows of
868+ x:xs -> if headerRowsNum > 0
869+ then (x, xs, length x)
870+ else ([], rows, length x)
871+ _ -> ([],[],0)
872+ title <- parseFromString' (trimInlines . mconcat <$> many inline) top
873+ let normWidths ws = map (/ max 1 (sum ws)) ws
874+ let widths =
875+ case trim <$> lookup "widths" fields of
876+ Just "auto" -> replicate numOfCols 0
877+ Just specs -> normWidths
878+ $ map (fromMaybe (0 :: Double) . safeRead)
879+ $ splitBy (`elem` (" ," :: String)) specs
880+ _ -> replicate numOfCols 0
881+ return $ B.table title
882+ (zip (replicate numOfCols AlignDefault) widths)
883+ headerRow
884+ bodyRows
874885
875886-- TODO:
876887-- - Only supports :format: fields with a single format for :raw: roles,
test/command/3533-rst-csv-tables.csvnew file+4
@@ -0,0 +1,4 @@
1+"Albatross", 2.99, "On a stick!"
2+"Crunchy Frog", 1.49, "If we took the bones out, it wouldn't be
3+crunchy, now would it?"
4+
test/command/3533-rst-csv-tables.mdnew file+55
@@ -0,0 +1,55 @@
1+```
2+% pandoc -f rst -t native
3+.. csv-table:: Test
4+ :widths: 10, 5, 10
5+ :header: Flavor,Price,Slogan
6+ :file: command/3533-rst-csv-tables.csv
7+^D
8+[Table [Str "Test"] [AlignDefault,AlignDefault,AlignDefault] [0.4,0.2,0.4]
9+ [[Plain [Str "Flavor"]]
10+ ,[Plain [Str "Price"]]
11+ ,[Plain [Str "Slogan"]]]
12+ [[[Plain [Str "Albatross"]]
13+ ,[Plain [Str "2.99"]]
14+ ,[Plain [Str "On",Space,Str "a",Space,Str "stick!"]]]
15+ ,[[Plain [Str "Crunchy",Space,Str "Frog"]]
16+ ,[Plain [Str "1.49"]]
17+ ,[Plain [Str "If",Space,Str "we",Space,Str "took",Space,Str "the",Space,Str "bones",Space,Str "out,",Space,Str "it",Space,Str "wouldn't",Space,Str "be",SoftBreak,Str "crunchy,",Space,Str "now",Space,Str "would",Space,Str "it?"]]]]]
18+```
19+
20+```
21+% pandoc -f rst -t native
22+.. csv-table:: Test
23+ :header-rows: 1
24+ :quote: '
25+ :delim: space
26+
27+ '' 'a' 'b'
28+ 'cat''s' 3 4
29+ 'dog''s' 2 3
30+^D
31+[Table [Str "Test"] [AlignDefault,AlignDefault,AlignDefault] [0.0,0.0,0.0]
32+ [[]
33+ ,[Plain [Str "a"]]
34+ ,[Plain [Str "b"]]]
35+ [[[Plain [Str "cat's"]]
36+ ,[Plain [Str "3"]]
37+ ,[Plain [Str "4"]]]
38+ ,[[Plain [Str "dog's"]]
39+ ,[Plain [Str "2"]]
40+ ,[Plain [Str "3"]]]]]
41+```
42+
43+```
44+% pandoc -f rst -t native
45+.. csv-table:: Test
46+ :escape: \
47+
48+ "1","\""
49+^D
50+[Table [Str "Test"] [AlignDefault,AlignDefault] [0.0,0.0]
51+ []
52+ [[[Plain [Str "1"]]
53+ ,[Plain [Str "\""]]]]]
54+```
55+