gridtables 0.0.2.0 → 0.0.3.0
raw patch · 4 files changed
+144/−29 lines, 4 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
Files
- CHANGELOG.md +11/−0
- gridtables.cabal +1/−1
- src/Text/GridTable/Trace.hs +70/−28
- test/test-gridtables.hs +62/−0
CHANGELOG.md view
@@ -2,6 +2,17 @@ `gridtables` uses [PVP Versioning][]. +## gridtables-0.0.3.0++Released 2022-08-18.++- Missing cells no longer cause an error, but are replaced with+ empty cells.++- The borders of the last cell in a row are allowed to be+ shorter than the cell. Previously the last column was+ discarded in that case.+ ## gridtables-0.0.2.0 Released 2022-07-30.
gridtables.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.4 name: gridtables-version: 0.0.2.0+version: 0.0.3.0 synopsis: Parser for reStructuredText-style grid tables. description: Provides a parser for plain-text representations of tables. This package supports table headers, cells
src/Text/GridTable/Trace.hs view
@@ -26,6 +26,7 @@ import Data.Array.MArray import Data.Array.ST import Data.Function (on)+import Data.List (foldl') import Data.Maybe (fromMaybe) import Data.Set (Set) import Data.Text (Text)@@ -72,6 +73,26 @@ | Missing -- ^ padding for short lines deriving stock (Eq) +-- | Info on the grid. Used to keep track of information collected while+-- tracing a character grid. The set of cells is used as a kind of queue+-- during parsing, while the other data is required to assemble the+-- final table.+data TraceInfo = TraceInfo+ { gridRowSeps :: Set CharRow+ , gridColSeps :: Set CharCol+ , gridCorners :: Set CharIndex+ , gridCells :: Set CellTrace+ }++-- | Initial tracing info.+initialTraceInfo :: TraceInfo+initialTraceInfo = TraceInfo+ { gridRowSeps = Set.fromList [CharRow 1]+ , gridColSeps = Set.fromList [CharCol 1]+ , gridCorners = Set.fromList [(CharRow 1, CharCol 1)]+ , gridCells = Set.empty+ }+ -- | Converts a list of lines into a char array. toCharGrid :: [Text] -> CharGrid toCharGrid lines =@@ -179,26 +200,6 @@ _ -> pure () return mutGrid --- | Info on the grid. Used to keep track of information collected while--- tracing a character grid. The set of cells is used as a kind of queue--- during parsing, while the other data is required to assemble the--- final table.-data TraceInfo = TraceInfo- { gridRowSeps :: Set CharRow- , gridColSeps :: Set CharCol- , gridCorners :: Set CharIndex- , gridCells :: Set CellTrace- }---- | Initial tracing info.-initialTraceInfo :: TraceInfo-initialTraceInfo = TraceInfo- { gridRowSeps = Set.fromList [CharRow 1]- , gridColSeps = Set.fromList [CharCol 1]- , gridCorners = Set.fromList [(CharRow 1, CharCol 1)]- , gridCells = Set.fromList []- }- -- | Trace the given char grid and collect all relevant info. -- This function calls itself recursively. traceCharGrid :: CharGrid@@ -256,7 +257,8 @@ C '+' -> let colseps' = Set.insert j colseps in case scanDown charGrid start j of- Nothing -> loop colseps' (j + 1)+ Nothing -> loop colseps' (j + 1) <|>+ lastCellInRow charGrid start (j + 1) Just (end, rowseps, newcolseps) -> pure ( end , rowseps@@ -302,7 +304,7 @@ go j (Just colseps) = case charGrid ! (bottom, j) of C '+' -> Just (Set.insert j colseps) C '-' -> Just colseps- _ -> Nothing+ _ -> Nothing in if charGrid ! (bottom, left) /= C '+' then Nothing@@ -323,9 +325,50 @@ go i (Just rowseps) = case charGrid ! (i, left) of C '+' -> Just (Set.insert i rowseps) C '|' -> Just rowseps- _ -> Nothing+ _ -> Nothing in foldr go (Just Set.empty) [bottom - 1, bottom - 2 .. top + 1] +lastCellInRow :: CharGrid -> CharIndex -> CharCol -> Maybe ScanResult+lastCellInRow charGrid start@(top, _left) right =+ if bounds charGrid `inRange` (top, right) &&+ charGrid ! (top, right) == Missing+ then scanRestOfLines charGrid start+ else Nothing++lastColumn :: CharGrid -> CharCol+lastColumn = snd . snd . bounds++lastRow :: CharGrid -> CharRow+lastRow = fst . snd . bounds++scanRightRestOfLine :: CharGrid -> CharIndex -> CharRow -> Maybe ColSeps+scanRightRestOfLine charGrid (_top, left) bottom =+ let go :: CharCol -> Maybe ColSeps -> Maybe ColSeps+ go _ Nothing = Nothing+ go j (Just colseps) = case charGrid ! (bottom, j) of+ C '+' -> Just (Set.insert j colseps)+ C '-' -> Just colseps+ Missing -> Just colseps+ _ -> Nothing++ in if charGrid ! (bottom, left) /= C '+'+ then Nothing+ else foldr go (Just Set.empty) [left + 1 .. lastColumn charGrid]++scanRestOfLines :: CharGrid -> CharIndex -> Maybe ScanResult+scanRestOfLines charGrid start@(top, _left) =+ let go :: Maybe CharIndex -> CharRow -> Maybe CharIndex+ go idx i = idx <|>+ case scanRightRestOfLine charGrid start i of+ Nothing -> Nothing+ Just _colseps -> Just (i, lastColumn charGrid)+ in case foldl' go Nothing [top + 1 .. lastRow charGrid] of+ Just (bottom, right) -> Just+ ( (bottom, right)+ , Set.singleton bottom+ , Set.singleton right)+ Nothing -> Nothing+ -- | Gets the textual contents, i.e. the lines of a cell. getLines :: CharGrid -> CharIndex -> CharIndex -> [Text] getLines charGrid (top, left) (bottom, right) =@@ -418,11 +461,10 @@ let fromBuilderCell :: BuilderCell -> GridCell [Text] fromBuilderCell = \case FilledCell c -> c- FreeCell -> error "Found an unassigned cell."- getAssocs tblgrid >>= (\kvs -> forM_ kvs $ \(idx, bc) ->- case bc of- FreeCell -> error $ "unassigned: " ++ show idx- _ -> pure ())+ FreeCell ->+ -- Found an unassigned cell; replace with empty cell. TODO: This+ -- should be reported as a warning.+ ContentCell 1 1 mempty mapArray fromBuilderCell tblgrid -- | Calculate the array indices that are spanned by a cell.
test/test-gridtables.hs view
@@ -340,6 +340,68 @@ , arrayTableColSpecs = defaultAlign [3] }) + , testCase "top row that's too short" $+ let ls = T.unlines+ [ "+---+-+"+ , "|one|two|"+ , "+---+---+"+ , "|one|two|"+ , "+---+---+"+ ]+ in parse' gridTable ls @?=+ Right (ArrayTable+ { arrayTableCells = listArray ((1,1), (2,2))+ [ ContentCell 1 1 ["one"]+ , ContentCell 1 1 ["two"]+ , ContentCell 1 1 ["one"]+ , ContentCell 1 1 ["two"]]+ , arrayTableHead = Nothing+ , arrayTableColSpecs = defaultAlign [3, 3]+ })++ , testCase "all vertical seps in last column too short" $+ let ls = T.unlines+ [ "+----+:-:+"+ , "|eins|long text|"+ , "+----+---+"+ , "|zwei|long text|"+ , "+----+---+"+ , "|drei|more text|"+ , "+----+---+"+ ]+ in parse' gridTable ls @?=+ Right (ArrayTable+ { arrayTableCells = listArray ((1,1), (3,2))+ [ ContentCell 1 1 ["eins"]+ , ContentCell 1 1 ["long text"]+ , ContentCell 1 1 ["zwei"]+ , ContentCell 1 1 ["long text"]+ , ContentCell 1 1 ["drei"]+ , ContentCell 1 1 ["more text"]+ ]+ , arrayTableHead = Nothing+ , arrayTableColSpecs = defaultAlign [4, 9]+ })++ , testCase "missing cell" $+ let ls = T.unlines+ [ "+---+"+ , "|one|"+ , "+---+---+"+ , "|one|two|"+ , "+---+---+"+ ]+ in parse' gridTable ls @?=+ Right (ArrayTable+ { arrayTableCells = listArray ((1,1), (2,2))+ [ ContentCell 1 1 ["one"]+ , ContentCell 1 1 []+ , ContentCell 1 1 ["one"]+ , ContentCell 1 1 ["two"]]+ , arrayTableHead = Nothing+ , arrayTableColSpecs = defaultAlign [3, 3]+ })+ , testCase "followed by non-empty line" $ let ls = T.unlines [ "+-----+"