Skip to content

Commit 9727946

Browse files
committed
Improved prettyprinting, allow manual ordering in Huddle
Generalized tabular prettyprinting Added prettyprinter tests collectFrom now takes HuddleItems
1 parent 74b989c commit 9727946

File tree

6 files changed

+81
-20
lines changed

6 files changed

+81
-20
lines changed

.github/workflows/ci.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717
strategy:
1818
fail-fast: false
1919
matrix:
20-
ghc: ["8.10.7", "9.2.8", "9.6.4", "9.8.1"]
20+
ghc: ["8.10.7", "9.2.8", "9.6.4", "9.8.1", "9.10.1"]
2121
os: [ubuntu-latest]
2222
steps:
2323
- uses: actions/checkout@v3

src/Codec/CBOR/Cuddle/CDDL.hs

+5
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,11 @@ import Optics.Core ((%), (.~))
5353
import Optics.Getter (view)
5454
import Optics.Lens (lens)
5555

56+
-- | The CDDL constructor takes three arguments:
57+
-- 1. Top level comments that precede the first definition
58+
-- 2. The root definition
59+
-- 3. All the other top level comments and definitions
60+
-- This ensures that `CDDL` is correct by construction.
5661
data CDDL = CDDL [Comment] Rule [TopLevel]
5762
deriving (Eq, Generic, Show, ToExpr)
5863

src/Codec/CBOR/Cuddle/CDDL/Resolve.hs

+12-12
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
{-# LANGUAGE CPP #-}
12
{-# LANGUAGE DataKinds #-}
23
{-# LANGUAGE DerivingVia #-}
34
{-# LANGUAGE LambdaCase #-}
@@ -58,12 +59,14 @@ import Data.Functor.Identity (Identity (..))
5859
import Data.Generics.Product
5960
import Data.Generics.Sum
6061
import Data.Hashable
62+
#if __GLASGOW_HASKELL__ < 9100
63+
import Data.List (foldl')
64+
#endif
6165
import Data.List.NonEmpty qualified as NE
6266
import Data.Map.Strict qualified as Map
6367
import Data.Text qualified as T
6468
import GHC.Generics (Generic)
6569
import Optics.Core
66-
import Data.List (foldl')
6770

6871
--------------------------------------------------------------------------------
6972
-- 1. Rule extensions
@@ -214,9 +217,6 @@ buildRefCTree rules = CTreeRoot $ fmap toCTreeRule rules
214217
toCTreeDataItem _ =
215218
It $ CTree.Postlude PTAny
216219

217-
toCTreeGroupEntryNC :: GroupEntry -> CTree.Node OrRef
218-
toCTreeGroupEntryNC = toCTreeGroupEntry
219-
220220
toCTreeGroupEntry :: GroupEntry -> CTree.Node OrRef
221221
toCTreeGroupEntry (GroupEntry (Just occi) _ (GEType mmkey t0)) =
222222
It $
@@ -247,20 +247,20 @@ buildRefCTree rules = CTreeRoot $ fmap toCTreeRule rules
247247
-- choice options
248248
toCTreeEnum :: Group -> CTree.Node OrRef
249249
toCTreeEnum (Group (a NE.:| [])) =
250-
It . CTree.Enum . It . CTree.Group $ toCTreeGroupEntryNC <$> gcGroupEntries a
250+
It . CTree.Enum . It . CTree.Group $ toCTreeGroupEntry <$> gcGroupEntries a
251251
toCTreeEnum (Group xs) =
252252
It . CTree.Choice $
253-
It . CTree.Enum . It . CTree.Group . fmap toCTreeGroupEntryNC <$> groupEntries
253+
It . CTree.Enum . It . CTree.Group . fmap toCTreeGroupEntry <$> groupEntries
254254
where
255255
groupEntries = fmap gcGroupEntries xs
256256

257257
-- Embed a group in another group, again floating out the choice options
258258
groupToGroup :: Group -> CTree.Node OrRef
259259
groupToGroup (Group (a NE.:| [])) =
260-
It . CTree.Group $ fmap toCTreeGroupEntryNC (gcGroupEntries a)
260+
It . CTree.Group $ fmap toCTreeGroupEntry (gcGroupEntries a)
261261
groupToGroup (Group xs) =
262262
It . CTree.Choice $
263-
fmap (It . CTree.Group . fmap toCTreeGroupEntryNC) (gcGroupEntries <$> xs)
263+
fmap (It . CTree.Group . fmap toCTreeGroupEntry) (gcGroupEntries <$> xs)
264264

265265
toKVPair :: Maybe MemberKey -> Type0 -> CTree.Node OrRef
266266
toKVPair Nothing t0 = toCTreeT0 t0
@@ -275,20 +275,20 @@ buildRefCTree rules = CTreeRoot $ fmap toCTreeRule rules
275275

276276
-- Interpret a group as a map. Note that we float out the choice options
277277
toCTreeMap :: Group -> CTree.Node OrRef
278-
toCTreeMap (Group (a NE.:| [])) = It . CTree.Map $ fmap toCTreeGroupEntryNC (gcGroupEntries a)
278+
toCTreeMap (Group (a NE.:| [])) = It . CTree.Map $ fmap toCTreeGroupEntry (gcGroupEntries a)
279279
toCTreeMap (Group xs) =
280280
It
281281
. CTree.Choice
282-
$ fmap (It . CTree.Map . fmap toCTreeGroupEntryNC) (gcGroupEntries <$> xs)
282+
$ fmap (It . CTree.Map . fmap toCTreeGroupEntry) (gcGroupEntries <$> xs)
283283

284284
-- Interpret a group as an array. Note that we float out the choice
285285
-- options
286286
toCTreeArray :: Group -> CTree.Node OrRef
287287
toCTreeArray (Group (a NE.:| [])) =
288-
It . CTree.Array $ fmap toCTreeGroupEntryNC (gcGroupEntries a)
288+
It . CTree.Array $ fmap toCTreeGroupEntry (gcGroupEntries a)
289289
toCTreeArray (Group xs) =
290290
It . CTree.Choice $
291-
fmap (It . CTree.Array . fmap toCTreeGroupEntryNC) (gcGroupEntries <$> xs)
291+
fmap (It . CTree.Array . fmap toCTreeGroupEntry) (gcGroupEntries <$> xs)
292292

293293
toCTreeMemberKey :: MemberKey -> CTree.Node OrRef
294294
toCTreeMemberKey (MKValue v) = It $ CTree.Literal v

src/Codec/CBOR/Cuddle/Comments.hs

+16-1
Original file line numberDiff line numberDiff line change
@@ -121,14 +121,24 @@ instance CollectComments Comment where
121121
hasComment :: HasComment a => a -> Bool
122122
hasComment = (/= mempty) . view commentL
123123

124+
-- | This operator is used to attach comments to terms. It will not overwrite
125+
-- any comments that are already present, but will add the new comments on a
126+
-- new line
127+
-- ```
128+
-- arr [0, 1] //- "This is an array with two values"
129+
-- ```
124130
(//-) :: HasComment a => a -> Comment -> a
125131
x //- cmt = x & commentL %~ (<> cmt)
126132

127133
infixr 0 //-
128134

135+
-- | This operator will parse the values from left to right and then append the
136+
-- parsed comment on the right to the parsed value on the left.
129137
(<*!) :: (HasComment a, Applicative m) => m a -> m Comment -> m a
130-
(<*!) c x = (//-) <$> c <*> x
138+
(<*!) x c = (//-) <$> x <*> c
131139

140+
-- | This operator will parse the values from left to right and then append the
141+
-- parsed comment on the left to the parsed value on the right.
132142
(!*>) :: (HasComment a, Applicative m) => m Comment -> m a -> m a
133143
(!*>) c x = flip (//-) <$> c <*> x
134144

@@ -154,6 +164,11 @@ instance CollectComments a => CollectComments (WithComment a) where
154164
withComment :: a -> WithComment a
155165
withComment = WithComment mempty
156166

167+
-- | This operator maps a function over a functor containing a `WithComment` and
168+
-- applies the comment within to the output of the applied function.
169+
-- ```
170+
-- (\x -> LInt x "a") !$> WithComment "b" (1 :: Integer) == Literal (LInt 1) "a\nb"
171+
-- ```
157172
(!$>) :: (HasComment b, Functor f) => (a -> b) -> f (WithComment a) -> f b
158173
f !$> wc = fmap (\(WithComment c x) -> f x //- c) wc
159174

src/Codec/CBOR/Cuddle/Huddle.hs

+7-4
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ module Codec.CBOR.Cuddle.Huddle (
2020
-- * Core Types
2121
Huddle,
2222
HuddleItem (..),
23-
huddleJoin,
23+
huddleAugment,
2424
Rule,
2525
Named,
2626
IsType0 (..),
@@ -144,8 +144,11 @@ data Huddle = Huddle
144144
}
145145
deriving (Generic, Show)
146146

147-
huddleJoin :: Huddle -> Huddle -> Huddle
148-
huddleJoin (Huddle rootsL itemsL) (Huddle rootsR itemsR) =
147+
-- | Joins two `Huddle` values with a left-bias. This means that this function
148+
-- is not symmetric and that any rules that are present in both prefer the
149+
-- definition from the `Huddle` value on the left.
150+
huddleAugment :: Huddle -> Huddle -> Huddle
151+
huddleAugment (Huddle rootsL itemsL) (Huddle rootsR itemsR) =
149152
Huddle (L.nubBy ((==) `on` name) $ rootsL <> rootsR) (itemsL |<> itemsR)
150153

151154
-- | This semigroup instance:
@@ -1057,7 +1060,7 @@ collectFrom topRs =
10571060
collectFromInit :: [HuddleItem] -> Huddle
10581061
collectFromInit rules =
10591062
Huddle (concatMap hiRule rules) (OMap.fromList $ (\x -> (hiName x, x)) <$> rules)
1060-
`huddleJoin` collectFrom rules
1063+
`huddleAugment` collectFrom rules
10611064

10621065
--------------------------------------------------------------------------------
10631066
-- Conversion to CDDL

src/Codec/CBOR/Cuddle/Pretty/Columnar.hs

+40-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
{-# LANGUAGE ScopedTypeVariables #-}
22

3+
-- | This module provides some utility functions to help with aligning pretty
4+
-- printed values by column.
35
module Codec.CBOR.Cuddle.Pretty.Columnar (
46
CellAlign (..),
57
Row (..),
@@ -27,33 +29,46 @@ data Cell ann = Cell
2729
, cellAlign :: CellAlign
2830
}
2931

32+
-- | Creates a cell by pretty printing the input value and then left-aligning
33+
-- the resulting `Doc` within the table.
3034
cellL :: Pretty a => a -> Cell ann
3135
cellL = (`Cell` LeftAlign) . pretty
3236

37+
-- | Creates a cell by pretty printing the input value and then right-aligning
38+
-- the resulting `Doc` within the table.
3339
cellR :: Pretty a => a -> Cell ann
3440
cellR = (`Cell` RightAlign) . pretty
3541

42+
-- | A cell that takes up a cell but has no content.
3643
emptyCell :: Cell ann
3744
emptyCell = Cell mempty LeftAlign
3845

46+
-- | Checks whether the cell contains a `Doc` with a rendered width of zero.
3947
isEmptyCell :: Cell ann -> Bool
4048
isEmptyCell (Cell d _) = renderedLen d == 0
4149

50+
-- | A row within the table.
4251
newtype Row ann = Row {rowCells :: [Cell ann]}
4352

53+
-- | Adds a cell at the beginning of the row.
4454
prependCell :: Cell ann -> Row ann -> Row ann
4555
prependCell c (Row cs) = Row $ c : cs
4656

57+
-- | A row with a single left-aligned document
4758
singletonRow :: Doc ann -> Row ann
4859
singletonRow x = Row [Cell x LeftAlign]
4960

61+
-- | `Columnar` is a two-dimensional table of `Doc`s. When rendered, the cells
62+
-- within each row will be aligned with the cells of every other row in the
63+
-- same column.
5064
newtype Columnar ann = Columnar {colRows :: [Row ann]}
5165

5266
prettyRow :: [Int] -> [Cell ann] -> Doc ann
5367
prettyRow = prettyRow'
5468
where
5569
prettyRow' [] (Cell c _ : cs) = c <> prettyRow' [] cs
5670
prettyRow' _ [] = mempty
71+
prettyRow' _ [Cell c LeftAlign] = c -- Do not add white space to the last cell
5772
prettyRow' (0 : ws) (_ : cs) = prettyRow' ws cs -- Skip empty columns
5873
prettyRow' (w : ws) (Cell c alignment : cs) =
5974
let
@@ -63,12 +78,23 @@ prettyRow = prettyRow'
6378
in
6479
align' w c <> prettyRow' ws cs
6580

81+
-- | Pretty print the `Columnar` as a table.
6682
prettyColumnar :: forall ann. Columnar ann -> Doc ann
6783
prettyColumnar (Columnar rows) = vcat $ prettyRow columnWidths . rowCells <$> rows
6884
where
6985
columnWidths =
7086
foldr (zipWith max . fmap (renderedLen . cellDoc) . rowCells) (repeat 0) rows
7187

88+
-- | Pretty prints the `Columnar` so that the rows are separated by by the
89+
-- separator `Doc` provided as the third argument and then everything is
90+
-- enclosed within the left and right brackets provided as the first and second
91+
-- argument accordingly. The brackets will be aligned with the separators in the
92+
-- first column, e.g.
93+
-- ```
94+
-- [ foo
95+
-- , bar
96+
-- ]
97+
-- ```
7298
columnarListing :: Doc ann -> Doc ann -> Doc ann -> Columnar ann -> Doc ann
7399
columnarListing lEnc rEnc _ (Columnar []) = lEnc <> rEnc
74100
columnarListing lEnc rEnc s (Columnar (row : rows)) =
@@ -80,10 +106,22 @@ columnarListing lEnc rEnc s (Columnar (row : rows)) =
80106
<> line'
81107
<> rEnc
82108

109+
-- | Pretty prints the `Columnar` so that every line after the first has a
110+
-- separator prepended to it. This can be useful when you want to align the rows,
111+
-- but the separator would cause all the other rows after the first to be shifted
112+
-- right by one. The way you use this is you reduce the indentation on the
113+
-- following lines by the width of the separator.
114+
-- ```
115+
-- foo = x
116+
-- , y
117+
-- , z
118+
-- ```
83119
columnarSepBy :: Doc ann -> Columnar ann -> Doc ann
84120
columnarSepBy _ (Columnar []) = mempty
85-
columnarSepBy s (Columnar (x : xs)) =
86-
prettyColumnar (Columnar [x]) <> line' <> prettyColumnar (Columnar $ prependRow <$> xs)
121+
columnarSepBy s (Columnar rows@(Row x : xs)) =
122+
prettyRow columnWidths x <> line' <> prettyColumnar (Columnar $ prependRow <$> xs)
87123
where
88124
prependRow (Row (Cell c al : cs)) = Row $ Cell (s <+> c) al : cs
89125
prependRow (Row []) = Row []
126+
columnWidths =
127+
foldr (zipWith max . fmap (renderedLen . cellDoc) . rowCells) (repeat 0) rows

0 commit comments

Comments
 (0)