packages feed

rainbox 0.10.0.2 → 0.12.0.0

raw patch · 26 files changed

+1518/−3103 lines, 26 filesdep +containersdep −ChasingBottomsdep −arraydep −transformersdep ~basePVP ok

version bump matches the API change (PVP)

Dependencies added: containers

Dependencies removed: ChasingBottoms, array, transformers

Dependency ranges changed: base

API changes (from Hackage documentation)

- Rainbox: Bar :: [Chunk] -> Bar
- Rainbox: background :: Cell -> Radiant
- Rainbox: bars :: Cell -> [Bar]
- Rainbox: boxCells :: (Ix col, Ix row) => Array (col, row) Cell -> Array (col, row) Box
- Rainbox: data Align a
- Rainbox: data Horiz
- Rainbox: data Vert
- Rainbox: glueBoxes :: (Ix col, Ix row) => Array (col, row) Box -> Box
- Rainbox: gridByCols :: [[Cell]] -> Box
- Rainbox: gridByRows :: [[Cell]] -> Box
- Rainbox: horiz :: Cell -> Align Horiz
- Rainbox: instance Eq Cell
- Rainbox: instance IsString Cell
- Rainbox: instance Show Cell
- Rainbox: newtype Bar
- Rainbox: printBox :: Box -> IO ()
- Rainbox: unBar :: Bar -> [Chunk]
- Rainbox: vert :: Cell -> Align Vert
- Rainbox.Array2d: arrayByCols :: a -> [[a]] -> Array (Int, Int) a
- Rainbox.Array2d: arrayByRows :: a -> [[a]] -> Array (Int, Int) a
- Rainbox.Array2d: cells :: Table lCol lRow col row a -> Array (col, row) a
- Rainbox.Array2d: cols :: (Ix col, Ix row) => Array (col, row) a -> [[a]]
- Rainbox.Array2d: data Table lCol lRow col row a
- Rainbox.Array2d: instance (Eq lCol, Eq lRow, Eq a, Ix col, Ix row) => Eq (Table lCol lRow col row a)
- Rainbox.Array2d: instance (Ix col, Ix row) => Functor (Table lCol lRow col row)
- Rainbox.Array2d: instance (Show lCol, Show lRow, Show col, Show row, Show a, Ix col, Ix row) => Show (Table lCol lRow col row a)
- Rainbox.Array2d: lCols :: Table lCol lRow col row a -> Array col lCol
- Rainbox.Array2d: lRows :: Table lCol lRow col row a -> Array row lRow
- Rainbox.Array2d: labelCols :: (Ix col, Ix row) => (col -> [(row, a)] -> lCol) -> Array (col, row) a -> Array col lCol
- Rainbox.Array2d: labelRows :: (Ix col, Ix row) => (row -> [(col, a)] -> lRow) -> Array (col, row) a -> Array row lRow
- Rainbox.Array2d: mapColLabels :: (Ix col, Ix row) => (lCol -> col -> [(lRow, row, a)] -> lCol') -> Table lCol lRow col row a -> Table lCol' lRow col row a
- Rainbox.Array2d: mapRowLabels :: (Ix col, Ix row) => (lRow -> row -> [(lCol, col, a)] -> lRow') -> Table lCol lRow col row a -> Table lCol lRow' col row a
- Rainbox.Array2d: mapTable :: (Ix col, Ix row) => (lCol -> lRow -> col -> row -> a -> b) -> Table lCol lRow col row a -> Table lCol lRow col row b
- Rainbox.Array2d: rows :: (Ix col, Ix row) => Array (col, row) a -> [[a]]
- Rainbox.Array2d: table :: (Ix col, Ix row) => (col -> [(row, a)] -> lCol) -> (row -> [(col, a)] -> lRow) -> Array (col, row) a -> Table lCol lRow col row a
- Rainbox.Box: Bar :: [Chunk] -> Bar
- Rainbox.Box: Height :: Int -> Height
- Rainbox.Box: Width :: Int -> Width
- Rainbox.Box: barToBox :: Bar -> Box
- Rainbox.Box: barsToBox :: Radiant -> Align Horiz -> [Bar] -> Box
- Rainbox.Box: blank :: Radiant -> Height -> Width -> Box
- Rainbox.Box: blankH :: Radiant -> Int -> Box
- Rainbox.Box: blankV :: Radiant -> Int -> Box
- Rainbox.Box: bottom :: Align Vert
- Rainbox.Box: catH :: Radiant -> Align Vert -> [Box] -> Box
- Rainbox.Box: catV :: Radiant -> Align Horiz -> [Box] -> Box
- Rainbox.Box: center :: Align a
- Rainbox.Box: chunk :: Chunk -> Box
- Rainbox.Box: chunks :: [Chunk] -> Box
- Rainbox.Box: class HasWidth a
- Rainbox.Box: column :: Radiant -> Align Horiz -> [Box] -> [Box]
- Rainbox.Box: data Align a
- Rainbox.Box: data Box
- Rainbox.Box: data Horiz
- Rainbox.Box: data Vert
- Rainbox.Box: grow :: Radiant -> Height -> Width -> Align Vert -> Align Horiz -> Box -> Box
- Rainbox.Box: growH :: Radiant -> Int -> Align Horiz -> Box -> Box
- Rainbox.Box: growV :: Radiant -> Int -> Align Vert -> Box -> Box
- Rainbox.Box: height :: Box -> Int
- Rainbox.Box: left :: Align Horiz
- Rainbox.Box: newtype Bar
- Rainbox.Box: newtype Height
- Rainbox.Box: newtype Width
- Rainbox.Box: printBox :: Box -> IO ()
- Rainbox.Box: punctuateH :: Radiant -> Align Vert -> Box -> [Box] -> Box
- Rainbox.Box: punctuateV :: Radiant -> Align Horiz -> Box -> [Box] -> Box
- Rainbox.Box: render :: Box -> [Chunk]
- Rainbox.Box: resize :: Radiant -> Height -> Width -> Align Vert -> Align Horiz -> Box -> Box
- Rainbox.Box: resizeH :: Radiant -> Int -> Align Horiz -> Box -> Box
- Rainbox.Box: resizeV :: Radiant -> Int -> Align Vert -> Box -> Box
- Rainbox.Box: right :: Align Horiz
- Rainbox.Box: sepH :: Radiant -> Int -> Align Vert -> [Box] -> Box
- Rainbox.Box: sepV :: Radiant -> Int -> Align Horiz -> [Box] -> Box
- Rainbox.Box: top :: Align Vert
- Rainbox.Box: unBar :: Bar -> [Chunk]
- Rainbox.Box: unBox :: Box -> BoxP
- Rainbox.Box: unHeight :: Height -> Int
- Rainbox.Box: unWidth :: Width -> Int
- Rainbox.Box: view :: Height -> Width -> Align Vert -> Align Horiz -> Box -> Box
- Rainbox.Box: viewH :: Int -> Align Horiz -> Box -> Box
- Rainbox.Box: viewV :: Int -> Align Vert -> Box -> Box
- Rainbox.Box: width :: HasWidth a => a -> Int
- Rainbox.Box.Primitives: Bar :: [Chunk] -> Bar
- Rainbox.Box.Primitives: Height :: Int -> Height
- Rainbox.Box.Primitives: NoHeight :: Int -> BoxP
- Rainbox.Box.Primitives: Rod :: [Nibble] -> Rod
- Rainbox.Box.Primitives: Width :: Int -> Width
- Rainbox.Box.Primitives: WithHeight :: [Rod] -> BoxP
- Rainbox.Box.Primitives: barToBox :: Bar -> Box
- Rainbox.Box.Primitives: barsToBox :: Radiant -> Align Horiz -> [Bar] -> Box
- Rainbox.Box.Primitives: blank :: Radiant -> Height -> Width -> Box
- Rainbox.Box.Primitives: bottom :: Align Vert
- Rainbox.Box.Primitives: catH :: Radiant -> Align Vert -> [Box] -> Box
- Rainbox.Box.Primitives: catV :: Radiant -> Align Horiz -> [Box] -> Box
- Rainbox.Box.Primitives: center :: Align a
- Rainbox.Box.Primitives: chunks :: [Chunk] -> Box
- Rainbox.Box.Primitives: class HasWidth a
- Rainbox.Box.Primitives: data Align a
- Rainbox.Box.Primitives: data Box
- Rainbox.Box.Primitives: data BoxP
- Rainbox.Box.Primitives: data Horiz
- Rainbox.Box.Primitives: data Nibble
- Rainbox.Box.Primitives: data Spaces
- Rainbox.Box.Primitives: data Vert
- Rainbox.Box.Primitives: height :: Box -> Int
- Rainbox.Box.Primitives: instance Eq Bar
- Rainbox.Box.Primitives: instance Eq Box
- Rainbox.Box.Primitives: instance Eq BoxP
- Rainbox.Box.Primitives: instance Eq Height
- Rainbox.Box.Primitives: instance Eq Horiz
- Rainbox.Box.Primitives: instance Eq Nibble
- Rainbox.Box.Primitives: instance Eq Rod
- Rainbox.Box.Primitives: instance Eq Spaces
- Rainbox.Box.Primitives: instance Eq Vert
- Rainbox.Box.Primitives: instance Eq Width
- Rainbox.Box.Primitives: instance Eq a => Eq (Align a)
- Rainbox.Box.Primitives: instance HasWidth Bar
- Rainbox.Box.Primitives: instance HasWidth Box
- Rainbox.Box.Primitives: instance HasWidth BoxP
- Rainbox.Box.Primitives: instance HasWidth Chunk
- Rainbox.Box.Primitives: instance HasWidth Nibble
- Rainbox.Box.Primitives: instance HasWidth Rod
- Rainbox.Box.Primitives: instance HasWidth Spaces
- Rainbox.Box.Primitives: instance IsString Bar
- Rainbox.Box.Primitives: instance IsString Box
- Rainbox.Box.Primitives: instance IsString Nibble
- Rainbox.Box.Primitives: instance IsString Rod
- Rainbox.Box.Primitives: instance Monoid Bar
- Rainbox.Box.Primitives: instance Ord Height
- Rainbox.Box.Primitives: instance Ord Width
- Rainbox.Box.Primitives: instance Show Bar
- Rainbox.Box.Primitives: instance Show Box
- Rainbox.Box.Primitives: instance Show BoxP
- Rainbox.Box.Primitives: instance Show Height
- Rainbox.Box.Primitives: instance Show Horiz
- Rainbox.Box.Primitives: instance Show Nibble
- Rainbox.Box.Primitives: instance Show Rod
- Rainbox.Box.Primitives: instance Show Spaces
- Rainbox.Box.Primitives: instance Show Vert
- Rainbox.Box.Primitives: instance Show Width
- Rainbox.Box.Primitives: instance Show a => Show (Align a)
- Rainbox.Box.Primitives: left :: Align Horiz
- Rainbox.Box.Primitives: newtype Bar
- Rainbox.Box.Primitives: newtype Height
- Rainbox.Box.Primitives: newtype Rod
- Rainbox.Box.Primitives: newtype Width
- Rainbox.Box.Primitives: numSpaces :: Spaces -> Int
- Rainbox.Box.Primitives: right :: Align Horiz
- Rainbox.Box.Primitives: spcBackground :: Spaces -> Radiant
- Rainbox.Box.Primitives: split :: Int -> (Int, Int)
- Rainbox.Box.Primitives: top :: Align Vert
- Rainbox.Box.Primitives: unBar :: Bar -> [Chunk]
- Rainbox.Box.Primitives: unBox :: Box -> BoxP
- Rainbox.Box.Primitives: unHeight :: Height -> Int
- Rainbox.Box.Primitives: unNibble :: Nibble -> Either Spaces Chunk
- Rainbox.Box.Primitives: unRod :: Rod -> [Nibble]
- Rainbox.Box.Primitives: unWidth :: Width -> Int
- Rainbox.Box.Primitives: viewH :: Int -> Align Horiz -> Box -> Box
- Rainbox.Box.Primitives: viewV :: Int -> Align Vert -> Box -> Box
- Rainbox.Box.Primitives: width :: HasWidth a => a -> Int
- Rainbox.Reader: (/+/) :: Monad m => Box -> Box -> Env m Box
- Rainbox.Reader: (/-/) :: Monad m => Box -> Box -> Env m Box
- Rainbox.Reader: (<+>) :: Monad m => Box -> Box -> Env m Box
- Rainbox.Reader: (<->) :: Monad m => Box -> Box -> Env m Box
- Rainbox.Reader: Bar :: [Chunk] -> Bar
- Rainbox.Reader: Height :: Int -> Height
- Rainbox.Reader: Specs :: Radiant -> Align Horiz -> Align Vert -> Int -> Int -> Specs
- Rainbox.Reader: Width :: Int -> Width
- Rainbox.Reader: alignH :: Specs -> Align Horiz
- Rainbox.Reader: alignV :: Specs -> Align Vert
- Rainbox.Reader: background :: Specs -> Radiant
- Rainbox.Reader: blank :: Radiant -> Height -> Width -> Box
- Rainbox.Reader: blankH :: Monad m => Int -> Env m Box
- Rainbox.Reader: blankV :: Monad m => Int -> Env m Box
- Rainbox.Reader: bottom :: Align Vert
- Rainbox.Reader: catH :: Monad m => [Box] -> Env m Box
- Rainbox.Reader: catV :: Monad m => [Box] -> Env m Box
- Rainbox.Reader: center :: Align a
- Rainbox.Reader: chunk :: Chunk -> Box
- Rainbox.Reader: chunks :: [Chunk] -> Box
- Rainbox.Reader: class HasWidth a
- Rainbox.Reader: column :: Monad m => [Box] -> Env m [Box]
- Rainbox.Reader: data Align a
- Rainbox.Reader: data Box
- Rainbox.Reader: data Horiz
- Rainbox.Reader: data Specs
- Rainbox.Reader: data Vert
- Rainbox.Reader: grow :: Monad m => Height -> Width -> Box -> Env m Box
- Rainbox.Reader: growH :: Monad m => Int -> Box -> Env m Box
- Rainbox.Reader: growV :: Monad m => Int -> Box -> Env m Box
- Rainbox.Reader: height :: Box -> Int
- Rainbox.Reader: instance Eq Specs
- Rainbox.Reader: instance Show Specs
- Rainbox.Reader: left :: Align Horiz
- Rainbox.Reader: newtype Bar
- Rainbox.Reader: newtype Height
- Rainbox.Reader: newtype Width
- Rainbox.Reader: printBox :: Box -> IO ()
- Rainbox.Reader: punctuateH :: Monad m => Box -> [Box] -> Env m Box
- Rainbox.Reader: punctuateV :: Monad m => Box -> [Box] -> Env m Box
- Rainbox.Reader: render :: Box -> [Chunk]
- Rainbox.Reader: resize :: Monad m => Height -> Width -> Box -> Env m Box
- Rainbox.Reader: resizeH :: Monad m => Int -> Box -> Env m Box
- Rainbox.Reader: resizeV :: Monad m => Int -> Box -> Env m Box
- Rainbox.Reader: right :: Align Horiz
- Rainbox.Reader: runEnv :: Specs -> Env Identity a -> a
- Rainbox.Reader: sepH :: Monad m => Int -> [Box] -> Env m Box
- Rainbox.Reader: sepV :: Monad m => Int -> [Box] -> Env m Box
- Rainbox.Reader: spaceH :: Specs -> Int
- Rainbox.Reader: spaceV :: Specs -> Int
- Rainbox.Reader: top :: Align Vert
- Rainbox.Reader: type Env = ReaderT Specs
- Rainbox.Reader: unBar :: Bar -> [Chunk]
- Rainbox.Reader: unBox :: Box -> BoxP
- Rainbox.Reader: unHeight :: Height -> Int
- Rainbox.Reader: unWidth :: Width -> Int
- Rainbox.Reader: view :: Monad m => Height -> Width -> Box -> Env m Box
- Rainbox.Reader: viewH :: Monad m => Int -> Box -> Env m Box
- Rainbox.Reader: viewV :: Monad m => Int -> Box -> Env m Box
- Rainbox.Reader: width :: HasWidth a => a -> Int
- Rainbox.Tutorial: Record :: String -> String -> [String] -> String -> String -> String -> Record
- Rainbox.Tutorial: balance :: Record -> String
- Rainbox.Tutorial: cell :: [Chunk] -> Radiant -> Cell
- Rainbox.Tutorial: cellRows :: [[Cell]]
- Rainbox.Tutorial: data Record
- Rainbox.Tutorial: email :: Record -> String
- Rainbox.Tutorial: firstName :: Record -> String
- Rainbox.Tutorial: instance Show Record
- Rainbox.Tutorial: lastName :: Record -> String
- Rainbox.Tutorial: phone :: Record -> String
- Rainbox.Tutorial: printSampleBox :: IO ()
- Rainbox.Tutorial: recordToCells :: Record -> Radiant -> [Cell]
- Rainbox.Tutorial: records :: [Record]
- Rainbox.Tutorial: spacedOutCells :: [[Cell]]
+ Rainbox: blank :: Alignment a -> Radiant -> Height -> Width -> Box a
+ Rainbox: cellBackground :: Cell -> Radiant
+ Rainbox: cellHoriz :: Cell -> Alignment Horizontal
+ Rainbox: cellRows :: Cell -> Seq (Seq Chunk)
+ Rainbox: cellVert :: Cell -> Alignment Vertical
+ Rainbox: centerH :: Alignment Horizontal
+ Rainbox: centerV :: Alignment Vertical
+ Rainbox: class Orientation a
+ Rainbox: data Alignment a
+ Rainbox: data Horizontal
+ Rainbox: data Vertical
+ Rainbox: fromChunk :: Alignment a -> Radiant -> Chunk -> Box a
+ Rainbox: intersperse :: a -> Seq a -> Seq a
+ Rainbox: separator :: Radiant -> Int -> Cell
+ Rainbox: spacer :: Orientation a => Radiant -> Int -> Box a
+ Rainbox: spreader :: Orientation a => Alignment a -> Int -> Box a
+ Rainbox: tableByColumns :: Seq (Seq Cell) -> Box Horizontal
+ Rainbox: tableByRows :: Seq (Seq Cell) -> Box Vertical
+ Rainbox: wrap :: Orientation a => Alignment b -> Radiant -> Box a -> Box b
+ Rainbox.Core: ABottom :: Horizontal
+ Rainbox.Core: ALeft :: Vertical
+ Rainbox.Core: ARight :: Vertical
+ Rainbox.Core: ATop :: Horizontal
+ Rainbox.Core: Box :: (Seq (Payload a)) -> Box a
+ Rainbox.Core: Cell :: Seq (Seq Chunk) -> Alignment Horizontal -> Alignment Vertical -> Radiant -> Cell
+ Rainbox.Core: Center :: Alignment a
+ Rainbox.Core: Core :: (Either Chunk (Height, Width)) -> Core
+ Rainbox.Core: Height :: Int -> Height
+ Rainbox.Core: NonCenter :: a -> Alignment a
+ Rainbox.Core: Payload :: (Alignment a) -> Radiant -> (Either RodRows Core) -> Payload a
+ Rainbox.Core: Rod :: (Either (Int, Radiant) Chunk) -> Rod
+ Rainbox.Core: RodRowsNoHeight :: Int -> RodRows
+ Rainbox.Core: RodRowsWithHeight :: (Seq (Seq Rod)) -> RodRows
+ Rainbox.Core: Width :: Int -> Width
+ Rainbox.Core: above :: UpDown a => a -> Int
+ Rainbox.Core: addHeightMap :: Seq (Seq (Box Horizontal, b, c)) -> (Map Int (Int, Int), Seq (Seq (Box Horizontal, b, c)))
+ Rainbox.Core: addHorizontalPadding :: Box Vertical -> Seq RodRows
+ Rainbox.Core: addVerticalPadding :: Box Horizontal -> Seq RodRows
+ Rainbox.Core: addWidthMap :: Seq (Seq (Box Vertical, b, c)) -> (Map Int (Int, Int), Seq (Seq (Box Vertical, b, c)))
+ Rainbox.Core: below :: UpDown a => a -> Int
+ Rainbox.Core: blank :: Alignment a -> Radiant -> Height -> Width -> Box a
+ Rainbox.Core: bottom :: Alignment Horizontal
+ Rainbox.Core: cellBackground :: Cell -> Radiant
+ Rainbox.Core: cellHoriz :: Cell -> Alignment Horizontal
+ Rainbox.Core: cellRows :: Cell -> Seq (Seq Chunk)
+ Rainbox.Core: cellToBoxH :: Cell -> (Box Horizontal, Alignment Vertical, Radiant)
+ Rainbox.Core: cellToBoxV :: Cell -> (Box Vertical, Alignment Horizontal, Radiant)
+ Rainbox.Core: cellVert :: Cell -> Alignment Vertical
+ Rainbox.Core: center :: Alignment a
+ Rainbox.Core: centerH :: Alignment Horizontal
+ Rainbox.Core: centerV :: Alignment Vertical
+ Rainbox.Core: chunksFromRodRows :: RodRows -> Seq (Seq Chunk)
+ Rainbox.Core: class HasHeight a
+ Rainbox.Core: class HasWidth a
+ Rainbox.Core: class LeftRight a
+ Rainbox.Core: class Orientation a
+ Rainbox.Core: class UpDown a
+ Rainbox.Core: data Alignment a
+ Rainbox.Core: data Cell
+ Rainbox.Core: data Horizontal
+ Rainbox.Core: data Payload a
+ Rainbox.Core: data RodRows
+ Rainbox.Core: data Vertical
+ Rainbox.Core: emptyCell :: Cell
+ Rainbox.Core: equalize :: a -> Seq (Seq a) -> Seq (Seq a)
+ Rainbox.Core: fromChunk :: Alignment a -> Radiant -> Chunk -> Box a
+ Rainbox.Core: height :: HasHeight a => a -> Int
+ Rainbox.Core: horizontalMerge :: Seq RodRows -> RodRows
+ Rainbox.Core: instance (HasHeight a, HasHeight b) => HasHeight (Either a b)
+ Rainbox.Core: instance (HasWidth a, HasWidth b) => HasWidth (Either a b)
+ Rainbox.Core: instance Eq Core
+ Rainbox.Core: instance Eq Height
+ Rainbox.Core: instance Eq Horizontal
+ Rainbox.Core: instance Eq Rod
+ Rainbox.Core: instance Eq RodRows
+ Rainbox.Core: instance Eq Vertical
+ Rainbox.Core: instance Eq Width
+ Rainbox.Core: instance Eq a => Eq (Alignment a)
+ Rainbox.Core: instance Eq a => Eq (Box a)
+ Rainbox.Core: instance Eq a => Eq (Payload a)
+ Rainbox.Core: instance HasHeight (Box Horizontal)
+ Rainbox.Core: instance HasHeight (Box Vertical)
+ Rainbox.Core: instance HasHeight (Payload a)
+ Rainbox.Core: instance HasHeight Chunk
+ Rainbox.Core: instance HasHeight Core
+ Rainbox.Core: instance HasHeight Height
+ Rainbox.Core: instance HasHeight RodRows
+ Rainbox.Core: instance HasWidth (Box Horizontal)
+ Rainbox.Core: instance HasWidth (Box Vertical)
+ Rainbox.Core: instance HasWidth (Payload a)
+ Rainbox.Core: instance HasWidth Chunk
+ Rainbox.Core: instance HasWidth Core
+ Rainbox.Core: instance HasWidth Rod
+ Rainbox.Core: instance HasWidth RodRows
+ Rainbox.Core: instance HasWidth Width
+ Rainbox.Core: instance LeftRight (Box Vertical)
+ Rainbox.Core: instance LeftRight (Payload Vertical)
+ Rainbox.Core: instance Monoid (Box a)
+ Rainbox.Core: instance Ord Core
+ Rainbox.Core: instance Ord Height
+ Rainbox.Core: instance Ord Horizontal
+ Rainbox.Core: instance Ord Rod
+ Rainbox.Core: instance Ord RodRows
+ Rainbox.Core: instance Ord Vertical
+ Rainbox.Core: instance Ord Width
+ Rainbox.Core: instance Ord a => Ord (Alignment a)
+ Rainbox.Core: instance Ord a => Ord (Box a)
+ Rainbox.Core: instance Ord a => Ord (Payload a)
+ Rainbox.Core: instance Orientation Horizontal
+ Rainbox.Core: instance Orientation Vertical
+ Rainbox.Core: instance Show Core
+ Rainbox.Core: instance Show Height
+ Rainbox.Core: instance Show Horizontal
+ Rainbox.Core: instance Show Rod
+ Rainbox.Core: instance Show RodRows
+ Rainbox.Core: instance Show Vertical
+ Rainbox.Core: instance Show Width
+ Rainbox.Core: instance Show a => Show (Alignment a)
+ Rainbox.Core: instance Show a => Show (Box a)
+ Rainbox.Core: instance Show a => Show (Payload a)
+ Rainbox.Core: instance UpDown (Box Horizontal)
+ Rainbox.Core: instance UpDown (Payload Horizontal)
+ Rainbox.Core: intersperse :: a -> Seq a -> Seq a
+ Rainbox.Core: left :: Alignment Vertical
+ Rainbox.Core: mconcatSeq :: Monoid a => Seq a -> a
+ Rainbox.Core: newtype Box a
+ Rainbox.Core: newtype Core
+ Rainbox.Core: newtype Height
+ Rainbox.Core: newtype Rod
+ Rainbox.Core: newtype Width
+ Rainbox.Core: padBoxH :: Map Int (Int, Int) -> Seq (Seq (Box Horizontal, a, b)) -> Seq (Seq (Box Horizontal, a, b))
+ Rainbox.Core: padBoxV :: Map Int (Int, Int) -> Seq (Seq (Box Vertical, a, b)) -> Seq (Seq (Box Vertical, a, b))
+ Rainbox.Core: port :: LeftRight a => a -> Int
+ Rainbox.Core: render :: Orientation a => Box a -> Seq Chunk
+ Rainbox.Core: right :: Alignment Vertical
+ Rainbox.Core: rodRows :: Orientation a => Box a -> RodRows
+ Rainbox.Core: rodRowsFromCore :: Radiant -> Core -> RodRows
+ Rainbox.Core: rowToBoxH :: Box Vertical -> Box Horizontal
+ Rainbox.Core: rowToBoxV :: Box Horizontal -> Box Vertical
+ Rainbox.Core: separator :: Radiant -> Int -> Cell
+ Rainbox.Core: spacer :: Orientation a => Radiant -> Int -> Box a
+ Rainbox.Core: split :: Int -> (Int, Int)
+ Rainbox.Core: spreader :: Orientation a => Alignment a -> Int -> Box a
+ Rainbox.Core: starboard :: LeftRight a => a -> Int
+ Rainbox.Core: tableByColumns :: Seq (Seq Cell) -> Box Horizontal
+ Rainbox.Core: tableByRows :: Seq (Seq Cell) -> Box Vertical
+ Rainbox.Core: tallestCellMap :: Seq (Seq (Box Horizontal)) -> Map Int (Int, Int)
+ Rainbox.Core: toBoxH :: (Box Vertical, Alignment Horizontal, Radiant) -> Box Horizontal
+ Rainbox.Core: toBoxV :: (Box Horizontal, Alignment Vertical, Radiant) -> Box Vertical
+ Rainbox.Core: top :: Alignment Horizontal
+ Rainbox.Core: verticalMerge :: Seq RodRows -> RodRows
+ Rainbox.Core: widestCellMap :: Seq (Seq (Box Vertical)) -> Map Int (Int, Int)
+ Rainbox.Core: width :: HasWidth a => a -> Int
+ Rainbox.Core: wrap :: Orientation a => Alignment b -> Radiant -> Box a -> Box b
+ Rainbox.Tutorial: Blue :: Line
+ Rainbox.Tutorial: Green :: Line
+ Rainbox.Tutorial: Orange :: Line
+ Rainbox.Tutorial: Red :: Line
+ Rainbox.Tutorial: Silver :: Line
+ Rainbox.Tutorial: Station :: Text -> [Line] -> [Text] -> Bool -> Station
+ Rainbox.Tutorial: Yellow :: Line
+ Rainbox.Tutorial: addressCell :: Radiant -> [Text] -> Cell
+ Rainbox.Tutorial: box1 :: Box Vertical
+ Rainbox.Tutorial: box2 :: Box Horizontal
+ Rainbox.Tutorial: box3 :: Box Horizontal
+ Rainbox.Tutorial: box4 :: Box Horizontal
+ Rainbox.Tutorial: box5 :: Box Horizontal
+ Rainbox.Tutorial: data Line
+ Rainbox.Tutorial: data Station
+ Rainbox.Tutorial: instance Enum Line
+ Rainbox.Tutorial: instance Eq Line
+ Rainbox.Tutorial: instance Ord Line
+ Rainbox.Tutorial: instance Show Line
+ Rainbox.Tutorial: lineRow :: Radiant -> Line -> Seq Chunk
+ Rainbox.Tutorial: linesCell :: Radiant -> [Line] -> Cell
+ Rainbox.Tutorial: metroLines :: Station -> [Line]
+ Rainbox.Tutorial: name :: Station -> Text
+ Rainbox.Tutorial: nameCell :: Radiant -> Text -> Cell
+ Rainbox.Tutorial: renderBox1 :: IO ()
+ Rainbox.Tutorial: renderBox2 :: IO ()
+ Rainbox.Tutorial: renderBox3 :: IO ()
+ Rainbox.Tutorial: renderBox4 :: IO ()
+ Rainbox.Tutorial: renderBox5 :: IO ()
+ Rainbox.Tutorial: renderStationTable :: IO ()
+ Rainbox.Tutorial: stationCells :: Radiant -> Station -> [Cell]
+ Rainbox.Tutorial: stationTable :: Box Vertical
+ Rainbox.Tutorial: stations :: [Station]
+ Rainbox.Tutorial: textBox :: Radiant -> Text -> Box a
+ Rainbox.Tutorial: textWithin :: Orientation a => Alignment a -> Int -> Int -> Radiant -> Radiant -> Text -> Box a
+ Rainbox.Tutorial: underground :: Station -> Bool
+ Rainbox.Tutorial: undergroundCell :: Radiant -> Bool -> Cell
+ Rainbox.Tutorial: within :: Orientation a => Alignment a -> Int -> Int -> Radiant -> Box a -> Box a
- Rainbox: Cell :: [Bar] -> Align Horiz -> Align Vert -> Radiant -> Cell
+ Rainbox: Cell :: Seq (Seq Chunk) -> Alignment Horizontal -> Alignment Vertical -> Radiant -> Cell
- Rainbox: bottom :: Align Vert
+ Rainbox: bottom :: Alignment Horizontal
- Rainbox: center :: Align a
+ Rainbox: center :: Alignment a
- Rainbox: data Box
+ Rainbox: data Box a
- Rainbox: left :: Align Horiz
+ Rainbox: left :: Alignment Vertical
- Rainbox: render :: Box -> [Chunk]
+ Rainbox: render :: Orientation a => Box a -> Seq Chunk
- Rainbox: right :: Align Horiz
+ Rainbox: right :: Alignment Vertical
- Rainbox: top :: Align Vert
+ Rainbox: top :: Alignment Horizontal
- Rainbox.Tutorial: address :: Record -> [String]
+ Rainbox.Tutorial: address :: Station -> [Text]

Files

README.md view
@@ -4,23 +4,23 @@ Provides pretty printing of boxes in two dimensions.  Rainbox is useful for console programs that need to format tabular data. +On Hackage+==========++https://hackage.haskell.org/package/rainbox+ Documentation =============  In addition to the Haddock documentation, a tutorial is available in-[the Rainbox.Tutorial module](lib/Rainbox/Tutorial.lhs).  This-module is best read in your text editor or through the Github web-interface, as it is written in literate Haskell, which HsColour does-not fare so well with.+[the Rainbox.Tutorial module](lib/Rainbox/Tutorial.lhs).  Portability ===========  There's nothing unportable in Rainbox; however, it does use-[Rainbow](http://hackage.haskell.org/package/rainbow) which works-only on UNIX-like systems because it uses the UNIX terminfo library.-I only develop for UNIX-like systems because they are the only ones-I use.+[Rainbow](http://hackage.haskell.org/package/rainbow) which is only+tested on UNIX-like systems.  Tests =====@@ -29,7 +29,7 @@      cabal configure --enable-tests     cabal build-    dist/build/rainbox-test/rainbox-test+    dist/build/rainbox-properties/rainbox-properties     dist/build/rainbox-visual/rainbox-visual  The last test, `rainbox-visual`, relies on you to examine the output@@ -42,8 +42,8 @@ and although you can see the output of `rainbox-visual` there, it's not formatted quite right on Travis. -At this time, Rainbox is verified to work with GHC versions 7.4.1,-7.6.3, and 7.8.2.+At this time, Rainbox is verified to work with GHC versions in the 7.8 series+and the 7.10 series.  License =======
changelog view
@@ -1,3 +1,7 @@+0.12.0.0++  * rewrote API to use monoids+ 0.6.0.0    * update to work with newer version of Rainbow
lib/Rainbox.hs view
@@ -1,147 +1,53 @@--- | Create grids of (possibly) colorful boxes.+-- | Typically to use Rainbox you will want these @import@s: ----- For an introduction, see "Rainbox.Tutorial".  That file is--- written in literate Haskell, so you will want to look at the--- source itself.  HsColour does not do very well with literate--- Haskell, so you will want to view the file in your text editor or--- on Github:+-- @+-- import qualified Data.Sequence as Seq+-- import Rainbow+-- import Rainbox ----- <https://github.com/massysett/rainbox/blob/master/lib/Rainbox/Tutorial.lhs>+-- -- and, for GHC before 7.10:+-- import Data.Monoid+-- @ ----- This module only helps you create simple grids of cells, rather--- like a spreadsheet that does not allow you to merge or split--- cells.  If your needs are more complicated, use "Rainbox.Box",--- which allows you to build 'Box'es of arbitrary complexity by--- pasting simpler 'Box'es together.  (You can of course use this--- module together with "Rainbox.Box" to create very complex--- layouts.)+-- Rainbox does not re-export anything from "Data.Sequence" or+-- "Rainbow" because I don't know if you want all those things dumped+-- into the same namespace.+--+-- "Rainbox.Tutorial" wil get you started.  "Rainbox.Core" contains+-- the implementation details, which you should not need to pay+-- attention to (if you do need to use "Rainbox.Core" for ordinary+-- usage of the library, that's a bug; please report it.) module Rainbox-  (-    -- * Alignment-    Align-  , Horiz-  , Vert-  , top-  , bottom+  ( -- * Alignment and Boxes+    Alignment+  , Horizontal+  , Vertical+  , center   , left   , right-  , center--  -- * Bar-  , Bar(..)--  -- * Cell and Box-  , Cell(..)+  , top+  , bottom+  , centerH+  , centerV   , Box--  -- * Creating Box and gluing them together+  , Orientation ( spacer, spreader ) -  -- | For simple needs you will only need 'gridByRows' or-  -- 'gridByCols'; 'boxCells' and 'glueBoxes' are provided for more-  -- complex needs.-  , gridByRows-  , gridByCols-  , boxCells-  , glueBoxes+  -- * Box construction+  , fromChunk+  , blank+  , wrap    -- * Rendering   , render-  , printBox-  ) where -import Rainbow.Colors-import Rainbox.Box-import Rainbox.Array2d-import Data.Array-import Data.String---- | A 'Cell' consists of multiple screen lines; each screen line is--- a 'Bar'.-data Cell = Cell-  { bars :: [Bar]-  -- ^ Each Bar is one line on the screen.--  , horiz :: Align Horiz-  -- ^ How this Cell aligns compared to the other Cell in its-  -- column; use 'left', 'center', or 'right'.--  , vert :: Align Vert-  -- ^ How this Cell aligns compared to other Cell in its row; use-  -- 'top', 'center', or 'bottom'.--  , background :: Radiant-  -- ^ Background color for necessary padding that is added to the-  -- Cell to make it the correct width and height.  Does not affect-  -- the 'Chunk' contained in the 'bars'; these will use the colors-  -- that are designated in the 'Chunk' itself.-  } deriving (Eq, Show)---- | Creates a Cell with a 'left' horizontal alignment, a 'top'--- vertical alignment, and a background of 'noColorRadianat'.  The--- cell will be one 'Bar' tall and contain the text given in the--- string.-instance IsString Cell where-  fromString s = Cell [(fromString s)] left top noColorRadiant---- | Returns the width of each 'Bar' in the 'Cell'.-cellWidths :: Cell -> [Int]-cellWidths = map width . bars---- | Transforms a grid of 'Cell' to a grid of 'Box' by adding--- necessary padding to each 'Cell'.  In every row of the array, all--- the 'Box' will have equal height; in every column of the array,--- all the 'Box' will have equal width.-boxCells-  :: (Ix col, Ix row)-  => Array (col, row) Cell-  -> Array (col, row) Box-boxCells ay = cells $ mapTable conv tbl-  where-    tbl = table getWidth getHeight ay-      where-        getWidth _ = maximum . (0:) . concat . map cellWidths . map snd-        getHeight _ = maximum . (0:) . map (length . bars . snd)-    conv lCol lRow _ _ c = grow bk (Height lRow) (Width lCol) av ah bx-      where-        Cell bs ah av bk = c-        bx = barsToBox bk ah bs---- | Use 'catH' and 'catV' to fuse an array of 'Box' into a single--- 'Box'.  For example, if the 'bounds' of the array are--- @((0,0),(3,5))@, then the array has the number of cells given by--- @rangeSize ((0,0), (3,5))@ (that is, 24).  The upper left corner--- is @(0,0)@ and the lower right corner is @(3,5)@; the upper right--- and lower left corners are @(3,0)@ and @(0,5)@, respectively.-glueBoxes-  :: (Ix col, Ix row)-  => Array (col, row) Box-  -> Box-glueBoxes-  = catH noColorRadiant top-  . map (catV noColorRadiant left)-  . cols---- | Creates a single 'Box' from a list of rows of 'Cell'.  Each list--- is a row of 'Cell'.  The list of rows is from top to bottom; within--- each row, the cells are given from left to right.  All rows will be--- the same length as the first row.  Any row that is longer than the--- first row will have cells lopped off of the end, and any row that--- is shorter than the first row will be padded with empty cells on--- the end.--gridByRows :: [[Cell]] -> Box-gridByRows = glueBoxes . boxCells . arrayByRows padCell---- | Creates a single 'Box' from a list of columns of 'Cell'.  Each--- list is a column of 'Cell'.  The list of columns is from left to--- right; within each column, the cells are given from top to bottom.--- All columns will be the same height as the first column.  Any--- column that is longer than the first column will have cells lopped--- off the bottom, and any column that is shorter than the first--- column will be padded on the bottom with blank cells.+  -- * Tables+  , Cell(..)+  , tableByRows+  , tableByColumns+  , separator -gridByCols :: [[Cell]] -> Box-gridByCols = glueBoxes . boxCells . arrayByCols padCell+  -- * Utilities+  , intersperse+  ) where -padCell :: Cell-padCell = Cell [] left top noColorRadiant+import Rainbox.Core
− lib/Rainbox/Array2d.hs
@@ -1,249 +0,0 @@--- | Helpers for two-dimensional arrays.-module Rainbox.Array2d-  (-  -- * Tables-    Table-  , lCols-  , lRows-  , cells-  , table-  , labelCols-  , labelRows-  , mapTable-  , mapColLabels-  , mapRowLabels--  -- * Two-dimensional arrays-  , cols-  , rows-  , arrayByRows-  , arrayByCols-  ) where--import Data.Array---- * Tables---- | A Table is a two-dimensional array with two associated--- one-dimensional arrays: an array of labels for each column, and--- an array of labels for each row.-data Table lCol lRow col row a = Table-  { lCols :: Array col lCol-  -- ^ One label for each column-  , lRows :: Array row lRow-  -- ^ One label for each row-  , cells :: Array (col, row) a-  -- ^ Two-dimensional array of cells-  } deriving (Eq, Show)--instance (Ix col, Ix row) => Functor (Table lCol lRow col row) where-  fmap f t =  t { cells = fmap f . cells $ t }---- | Make a new Table.-table-  :: (Ix col, Ix row)-  => (col -> [(row, a)] -> lCol)-  -- ^ Function to generate the column labels.  It is applied to the-  -- column index and the full contents of the column.- -  -> (row -> [(col, a)] -> lRow)-  -- ^ Function to generate the row labels.  It is applied to the-  -- row index and the full contents of the row.--  -> Array (col, row) a-  -- ^ Cells of the table--  -> Table lCol lRow col row a-table fCol fRow ay = Table ayc ayr ay-  where-    ayc = labelCols fCol ay-    ayr = labelRows fRow ay---- | Given a two-dimensional array and a function that generates--- labels, return an array of column labels.-labelCols-  :: (Ix col, Ix row)-  => (col -> [(row, a)] -> lCol)-  -- ^ Function to generate the column labels.  It is applied to the-  -- column index and the full contents of the column.-  -> Array (col, row) a-  -> Array col lCol-labelCols f a = listArray (minCol, maxCol) es-  where-    ((minCol, minRow), (maxCol, maxRow)) = bounds a-    es = zipWith f ixsCols . map mkRow $ ixsCols-      where-        ixsCols = range (minCol, maxCol)-        mkRow col = zip ixsRows (map (\rw -> a ! (col, rw)) ixsRows)-          where-            ixsRows = range (minRow, maxRow)---- | Given a two-dimensional array and a function that generates--- labels, return an array of row labels.-labelRows-  :: (Ix col, Ix row)-  => (row -> [(col, a)] -> lRow)-  -- ^ Function to generate the row labels.  It is applied to the-  -- row index and the full contents of the row.-  -> Array (col, row) a-  -> Array row lRow-labelRows f a = listArray (minRow, maxRow) es-  where-    ((minCol, minRow), (maxCol, maxRow)) = bounds a-    es = zipWith f ixsRows . map mkCol $ ixsRows-      where-        ixsRows = range (minRow, maxRow)-        mkCol row = zip ixsCols (map (\cl -> a ! (cl, row)) ixsCols)-          where-            ixsCols = range (minCol, maxCol)---- | Transform the cells of the table.  Similar to the Functor--- instance, but the mapping function has access to the label and--- index of each cell in the 'Table'.-mapTable-  :: (Ix col, Ix row)-  => (lCol -> lRow -> col -> row -> a -> b)-  -- ^ Function is passed the label for the column, the label for-  -- the row, the column index, the row index, and the contents of-  -- the cell.  It returns a new cell.-  -> Table lCol lRow col row a-  -> Table lCol lRow col row b-mapTable f (Table cs rs ls) = Table cs rs ls'-  where-    ls' = listArray (bounds ls) . map g . assocs $ ls-      where-        g ((col, row), e) = f (cs ! col) (rs ! row) col row e---- | Transform the column labels.-mapColLabels-  :: (Ix col, Ix row)-  => (lCol -> col -> [(lRow, row, a)] -> lCol')-  -- ^ The function is passed the column label, column index, and-  -- the full contents of the column.-  -> Table lCol lRow col row a-  -> Table lCol' lRow col row a-mapColLabels f (Table cs rs ls) = Table cs' rs ls-  where-    ((colMin, rowMin), (colMax, rowMax)) = bounds ls-    cs' = listArray (colMin, colMax) es-      where-        es = zipWith3 f (elems cs) (indices cs) rws-          where-            rws = map mkRow . indices $ cs-              where-                mkRow idx = zipWith3 (,,) (elems rs)-                  (indices rs)-                  (map (ls !) (range ((idx, rowMin), (idx, rowMax))))---- | Transform the row labels.-mapRowLabels-  :: (Ix col, Ix row)-  => (lRow -> row -> [(lCol, col, a)] -> lRow')-  -- ^ The function is passed the row label, the row index, and the-  -- full contents of the row.-  -> Table lCol lRow col row a-  -> Table lCol lRow' col row a-mapRowLabels f (Table cs rs ls) = Table cs rs' ls-  where-    ((colMin, rowMin), (colMax, rowMax)) = bounds ls-    rs' = listArray (rowMin, rowMax) es-      where-        es = zipWith3 f (elems rs) (indices rs) cls-          where-            cls = map mkCol . indices $ rs-              where-                mkCol idx = zipWith3 (,,) (elems cs)-                  (indices cs)-                  (map (ls !) (range ((colMin, idx), (colMax, idx))))---- * Two-dimensional arrays---- | Given a two-dimensional array, return a list of columns in--- order.-cols-  :: (Ix col, Ix row)-  => Array (col, row) a-  -> [[a]]-cols ay = map getCol $ range (minCol, maxCol)-  where-    ((minCol, minRow), (maxCol, maxRow)) = bounds ay-    ixsRows = range (minRow, maxRow)-    getCol ixCol = map (\rw -> ay ! (ixCol, rw)) ixsRows---- | Given a two-dimensional array, return a list of rows in order.-rows-  :: (Ix col, Ix row)-  => Array (col, row) a-  -> [[a]]-rows ay = map getRow $ range (minRow, maxRow)-  where-    ((minCol, minRow), (maxCol, maxRow)) = bounds ay-    ixsCols = range (minCol, maxCol)-    getRow ixRow = map (\cl -> ay ! (cl, ixRow)) ixsCols---- | Generate a two-dimensional array from a list of rows.  Every--- row's length will be equal to the length of the first row; any rows--- after the first row that are shorter than the first row will have--- extra columns appended to the end.  Therefore, the resulting--- 'Array' will have no undefined values.-arrayByRows-  :: a-  -- ^ Append this empty value to rows that are too short.-  -> [[a]]-  -- ^ One list per row-  -> Array (Int, Int) a-arrayByRows empty ls-  = array ((0,0), (colMax, rowMax))-  . indexRows-  . padder empty-  $ ls-  where-    rowMax = length ls - 1-    colMax = case ls of-      [] -> -1-      x:_ -> length x - 1---- | Returns a list where every row is the same length as the first--- row.  Subsequent rows are padded on the end or have elements--- removed from the end, as needed.-padder-  :: a-  -- ^ Empty element-  -> [[a]]-  -> [[a]]-padder emp input = case input of-  [] -> []-  x:xs -> x : map adjust xs-    where-      len = length x-      adjust ls = take len $ ls ++ repeat emp--indexRows :: [[a]] -> [((Int, Int),a)]-indexRows = concat . map f . zip [0 ..]-  where-    f (rw, ls) = map g $ zip [0 ..] ls-      where-        g (cl, a) = ((cl, rw), a)---- | Generate a two-dimensional array from a list of columns.  Every--- column will be the same height as the first column; subsequent--- colums will be padded or truncated on the bottom, as needed.--- Therefore the resulting 'Array' will have no undefined elements.-arrayByCols-  :: a-  -- ^ Append this value to columns that are too short.-  -> [[a]]-  -- ^ One list per column; the head of each list is the top of the-  -- column.-  -> Array (Int, Int) a-arrayByCols empty ls-  = listArray ((0,0), (colMax, rowMax))-  . concat-  . padder empty-  $ ls-  where-    colMax = length ls - 1-    rowMax = case ls of-      [] -> -1-      x:_ -> length x - 1-
− lib/Rainbox/Box.hs
@@ -1,351 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}--- | Working with 'Box'.------ A 'Box' is a rectangular block of text.  You can paste 'Box'--- together to create new rectangles, and you can grow or reduce--- existing 'Box' to create new 'Box'es.------ There are only six primitive functions that make a 'Box':------ * 'B.blank' - formats a blank box with nothing but a (possibly)--- colorful background.  Useful to paste to other 'Box' to provide--- white space.------ * 'B.chunks' - Makes a box out of Rainbow 'Chunk'.------ * 'B.catH' - paste 'Box' together horizontally------ * 'B.catV' - paste 'Box' together vertically------ * 'B.viewH' - view a 'Box', keeping the same height but possibly--- trimming the width------ * 'B.viewV' - view a 'Box', keeping the same width but possibly--- trimming the height------ The other functions use these building blocks to do other useful--- things.------ There are many crude diagrams in the Haddock documentation.  A--- dash means a character with data; a period means a blank--- character.  When you print your 'Box', the blank characters will--- have the appropriate background color.-module Rainbox.Box-  (-  -- * Height and columns-    Height(..)-  , B.height-  , Width(..)-  , B.HasWidth(..)--  -- * Alignment-  , Align-  , Vert-  , Horiz-  , B.center-  , B.top-  , B.bottom-  , B.left-  , B.right--  -- * Box properties-  , B.Bar(..)-  , B.barToBox-  , B.barsToBox-  , B.Box-  , B.unBox--  -- * Making Boxes-  , B.blank-  , blankH-  , blankV-  , B.chunks-  , chunk--  -- * Pasting Boxes together-  , B.catH-  , B.catV-  , sepH-  , sepV-  , punctuateH-  , punctuateV--  -- * Viewing Boxes-  , view-  , B.viewH-  , B.viewV--  -- * Growing Boxes-  , grow-  , growH-  , growV-  , column--  -- * Resizing-  , resize-  , resizeH-  , resizeV--  -- * Printing Boxes-  , render-  , printBox-  ) where--import Data.Monoid-import Data.List (intersperse)-import qualified Data.Text as X-import Rainbow-import qualified Rainbox.Box.Primitives as B-import Rainbox.Box.Primitives-  ( Box-  , Align-  , Horiz-  , Vert-  , Height(..)-  , Width(..)-  , unBox-  )-import qualified Data.ByteString as BS------- # Box making------- | A blank horizontal box with a given width and no height.-blankH-  :: Radiant-  -- ^ Background colors-  -> Int-  -- ^ Box width-  -> Box-blankH bk i = B.blank bk (Height 0) (Width i)---- | A blank vertical box with a given length.-blankV-  :: Radiant-  -- ^ Background colors-  -> Int-  -- ^ Box height-  -> Box-blankV bk i = B.blank bk (Height i) (Width 0)---- | A Box made of a single 'Chunk'.-chunk :: Chunk -> Box-chunk = B.chunks . (:[])---- | Grow a box.  Each dimension of the result 'Box' is never smaller--- than the corresponding dimension of the input 'Box'.  Analogous to--- 'view', so you give the resulting dimensions that you want.  The--- alignment is analogous to 'view'; for instance, if you specify--- that the alignment is 'top' and 'left', the extra padding is--- added to the right and bottom sides of the resulting 'Box'.--grow-  :: Radiant-  -- ^ Background colors-  -> Height-  -> Width-  -> Align Vert-  -> Align Horiz-  -> Box-  -> Box-grow bk (B.Height h) (B.Width w) av ah-  = growH bk w ah-  . growV bk h av---- | Grow a 'Box' horizontally.--growH-  :: Radiant-  -- ^ Background colors-  -> Int-  -- ^ Resulting width-  -> Align Horiz-  -> Box-  -> Box-growH bk tgtW a b-  | tgtW < w = b-  | otherwise = B.catH bk B.top [lft, b, rt]-  where-    w = B.width b-    diff = tgtW - w-    (lft, rt) = (blankH bk wl, blankH bk wr)-    (wl, wr)-      | a == B.center = B.split diff-      | a == B.left = (0, diff)-      | otherwise = (diff, 0)---- | Grow a 'Box' vertically.-growV-  :: Radiant-  -- ^ Background colors-  -> Int-  -- ^ Resulting height-  -> Align Vert-  -> Box-  -> Box-growV bk tgtH a b-  | tgtH < h = b-  | otherwise = B.catV bk B.left [tp, b, bt]-  where-    h = B.height b-    diff = tgtH - h-    (tp, bt) = (blankV bk ht, blankV bk hb)-    (ht, hb)-      | a == B.center = B.split diff-      | a == B.top = (0, diff)-      | otherwise = (diff, 0)---- | Returns a list of 'Box', each being exactly as wide as the--- widest 'Box' in the input list.-column-  :: Radiant-  -- ^ Background colors-  -> Align Horiz-  -> [Box]-  -> [Box]-column bk ah bs = map (growH bk w ah) bs-  where-    w = maximum . (0:) . map B.width $ bs--view-  :: Height-  -> Width-  -> Align Vert-  -> Align Horiz-  -> Box-  -> Box-view h w av ah-  = B.viewH (B.unWidth w) ah-  . B.viewV (B.unHeight h) av------- # Resizing------- | Resize a 'Box'.  Will grow or trim it as necessary in order to--- reach the resulting size.  Returns an empty 'Box' if either--- 'Height' or 'Width' is less than 1.--resize-  :: Radiant-  -- ^ Background colors-  -> Height-  -> Width-  -> Align Vert-  -> Align Horiz-  -> Box-  -> Box-resize bk h w av ah-  = resizeH bk (unWidth w) ah-  . resizeV bk (unHeight h) av---- | Resize horizontally.-resizeH-  :: Radiant-  -- ^ Background colors-  -> Int-  -- ^ Resulting width-  -> Align Horiz-  -> Box-  -> Box-resizeH bk w a b-  | bw < w = growH bk w a b-  | bw > w = B.viewH w a b-  | otherwise = b-  where-    bw = B.width b---- | Resize vertically.-resizeV-  :: Radiant-  -- ^ Background colors-  -> Int-  -- ^ Resulting height-  -> Align Vert-  -> Box-  -> Box-resizeV bk h a b-  | bh < h = growV bk h a b-  | bh > h = B.viewV h a b-  | otherwise = b-  where-    bh = B.height b------- # Glueing------- | @sepH sep a bs@ lays out @bs@ horizontally with alignment @a@,---   with @sep@ amount of space in between each.-sepH-  :: Radiant-  -- ^ Background colors-  -> Int-  -- ^ Number of separating spaces-  -> Align Vert-  -> [Box]-  -> Box-sepH bk sep a = punctuateH bk a bl-  where-    bl = blankH bk sep---- | @sepV sep a bs@ lays out @bs@ vertically with alignment @a@,---   with @sep@ amount of space in between each.-sepV-  :: Radiant-  -- ^ Background colors-  -> Int-  -- ^ Number of separating spaces-  -> Align Horiz-  -> [Box]-  -> Box-sepV bk sep a = punctuateV bk a bl-  where-    bl = blankV bk sep---- | @punctuateH a p bs@ horizontally lays out the boxes @bs@ with a---   copy of @p@ interspersed between each.-punctuateH-  :: Radiant-  -- ^ Background colors-  -> Align Vert-  -> Box-  -> [Box]-  -> Box-punctuateH bk a sep = B.catH bk a . intersperse sep---- | A vertical version of 'punctuateH'.-punctuateV-  :: Radiant-  -- ^ Background colors-  -> Align Horiz-  -> Box-  -> [Box]-  -> Box-punctuateV bk a sep = B.catV bk a . intersperse sep---- | Convert a 'Box' to Rainbow 'Chunk's.  You can then print it, as--- described in "Rainbow".-render :: Box -> [Chunk]-render bx = case unBox bx of-  B.NoHeight _ -> []-  B.WithHeight rw ->-    concat . concat . map (: [["\n"]])-    . map renderRod $ rw--renderRod :: B.Rod -> [Chunk]-renderRod = map toChunk . B.unRod-  where-    toChunk = either spcToChunk id . B.unNibble-    spcToChunk ss =-      chunkFromText (X.replicate (B.numSpaces ss) (X.singleton ' '))-      <> back (B.spcBackground ss)---- | Prints a Box to standard output.  The highest number of available--- colors are used, using 'byteStringMakerFromEnvironment' from--- "Rainbow".-printBox :: Box -> IO ()-printBox b = do-  mkr <- byteStringMakerFromEnvironment-  mapM_ BS.putStr . chunksToByteStrings mkr . render $ b
− lib/Rainbox/Box/Primitives.hs
@@ -1,572 +0,0 @@--- | Box primitives.------ This module provides all functions that have access to the--- internals of a 'Box'.  There are only six functions that make a--- 'Box':------ * 'blank' - formats a blank box with nothing but a (possibly)--- colorful background.  Useful to paste to other 'Box' to provide--- white space.------ * 'chunks' - Makes a box out of Rainbow 'Chunk'.------ * 'catH' - paste 'Box' together horizontally------ * 'catV' - paste 'Box' together vertically------ * 'viewH' - view a 'Box', keeping the same height but possibly--- trimming the width------ * 'viewV' - view a 'Box', keeping the same width but possibly--- trimming the height------ There are many crude diagrams in the Haddock documentation.  A--- dash means a character with data; a period means a blank--- character.  When you print your 'Box', the blank characters will--- have the appropriate background color.-module Rainbox.Box.Primitives-  (-  -- * Alignment-    Align-  , Vert-  , Horiz-  , center-  , top-  , bottom-  , left-  , right--  -- * Box-  , Bar(..)-  , Rod(..)-  , barToBox-  , barsToBox-  , Nibble-  , unNibble-  , Spaces-  , numSpaces-  , spcBackground-  , BoxP(..)-  , Box-  , unBox--  -- * Height and Width-  , Height(..)-  , height-  , Width(..)-  , HasWidth(..)--  -- * Making Boxes-  , blank-  , chunks-  , catH-  , catV-  , viewH-  , viewV--  -- * Helpers-  , split--  ) where--import qualified Data.Foldable as F-import Rainbow-import Rainbow.Types-import Data.Monoid-import qualified Data.Text as X-import Data.String---- # Box--data Spaces = Spaces-  { numSpaces :: Int-  , spcBackground :: Radiant-  } deriving (Eq, Show)--instance HasWidth Spaces where-  width = numSpaces--newtype Nibble = Nibble { unNibble :: Either Spaces Chunk }-  deriving (Eq, Show)--instance IsString Nibble where-  fromString = Nibble . Right . fromString--instance HasWidth Nibble where-  width = either width width . unNibble---- | Occupies a single row on screen.  The 'Chunk's you place in a--- 'Bar' should not have any control characters such as newlines or--- tabs, as rainbox assumes that each character in a 'Bar' takes up--- one screen column and that each character does not create--- newlines.  Leave newline handling up to rainbox.  However,--- rainbox will /not/ check to make sure that your inputs do not--- contain newlines, tabs, or other spurious characters.  Similarly, use of--- combining characters will create unexpected results, as Rainbox--- will see something that takes up (for instance) two characters--- and think it takes up two screen columns, when in reality it will--- take up only one screen column.  So, if you need accented--- characters, use a single Unicode code point, not two code points.--- For example, for é, use U+00E9, not U+0065 and U+0301.-newtype Bar = Bar { unBar :: [Chunk] }-  deriving (Eq, Show)--barToBox :: Bar -> Box-barToBox = chunks . unBar--barsToBox-  :: Radiant-  -- ^ Background colors-  -> Align Horiz-  -> [Bar]-  -> Box-barsToBox bk ah = catV bk ah . map barToBox--instance IsString Bar where-  fromString = Bar . (:[]) . fromString--instance Monoid Bar where-  mempty = Bar []-  mappend (Bar l) (Bar r) = Bar $ l ++ r---- | A 'Box' has a width in columns and a height in rows.  Its--- height and width both are always at least zero.  It can have--- positive height even if its width is zero, and it can have--- positive width even if its height is zero.------ Each row in a 'Box' always has the same number of characters; a--- 'Box' with zero height has no characters but still has a certain--- width.--newtype Box = Box { unBox :: BoxP }-  deriving (Eq, Show)--newtype Rod = Rod { unRod :: [Nibble] }-  deriving (Eq, Show)--instance IsString Rod where-  fromString = Rod . (:[]) . fromString--instance HasWidth Rod where-  width = sum . map width . unRod---- | Box payload.  Has the data of the box.-data BoxP-  = NoHeight Int-  -- ^ A Box with width but no height.  The Int must be at least-  -- zero.  If it is zero, the Box has no height and no width.-  | WithHeight [Rod]-  -- ^ A Box that has height of at least one.  It must have at least-  -- one component Bar.-  deriving (Eq, Show)--instance HasWidth BoxP where-  width b = case b of-    NoHeight w -> w-    WithHeight ns -> sum . map width $ ns--instance IsString Box where-  fromString = Box . WithHeight . (:[]) . fromString---- # Height and Width---- | A count of rows-newtype Height = Height { unHeight :: Int }-  deriving (Eq, Ord, Show)---- | How many 'Rod' are in this 'Box'?-height :: Box -> Int-height b = case unBox b of-  NoHeight _ -> 0-  WithHeight rs -> length rs---- | A count of columns-newtype Width = Width { unWidth :: Int }-  deriving (Eq, Ord, Show)---- | How many columns are in this thing? A column is one character--- wide.  Every 'Bar' in a 'Box' always has the same number of--- columns.------ This is for things that have a single, solitary width, not things--- like columns that might have different widths at different--- points.-class HasWidth a where-  width :: a -> Int--instance HasWidth Bar where-  width = sum . map (sum . map X.length . chunkTexts) . unBar--instance HasWidth Box where-  width b = case unBox b of-    NoHeight i -> i-    WithHeight rs -> case rs of-      [] -> error "cols: error"-      x:_ -> width x--instance HasWidth Chunk where-  width = sum . map X.length . chunkTexts---- # Making Boxes---- | A blank 'Box'.  Useful for aligning other 'Box'.-blank-  :: Radiant-  -- ^ Background colors-  -> Height-  -> Width-  -> Box-blank bk r c-  | unHeight r < 1 = Box $ NoHeight (max 0 (unWidth c))-  | otherwise = Box . WithHeight $ replicate (unHeight r) row-  where-    row | unWidth c < 1 = Rod []-        | otherwise = Rod [ blanks bk (unWidth c) ]---- | A 'Box' made of 'Chunk'.  Always one Bar tall, and has as many--- columns as there are characters in the 'Chunk'.-chunks :: [Chunk] -> Box-chunks = Box . WithHeight . (:[]) . Rod . map (Nibble . Right)---- | Alignment.-data Align a = Center | NonCenter a-  deriving (Eq, Show)---- | Vertical alignment.-data Vert = ATop | ABottom-  deriving (Eq, Show)---- | Horizontal alignment.-data Horiz = ALeft | ARight-  deriving (Eq, Show)--center :: Align a-center = Center--top :: Align Vert-top = NonCenter ATop--bottom :: Align Vert-bottom = NonCenter ABottom--left :: Align Horiz-left = NonCenter ALeft--right :: Align Horiz-right = NonCenter ARight---- | Merge several Box horizontally into one Box.  That is, with--- alignment set to ATop:------ > --- ------- ------- > --- ---------- > --------- becomes------ > ----------------- > ----------....--- > ---...........------ With alignment set to ABottom, becomes------ > ---...........--- > ----------....--- > ----------------catH-  :: Radiant-  -- ^ Background colors-  -> Align Vert-  -> [Box]-  -> Box-catH bk al bs-  | null bs = Box $ NoHeight 0-  | hght == 0 = Box . NoHeight . sum . map width $ bs-  | otherwise = Box . WithHeight . mergeHoriz . map (pad . unBox) $ bs-  where-    pad = padHoriz bk al hght-    hght = F.maximum . (0:) . map height $ bs---- | Merge several Box vertically into one Box.  That is, with--- alignment set to 'left':------ > ---------- > ---------- >--- > ------ > ------ >--- > ------- > ---------- becomes------ > ---------- > ---------- > ---....--- > ---....--- > ---....--- > ----...--- > ----...------ With alignment set to 'right', becomes------ > ---------- > ---------- > ....------ > ....------ > ...------- > ...------catV-  :: Radiant-  -- ^ Background colors-  -> Align Horiz-  -> [Box]-  -> Box-catV bk al bs-  | null bs = Box $ NoHeight 0-  | otherwise = Box . foldr f (NoHeight w)-      . concat . map (flatten . unBox) $ bs-  where-    w = F.maximum . (0:) . map width $ bs-    f mayR bp = case mayR of-      Nothing -> bp-      Just rw -> case bp of-        WithHeight wh -> WithHeight $ padded : wh-        _ -> WithHeight [padded]-        where-          padded = padVert bk al w rw-    flatten bp = case bp of-      NoHeight _ -> [Nothing]-      WithHeight rs -> map Just rs----- | Given the resulting height, pad a list of Height.  So, when given--- a height of 3 and an alignment of 'top':------ > ----------- > -------------- becomes------ > ----------- > ----------- > ........------ where dashes is a 'Bar' with data, and dots is a blank 'Bar'.--padHoriz-  :: Radiant-  -- ^ Background colors-  -> Align Vert-  -> Int-  -> BoxP-  -> [Rod]-padHoriz bk a hght bp = case bp of-  NoHeight w -> map (Rod . (:[])) . replicate h $ blanks bk w-  WithHeight rs -> concat [tp, rs, bot]-    where-      nPad = max 0 $ h - length rs-      (nATop, nBot) = case a of-        Center -> split nPad-        NonCenter ATop -> (0, nPad)-        NonCenter ABottom -> (nPad, 0)-      pad = Rod [blanks bk len]-        where-          len = case rs of-            [] -> 0-            x:_ -> width x-      (tp, bot) = (replicate nATop pad, replicate nBot pad)-  where-    h = max 0 hght---- | Given the resulting width, pad a 'Bar'.  So, when given--- a width of 10 and an alignment of 'right',------ > ------------- becomes------ > ...---------padVert-  :: Radiant-  -- ^ Background colors-  -> Align Horiz-  -> Int-  -> Rod-  -> Rod-padVert bk a wdth rw@(Rod cs) = Rod . concat $ [lft, cs, rght]-  where-    nPad = max 0 $ w - width rw-    (nLeft, nRight) = case a of-      Center -> split nPad-      NonCenter ALeft -> (0, nPad)-      NonCenter ARight -> (nPad, 0)-    (lft, rght) = (mkPad nLeft, mkPad nRight)-    mkPad n-      | n == 0 = []-      | otherwise = [blanks bk n]-    w = max 0 wdth----- | Merge several horizontal Height into one set of horizontal 'Bar'.--- That is:------ > ----- ----- -------- > ----- ----- -------- > ----- ----- ----------- into------ > ------------------ > ------------------ > --------------------- Strange behavior will result if each input list is not exactly--- the same length.--mergeHoriz :: [[Rod]] -> [Rod]-mergeHoriz = foldr (zipWith merge) (repeat (Rod []))-  where-    merge (Rod r1) (Rod r2) = Rod $ r1 ++ r2---- # Viewing---- | View a 'Box', possibly shrinking it.  You set the size of your--- viewport and how it is oriented relative to the 'Box' as a whole.--- The 'Box' returned may be smaller than the argument 'Box', but it--- will never be bigger.------ Examples:------ >>> :set -XOverloadedStrings--- >>> let box = catV defaultBackground top [ "ab", "cd" ]--- >>> printBox . view (Height 1) (Width 1) left top $ box--- a------ >>> printBox . view (Height 1) (Width 1) right bottom $ box--- d--viewV :: Int -> Align Vert -> Box -> Box-viewV hght a (Box b) = Box $ case b of-  WithHeight rs-    | h == 0 -> NoHeight . width . head $ rs-    | otherwise -> WithHeight $ case a of-        NonCenter ATop -> take h rs-        NonCenter ABottom -> drop extra rs-        Center -> drop nDrop . take nTake $ rs-          where-            (trimL, trimR) = split extra-            nTake = length rs - trimR-            nDrop = trimL-        where-          extra = max 0 $ length rs - h-  x -> x-  where-    h = max 0 hght--viewH :: Int -> Align Horiz -> Box -> Box-viewH wdth a (Box b) = Box $ case b of-  NoHeight nh -> NoHeight (min w nh)-  WithHeight rs -> WithHeight $ map f rs-    where-      f rw = case a of-        NonCenter ALeft -> takeChars w rw-        NonCenter ARight -> dropChars extra rw-        Center -> dropChars nDrop . takeChars nTake $ rw-          where-            (trimL, trimR) = split extra-            nTake = max 0 $ width rw - trimR-            nDrop = trimL-        where-          extra = max 0 $ width rw - w-  where-    w = max 0 wdth---dropChars :: Int -> Rod -> Rod-dropChars colsIn = Rod . go colsIn . unRod-  where-    go n cs-      | n <= 0 = cs-      | otherwise = case cs of-         [] -> []-         x:xs-           | lenX <= n -> go (n - lenX) xs-           | otherwise -> x' : xs-           where-             lenX = case unNibble x of-              Left blnk -> numSpaces blnk-              Right chk -> width chk-             x' = case unNibble x of-              Left blnk -> Nibble . Left $-                blnk { numSpaces = numSpaces blnk - n }-              Right chk -> Nibble . Right . dropChunkChars n $ chk---- | Drops the given number of characters from a Chunk.-dropChunkChars :: Int -> Chunk -> Chunk-dropChunkChars n c = c { chunkTexts = go n (chunkTexts c) }-  where-    go nLeft ls = case ls of-      [] -> []-      t:ts-        | len < nLeft -> go (nLeft - len) ts-        | len == nLeft -> ts-        | otherwise -> X.drop nLeft t : ts-        where-          len = X.length t--takeChars :: Int -> Rod -> Rod-takeChars colsIn = Rod . go colsIn . unRod-  where-    go n cs-      | n <= 0 = []-      | otherwise = case cs of-          [] -> []-          x:xs-            | lenX <= n -> x : go (n - lenX) xs-            | otherwise -> [x']-            where-              (lenX, x') = case unNibble x of-                Left blnk ->-                  ( numSpaces blnk,-                    Nibble . Left $ blnk { numSpaces = n } )-                Right chk ->-                  ( width chk,-                    Nibble . Right . takeChunkChars n $ chk)--takeChunkChars :: Int -> Chunk -> Chunk-takeChunkChars n c = c { chunkTexts = go n (chunkTexts c) }-  where-    go nLeft ls = case ls of-      [] -> []-      t:ts-        | len < nLeft -> t : go (nLeft - len) ts-        | len == nLeft -> [t]-        | otherwise -> [X.take nLeft t]-        where-          len = X.length t------- # Helpers------- | Generate spaces.-blanks-  :: Radiant-  -- ^ Background colors-  -> Int-  -- ^ Number of blanks-  -> Nibble-blanks bk c = Nibble (Left (Spaces c bk))---- | Split a number into two parts, so that the sum of the two parts--- is equal to the original number.-split :: Int -> (Int, Int)-split i = (r, r + rm)-  where-    (r, rm) = i `quotRem` 2-
+ lib/Rainbox/Core.hs view
@@ -0,0 +1,683 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE FlexibleContexts #-}+{-# OPTIONS_HADDOCK not-home #-}+-- | Contains the innards of 'Rainbox'.  You shouldn't need anything+-- in here.  Some functions here are partial or have undefined results+-- if their inputs don't respect particular invariants.+module Rainbox.Core where++import Rainbow+import Control.Monad (join)+import Data.Monoid+import Rainbow.Types (Chunk(..))+import Data.Sequence (Seq, ViewL(..), viewl, (|>), (<|))+import qualified Data.Foldable as F+import qualified Data.Sequence as Seq+import qualified Data.Text as X+import qualified Data.Map as M++-- # Alignment++-- | Alignment.  Used in conjunction with 'Horizontal' and 'Vertical',+-- this determines how a payload aligns with the axis of a 'Box'.+data Alignment a = Center | NonCenter a+  deriving (Eq, Ord, Show)++-- # Horizontal and vertical++-- | Determines how a payload aligns with a horizontal axis.+data Horizontal = ATop | ABottom+  deriving (Eq, Ord, Show)++-- | Determines how a payload aligns with a vertical axis.+data Vertical = ALeft | ARight+  deriving (Eq, Ord, Show)++-- | Place this payload so that it is centered on the vertical axis or+-- horizontal axis.+center :: Alignment a+center = Center++-- | Center horizontally; like 'center', but monomorphic.+centerH :: Alignment Horizontal+centerH = center++-- | Center vertically; like 'center', but monomorphic.+centerV :: Alignment Vertical+centerV = center++-- | Place this payload's left edge on the vertical axis.+left :: Alignment Vertical+left = NonCenter ALeft++-- | Place this payload's right edge on the vertical axis.+right :: Alignment Vertical+right = NonCenter ARight++-- | Place this payload's top edge on the horizontal axis.+top :: Alignment Horizontal+top = NonCenter ATop++-- | Place this payload's bottom edge on the horizontal axis.+bottom :: Alignment Horizontal+bottom = NonCenter ABottom+++-- # Width and height++-- | A count of rows.+newtype Height = Height Int+  deriving (Eq, Ord, Show)++-- | A count of columns.+newtype Width = Width Int+  deriving (Eq, Ord, Show)++class HasHeight a where+  height :: a -> Int++instance HasHeight Height where+  height (Height a) = max 0 a++instance HasHeight Chunk where+  height _ = 1++instance (HasHeight a, HasHeight b) => HasHeight (Either a b) where+  height = either height height++class HasWidth a where+  width :: a -> Int++instance HasWidth Width where+  width (Width a) = max 0 a++instance HasWidth Chunk where+  width (Chunk _ ts) = F.sum . fmap X.length $ ts++instance (HasWidth a, HasWidth b) => HasWidth (Either a b) where+  width = either width width++-- # Core++-- | A 'Core' is either a single 'Chunk' or, if the box is blank, is+-- merely a height and a width.+newtype Core = Core (Either Chunk (Height, Width))+  deriving (Eq, Ord, Show)++instance HasWidth Core where+  width (Core ei) = either width (width . snd) ei++instance HasHeight Core where+  height (Core ei) = either height (height . fst) ei++-- # Rods++-- | An intermediate type used in rendering; it consists either of+-- text 'Chunk' or of a number of spaces coupled with a background color.+newtype Rod = Rod (Either (Int, Radiant) Chunk)+  deriving (Eq, Ord, Show)++instance HasWidth Rod where+  width (Rod ei) = case ei of+    Left (i, _) -> max 0 i+    Right c -> width c++-- # RodRows++-- | A list of screen rows; each screen row is a 'Seq' of 'Rod'.+--+-- A 'RodRows' with width but no height does nothing if rendered+-- alone, but it can affect the width of other 'RodRows' if combined+-- with them.+data RodRows+  = RodRowsWithHeight (Seq (Seq Rod))+  -- ^ Each outer 'Seq' represents a single screen row.  Each 'Seq'+  -- has a height of 1.+  --+  -- The outer 'Seq' must have a length of at least 1, even if the+  -- inner 'Seq' is empty.  If the outer 'Seq' has a length of zero,+  -- undefined behavior occurs.  For a 'RodRows' with no height and no+  -- width, use 'RodRowsNoHeight'.++  | RodRowsNoHeight Int+  -- ^ A 'RodRows' that has no height.  If the 'Int' is less than 1,+  -- the 'RodRows' has no width and no height.  Otherwise, the+  -- 'RodRows' has no height but has the given width.+  deriving (Eq, Ord, Show)++instance HasHeight RodRows where+  height (RodRowsWithHeight sq) = Seq.length sq+  height (RodRowsNoHeight _) = 0++instance HasWidth RodRows where+  width (RodRowsWithHeight sq) = F.foldl' max 0 . fmap (F.sum . fmap width) $ sq+  width (RodRowsNoHeight i) = max 0 i++-- | Convert a 'Core' to a 'Seq' of 'Rod' for rendering.+rodRowsFromCore :: Radiant -> Core -> RodRows+rodRowsFromCore bk (Core ei) = case ei of+  Left ck -> RodRowsWithHeight . Seq.singleton+    . Seq.singleton . Rod . Right $ ck+  Right (Height h, Width w)+    | h < 1  -> RodRowsNoHeight w+    | otherwise -> RodRowsWithHeight . Seq.replicate h . Seq.singleton+        . Rod . Left $ (w, bk)++-- | Converts a 'RodRows' to a nested 'Seq' of 'Chunk' in+-- preparation for rendering.  Newlines are added to the end of each+-- line.+chunksFromRodRows :: RodRows -> Seq (Seq Chunk)+chunksFromRodRows rr = case rr of+  RodRowsWithHeight sq -> fmap (|> "\n") . fmap (fmap chunkFromRod) $ sq+    where+      chunkFromRod (Rod ei) = case ei of+        Left (i, r) -> (chunkFromText . X.replicate i $ " ") <> back r+        Right c -> c+  RodRowsNoHeight _ -> Seq.empty+++-- # Payload++-- | A 'Payload' holds a 'RodRows', which determines the number+-- and content of the screen rows.  The 'Payload' also has an+-- 'Alignment', which specifies how the payload aligns with the axis.+-- Whether the 'Alignment' is 'Horizontal' or 'Vertical' determines+-- the orientation of the 'Payload'.  The 'Payload' also contains a+-- background color, which is type 'Radiant'.  The background color+-- extends continuously from the 'Payload' in both directions that are+-- perpendicular to the axis.++data Payload a = Payload (Alignment a) Radiant (Either RodRows Core)+  deriving (Eq, Ord, Show)++instance HasWidth (Payload a) where+  width (Payload _ _ ei) = width ei++instance HasHeight (Payload a) where+  height (Payload _ _ ei) = height ei++-- # Padding and merging++-- | Adds padding to the top and bottom of each Payload.  A Payload+-- with a Core is converted to a RodRows and has padding added; a+-- Payload with a RodRows has necessary padding added to the top and+-- bottom.  The number of elements in the resulting Seq is the same as+-- the number of elements in the input Seq; no merging is performed.++addVerticalPadding+  :: Box Horizontal+  -> Seq RodRows+addVerticalPadding bx@(Box sqnce) = fmap eqlize sqnce+  where+    maxTop = above bx+    maxBot = below bx+    eqlize bhp@(Payload _ rd ei) = case ei of+      Left rr -> eqlzeRodRows rr+      Right cre -> eqlzeRodRows (rodRowsFromCore rd cre)+      where+        eqlzeRodRows rr = case rr of+          RodRowsWithHeight sq -> RodRowsWithHeight $ tp w <> sq <> bot w+          RodRowsNoHeight i+            | maxTop + maxBot == 0 -> RodRowsNoHeight i+            | otherwise -> RodRowsWithHeight $ tp w <> bot w+          where+            w = width rr+        tp w = Seq.replicate (max 0 (maxTop - above bhp)) (pad w)+        bot w = Seq.replicate (max 0 (maxBot - below bhp)) (pad w)+        pad w = Seq.singleton . Rod . Left $ (w, rd)++-- | Merges multiple horizontal RodRows into a single RodRows.  All+-- RodRows must already have been the same height; if they are not the+-- same height, undefined behavior occurs.++horizontalMerge :: Seq RodRows -> RodRows+horizontalMerge sqn = case viewl sqn of+  EmptyL -> RodRowsNoHeight 0+  x :< xs -> case x of+    RodRowsNoHeight i -> RodRowsNoHeight $ F.foldl' comb i xs+      where+        comb acc x' = case x' of+          RodRowsNoHeight i' -> acc + i'+          RodRowsWithHeight _ -> error "horizontalMerge: error 1"+    RodRowsWithHeight sq -> RodRowsWithHeight $ F.foldl' comb sq xs+      where+        comb acc rr = case rr of+          RodRowsWithHeight sq' -> Seq.zipWith (<>) acc sq'+          RodRowsNoHeight _ -> error "horizontalMerge: error 2"++-- | Adds padding to the left and right of each Payload.+-- A Payload with a Core is converted to a RodRows and has padding+-- added; a Payload with a RodRows has necessary padding added to the+-- left and right.  The number of elements in the resulting Seq is+-- the same as the number of elements in the input Seq; no merging is+-- performed.++addHorizontalPadding+  :: Box Vertical+  -> Seq RodRows+addHorizontalPadding bx@(Box sqnce) = fmap eqlize sqnce+  where+    maxLeft = port bx+    maxRight = starboard bx+    eqlize (Payload a rd ei) = case ei of+      Left rr -> addLeftRight rr+      Right cre -> addLeftRight $ rodRowsFromCore rd cre+      where+        addLeftRight (RodRowsNoHeight _) = RodRowsNoHeight $ maxLeft + maxRight+        addLeftRight (RodRowsWithHeight sq) = RodRowsWithHeight $+          fmap addLeftRightToLine sq+        addLeftRightToLine lin = padder lenLft <> lin <> padder lenRgt+          where+            lenLin = F.sum . fmap width $ lin+            lenLft = case a of+              Center -> maxLeft - (fst . split $ lenLin)+              NonCenter ALeft -> maxLeft+              NonCenter ARight -> maxLeft - lenLin+            lenRgt = case a of+              Center -> maxRight - (snd . split $ lenLin)+              NonCenter ALeft -> maxRight - lenLin+              NonCenter ARight -> maxRight+            padder len+              | len < 1 = Seq.empty+              | otherwise = Seq.singleton . Rod . Left $ (len, rd)+++-- | Merge multiple vertical RodRows into a single RodRows.  Each+-- RodRows should already be the same width.++verticalMerge :: Seq RodRows -> RodRows+verticalMerge sqnce = case viewl sqnce of+  EmptyL -> RodRowsNoHeight 0+  x :< xs -> F.foldl' comb x xs+    where+      comb acc rr = case (acc, rr) of+        (RodRowsNoHeight w, RodRowsNoHeight _) -> RodRowsNoHeight w+        (RodRowsNoHeight _, RodRowsWithHeight sq) -> RodRowsWithHeight sq+        (RodRowsWithHeight sq, RodRowsNoHeight _) -> RodRowsWithHeight sq+        (RodRowsWithHeight sq1, RodRowsWithHeight sq2) ->+          RodRowsWithHeight $ sq1 <> sq2++-- # Box++-- | A 'Box' is the central building block.  It consists of zero or+-- more payloads; each payload has the same orientation, which is either+-- 'Horizontal' or 'Vertical'.  This orientation also determines+-- the orientation of the entire 'Box'.+--+-- A 'Box' is a 'Monoid' so you can combine them using the usual+-- monoid functions.  For a 'Box' 'Vertical', the leftmost values+-- added with 'mappend' are at the top of the 'Box'; for a 'Box'+-- 'Horizontal', the leftmost values added with 'mappend' are on the+-- left side of the 'Box'.+newtype Box a = Box (Seq (Payload a))+  deriving (Eq, Ord, Show)++instance Monoid (Box a) where+  mempty = Box Seq.empty+  mappend (Box x) (Box y) = Box (x <> y)++-- # Orientation++-- | This typeclass is responsible for transforming a 'Box' into+-- Rainbow 'Chunk' so they can be printed to your screen.  This+-- requires adding appropriate whitespace with the right colors, as+-- well as adding newlines in the right places.+class Orientation a where+  rodRows :: Box a -> RodRows++  spacer :: Radiant -> Int -> Box a+  -- ^ Builds a one-dimensional box of the given size; its single+  -- dimension is parallel to the axis.  When added to a+  -- box, it will insert blank space of the given length.  For a 'Box'+  -- 'Horizontal', this produces a horizontal line; for a 'Box'+  -- 'Vertical', a vertical line.++  spreader :: Alignment a -> Int -> Box a+  -- ^ Builds a one-dimensional box of the given size; its single+  -- dimension is perpendicular to the axis.  This can be used to make+  -- a 'Box' 'Vertical' wider or a 'Box' 'Horizontal' taller.++instance Orientation Vertical where+  rodRows = verticalMerge . addHorizontalPadding++  spacer r i = Box . Seq.singleton $+    Payload (NonCenter ALeft) r (Right . Core . Right $+      (Height (max 0 i), Width 0))+  spreader a i = Box . Seq.singleton $+    Payload a noColorRadiant (Right . Core . Right $+      (Height 0, Width (max 0 i)))++instance Orientation Horizontal where+  rodRows = horizontalMerge . addVerticalPadding++  spacer r i = Box . Seq.singleton $+    Payload (NonCenter ATop) r (Right . Core . Right $+      (Height 0, Width (max 0 i)))+  spreader a i = Box . Seq.singleton $+    Payload a noColorRadiant (Right . Core . Right $+      (Height (max 0 i), Width 0))++-- # port, starboard, above, below+++-- | Things that are oriented around a vertical axis.+class LeftRight a where+  -- | Length to the left of the vertical axis.+  port :: a -> Int++  -- | Length to the right of the vertical axis.+  starboard :: a -> Int++-- | Things that are oriented around a horizontal axis.+class UpDown a where+  -- | Number of lines above the horizontal axis.+  above :: a -> Int+  -- | Number of lines below the horizontal axis.+  below :: a -> Int+++instance LeftRight (Payload Vertical) where+  port (Payload a _ ei) = case a of+    NonCenter ALeft -> 0+    NonCenter ARight -> width ei+    Center -> fst . split . width $ ei++  starboard (Payload a _ s3) = case a of+    NonCenter ALeft -> width s3+    NonCenter ARight -> 0+    Center -> snd . split . width $ s3++instance UpDown (Payload Horizontal) where+  above (Payload a _ s3) = case a of+    NonCenter ATop -> 0+    NonCenter ABottom -> height s3+    Center -> fst . split . height $ s3++  below (Payload a _ s3) = case a of+    NonCenter ATop -> height s3+    NonCenter ABottom -> 0+    Center -> snd . split . height $ s3++instance LeftRight (Box Vertical) where+  port (Box sq) = F.foldl' max 0 . fmap port $ sq+  starboard (Box sq) = F.foldl' max 0 . fmap starboard $ sq++instance HasWidth (Box Vertical) where+  width b = port b + starboard b++instance HasHeight (Box Vertical) where+  height (Box sq) = F.sum . fmap height $ sq++instance UpDown (Box Horizontal) where+  above (Box sq) = F.foldl' max 0 . fmap above $ sq+  below (Box sq) = F.foldl' max 0 . fmap below $ sq++instance HasHeight (Box Horizontal) where+  height b = above b + below b++instance HasWidth (Box Horizontal) where+  width (Box sq) = F.sum . fmap width $ sq++-- # Box construction++-- | Construct a box from a single 'Chunk'.+fromChunk+  :: Alignment a+  -> Radiant+  -- ^ Background color.  The background color in the 'Chunk' is not+  -- changed; this background is used if the 'Payload' must be padded+  -- later on.+  -> Chunk+  -> Box a+fromChunk a r = Box . Seq.singleton . Payload a r  . Right . Core . Left++-- | Construct a blank box.  Useful for adding in background spacers.+-- For functions that build one-dimensional boxes, see 'spacer' and+-- 'spreader'.+blank+  :: Alignment a+  -> Radiant+  -- ^ Color for the blank area.+  -> Height+  -> Width+  -> Box a+blank a r h w =+  Box . Seq.singleton . Payload a r . Right . Core . Right $ (h, w)++-- | Wrap a 'Box' in another 'Box'.  Useful for changing a+-- 'Horizontal' 'Box' to a 'Vertical' one, or simply for putting a+-- 'Box' inside another one to control size and background color.+wrap+  :: Orientation a+  => Alignment b+  -- ^ Alignment for new 'Box'.  This also determines whether the new+  -- 'Box' is 'Horizontal' or 'Vertical'.+  -> Radiant+  -- ^ Background color for new box+  -> Box a+  -> Box b+wrap a r = Box . Seq.singleton . Payload a r . Left . rodRows++-- # Box rendering++-- | Convert a box to a 'Seq' of 'Chunk' in preparation for rendering.+-- Use 'F.toList' to convert the 'Seq' of 'Chunk' to a list so that+-- you can print it using the functions in "Rainbow".+render :: Orientation a => Box a -> Seq Chunk+render = join . chunksFromRodRows . rodRows+++-- # Tables++-- | A single cell in a spreadsheet-like grid.+data Cell = Cell+  { cellRows :: Seq (Seq Chunk)+  -- ^ The cell can have multiple rows of text; there is one 'Seq' for+  -- each row of text.+  , cellHoriz :: Alignment Horizontal+  -- ^ How this 'Cell' should align compared to other 'Cell' in its+  -- row.+  , cellVert :: Alignment Vertical+  -- ^ How this 'Cell' should align compared to other 'Cell' in its column.+  , cellBackground :: Radiant+  -- ^ Background color for this cell.  The background in the+  -- individual 'Chunk' in the 'cellRows' are not affected by+  -- 'cellBackground'; instead, 'cellBackground' determines the color+  -- of necessary padding that will be added so that the cells make a+  -- uniform table.+  }++-- | Creates a blank 'Cell' with the given background color and width;+-- useful for adding separators between columns.+separator :: Radiant -> Int -> Cell+separator rd i = Cell (Seq.singleton (Seq.singleton ck)) top left rd+  where+    ck = (chunkFromText $ X.replicate (max 0 i) " ") <> back rd++emptyCell :: Cell+emptyCell = Cell Seq.empty center center noColorRadiant+++-- Cells by row:+-- 0. Ensure each row is equal length+-- 1. Create one BoxV for each cell+-- 2. Create widest cell map+-- 3. Pad each BoxV to appropriate width, using cellVert alignment+-- 4. Convert each BoxV to BoxH, using cellHoriz and cellBackground+-- 5. mconcatSeq each row+-- 6. Convert each row to BoxV; use default background+--    and center alignment+-- 7. mconcatSeq the rows++-- | Create a table where each inner 'Seq' is a row of cells,+-- from left to right.  If necessary, blank cells are added to the end+-- of a row to ensure that each row has the same number of cells as+-- the longest row.+tableByRows :: Seq (Seq Cell) -> Box Vertical+tableByRows+  = mconcatSeq+  . fmap rowToBoxV+  . fmap mconcatSeq+  . fmap (fmap toBoxH)+  . uncurry padBoxV+  . addWidthMap+  . fmap (fmap cellToBoxV)+  . equalize emptyCell++rowToBoxV :: Box Horizontal -> Box Vertical+rowToBoxV = wrap center noColorRadiant++cellToBoxV :: Cell -> (Box Vertical, Alignment Horizontal, Radiant)+cellToBoxV (Cell rs ah av rd) = (bx, ah, rd)+  where+    bx = mconcatSeq+       . fmap (wrap av rd)+       . fmap (mconcatSeq . fmap (fromChunk top rd))+       $ rs++toBoxH+  :: (Box Vertical, Alignment Horizontal, Radiant)+  -> Box Horizontal+toBoxH (bv, ah, rd) = wrap ah rd bv++addWidthMap+  :: Seq (Seq (Box Vertical, b, c))+  -> (M.Map Int (Int, Int), Seq (Seq (Box Vertical, b, c)))+addWidthMap sqnce = (m, sqnce)+  where+    m = widestCellMap . fmap (fmap (\(a, _, _) -> a)) $ sqnce++padBoxV+  :: M.Map Int (Int, Int)+  -> Seq (Seq (Box Vertical, a, b))+  -> Seq (Seq (Box Vertical, a, b))+padBoxV mp = fmap (Seq.mapWithIndex f)+  where+    f idx (bx, a, b) = (bx <> padLeft <> padRight, a, b)+      where+        (lenL, lenR) = mp M.! idx+        padLeft = spreader right lenL+        padRight = spreader left lenR+++widestCellMap :: Seq (Seq (Box Vertical)) -> M.Map Int (Int, Int)+widestCellMap = F.foldl' outer M.empty+  where+    outer mpOuter = Seq.foldlWithIndex inner mpOuter+      where+        inner mpInner idx bx = case M.lookup idx mpInner of+          Nothing -> M.insert idx (port bx, starboard bx) mpInner+          Just (pOld, sOld) -> M.insert idx+            (max pOld (port bx), max sOld (starboard bx)) mpInner++-- Table by columns:+--+-- 0.  Equalize columns+-- 1.  Create one BoxH for each cell+-- 2.  Create tallest cell map+-- 3.  Pad each BoxH to appropriate height, using cellHeight alignment+-- 4.  Convert each BoxH to BoxV, using cellVert and cellBackground+-- 5.  mconcatSeq each column+-- 6.  Convert each column to BoxH+-- 7.  mconcatSeq the columns++-- | Create a table where each inner 'Seq' is a column of cells,+-- from top to bottom.  If necessary, blank cells are added to the end+-- of a column to ensure that each column has the same number of cells+-- as the longest column.+tableByColumns :: Seq (Seq Cell) -> Box Horizontal+tableByColumns+  = mconcatSeq+  . fmap rowToBoxH+  . fmap mconcatSeq+  . fmap (fmap toBoxV)+  . uncurry padBoxH+  . addHeightMap+  . fmap (fmap cellToBoxH)+  . equalize emptyCell+++rowToBoxH :: Box Vertical -> Box Horizontal+rowToBoxH = wrap top noColorRadiant+++cellToBoxH :: Cell -> (Box Horizontal, Alignment Vertical, Radiant)+cellToBoxH (Cell rs ah av rd) = (bx, av, rd)+  where+    bx = wrap ah rd+       . mconcatSeq+       . fmap (wrap av rd)+       . fmap (mconcatSeq . fmap (fromChunk top rd))+       $ rs++addHeightMap+  :: Seq (Seq (Box Horizontal, b, c))+  -> (M.Map Int (Int, Int), Seq (Seq (Box Horizontal, b, c)))+addHeightMap sqnce = (m, sqnce)+  where+    m = tallestCellMap . fmap (fmap (\(a, _, _) -> a)) $ sqnce++tallestCellMap :: Seq (Seq (Box Horizontal)) -> M.Map Int (Int, Int)+tallestCellMap = F.foldl' outer M.empty+  where+    outer mpOuter = Seq.foldlWithIndex inner mpOuter+      where+        inner mpInner idx bx = case M.lookup idx mpInner of+          Nothing -> M.insert idx (above bx, below bx) mpInner+          Just (aOld, bOld) -> M.insert idx+            (max aOld (above bx), max bOld (below bx)) mpInner+++padBoxH+  :: M.Map Int (Int, Int)+  -> Seq (Seq (Box Horizontal, a, b))+  -> Seq (Seq (Box Horizontal, a, b))+padBoxH mp = fmap (Seq.mapWithIndex f)+  where+    f idx (bx, a, b) = (bx <> padTop <> padBot, a, b)+      where+        (lenT, lenB) = mp M.! idx+        padTop = spreader bottom lenT+        padBot = spreader top lenB+++toBoxV+  :: (Box Horizontal, Alignment Vertical, Radiant)+  -> Box Vertical+toBoxV (bh, av, rd) = wrap av rd bh+++-- | Ensures that each inner 'Seq' is the same length by adding the+-- given empty element where needed.+equalize :: a -> Seq (Seq a) -> Seq (Seq a)+equalize emp sqnce = fmap adder sqnce+  where+    maxLen = F.foldl' max 0 . fmap Seq.length $ sqnce+    adder sq = sq <> pad+      where+        pad = Seq.replicate (max 0 (maxLen - Seq.length sq)) emp++mconcatSeq :: Monoid a => Seq a -> a+mconcatSeq = F.foldl' (<>) mempty+++-- # Utilities++-- | Like 'Data.List.intersperse' in "Data.List", but works on 'Seq'.+intersperse :: a -> Seq a -> Seq a+intersperse new sq = case viewl sq of+  EmptyL -> Seq.empty+  x :< xs -> x <| go xs+    where+      go sqnce = case viewl sqnce of+        EmptyL -> Seq.empty+        a :< as -> new <| a <| go as++-- | Split a number into two parts, so that the sum of the two parts+-- is equal to the original number.+split :: Int -> (Int, Int)+split i = (r, r + rm)+  where+    (r, rm) = i `quotRem` 2+
− lib/Rainbox/Reader.hs
@@ -1,290 +0,0 @@--- | 'Box' with many functions in a 'Reader' monad.------ The advantage of this module over "Rainbox" is that many of the--- functions have fewer arguments because they are instead carried--- in the 'Reader' monad.  This also allows you to use four infix--- operators to easily join up 'Box'.  The disadvantage is that--- using the 'Reader' monad adds a layer of indirection.-module Rainbox.Reader-  (-  -- * Box properties-    B.Bar(..)-  , B.Box-  , B.unBox--  -- * Height and columns-  , B.Height(..)-  , B.height-  , B.Width(..)-  , B.HasWidth(..)--  -- * Alignment-  , B.Align-  , B.Vert-  , B.Horiz-  , B.center-  , B.top-  , B.bottom-  , B.left-  , B.right--  -- * Reader monad-  , Specs(..)-  , Env-  , runEnv--  -- * Making Boxes-  , B.blank-  , blankH-  , blankV-  , B.chunks-  , R.chunk--  -- * Pasting Boxes together-  , catH-  , catV-  , sepH-  , sepV-  , punctuateH-  , punctuateV-  , (<->)-  , (<+>)-  , (/-/)-  , (/+/)--  -- * Viewing Boxes-  , view-  , viewH-  , viewV--  -- * Growing Boxes-  , grow-  , growH-  , growV-  , column--  -- * Resizing-  , resize-  , resizeH-  , resizeV--  -- * Printing Boxes-  , R.render-  , R.printBox-  ) where---import Control.Monad.Trans.Reader-import Data.Functor.Identity-import qualified Rainbox.Box.Primitives as B-import qualified Rainbox.Box as R-import Rainbox.Box.Primitives-  ( Box-  , Height-  , Width-  , Align-  , Horiz-  , Vert-  )-import Rainbow--data Specs = Specs-  { background :: Radiant-  , alignH :: Align Horiz-  , alignV :: Align Vert-  , spaceH :: Int-  -- ^ Amount of intervening space for horizontal joins-  , spaceV :: Int-  --  ^ Amount of intervening space for vertical joins-  } deriving (Eq, Show)--type Env = ReaderT Specs--runEnv :: Specs -> Env Identity a -> a-runEnv s = runIdentity . ($ s) . runReaderT--blankH :: Monad m => Int -> Env m Box-blankH i = do-  b <- asks background-  return $ R.blankH b i--blankV :: Monad m => Int -> Env m Box-blankV i = do-  b <- asks background-  return $ R.blankV b i--catH :: Monad m => [Box] -> Env m Box-catH bxs = do-  bk <- asks background-  al <- asks alignV-  return $ B.catH bk al bxs--catV :: Monad m => [Box] -> Env m Box-catV bxs = do-  bk <- asks background-  al <- asks alignH-  return $ B.catV bk al bxs--grow :: Monad m => Height -> Width -> Box -> Env m Box-grow r c bx = do-  b <- asks background-  h <- asks alignH-  v <- asks alignV-  return $ R.grow b r c v h bx--growH :: Monad m => Int -> Box -> Env m Box-growH i bx = do-  b <- asks background-  h <- asks alignH-  return $ R.growH b i h bx--growV :: Monad m => Int -> Box -> Env m Box-growV i bx = do-  b <- asks background-  v <- asks alignV-  return $ R.growV b i v bx--column :: Monad m => [Box] -> Env m [Box]-column bs = do-  bk <- asks background-  ah <- asks alignH-  return $ R.column bk ah bs--resize-  :: Monad m => Height-  -> Width-  -> Box-  -> Env m Box-resize r c bx = do-  b <- asks background-  h <- asks alignH-  v <- asks alignV-  return $ R.resize b r c v h bx--resizeH-  :: Monad m => Int-  -> Box-  -> Env m Box-resizeH i bx = do-  b <- asks background-  h <- asks alignH-  return $ R.resizeH b i h bx--resizeV-  :: Monad m => Int-  -> Box-  -> Env m Box-resizeV i bx = do-  b <- asks background-  v <- asks alignV-  return $ R.resizeV b i v bx--sepH-  :: Monad m => Int-  -> [Box]-  -> Env m Box-sepH i bx = do-  b <- asks background-  v <- asks alignV-  return $ R.sepH b i v bx--sepV-  :: Monad m => Int-  -> [Box]-  -> Env m Box-sepV i bx = do-  b <- asks background-  h <- asks alignH-  return $ R.sepV b i h bx--punctuateH-  :: Monad m => Box-  -> [Box]-  -> Env m Box-punctuateH bx bxs = do-  b <- asks background-  v <- asks alignV-  return $ R.punctuateH b v bx bxs--punctuateV-  :: Monad m => Box-  -> [Box]-  -> Env m Box-punctuateV bx bxs = do-  b <- asks background-  h <- asks alignH-  return $ R.punctuateV b h bx bxs--view-  :: Monad m-  => Height-  -> Width-  -> Box-  -> Env m Box-view h w b = do-  av <- asks alignV-  ah <- asks alignH-  return $ R.view h w av ah b--viewH-  :: Monad m-  => Int-  -> Box-  -> Env m Box-viewH h b = do-  ah <- asks alignH-  return $ B.viewH h ah b--viewV-  :: Monad m-  => Int-  -> Box-  -> Env m Box-viewV h b = do-  av <- asks alignV-  return $ B.viewV h av b---- | Paste two 'Box' together horizontally with no intervening--- space.  Left fixity, precedence 5.-(<->) :: Monad m => Box -> Box -> Env m Box-(<->) l r = do-  b <- asks background-  a <- asks alignV-  return $ B.catH b a [l, r]--infixl 5 <->---- | Paste two 'Box' together horizontally.  Intervening space is--- determined by 'spaceH'.--- Left fixity, precedence 5.-(<+>) :: Monad m => Box -> Box -> Env m Box-(<+>) l r = do-  bk <- asks background-  a <- asks alignV-  sp <- asks spaceH-  bx <- blankH sp-  return $ B.catH bk a [ l, bx, r ]--infixl 5 <+>---- | Paste two 'Box' together vertically with no intervening space.--- Left fixity, precedence 6.-(/-/) :: Monad m => Box -> Box -> Env m Box-(/-/) h l = do-  b <- asks background-  a <- asks alignH-  return $ B.catV b a [ h, l ]--infixl 6 /-/---- | Paste two 'Box' together vertically.  Intervening space is--- determined by 'spaceV'.  Left fixity, precedence 6.-(/+/) :: Monad m => Box -> Box -> Env m Box-(/+/) h l = do-  bk <- asks background-  a <- asks alignH-  sp <- asks spaceV-  bx <- blankV sp-  return $ B.catV bk a [ h, bx, l ]--infixl 6 /+/
+ lib/Rainbox/Tutorial.hs view
@@ -0,0 +1,521 @@+-- Modules might be imported solely so Haddock can hyperlink the+-- identifiers++{-# OPTIONS_GHC -fno-warn-unused-imports #-}+{-# LANGUAGE OverloadedStrings #-}++{-| The Rainbox tutorial++Rainbox helps you create arrangements of (possibly) colorful text+boxes. This module contains a tutorial.  Typically the "Rainbox"+module contains all you need.  There is also a "Rainbox.Core" module+which contains all the innards of Rainbox, but ordinarily you won't+need it.++The basic building block of Rainbox is known as a @core@.  A core is+either a single 'Chunk' or a blank box of arbitrary size.  A core made+of a single 'Chunk' should not contain any newlines.  Leave newline+handling up to Rainbox.  However, Rainbox will not check to ensure+that your 'Chunk' does not contain any newline characters.  If it does+contain newlines, your boxes will not look right.  Also, Rainbox needs+to know how wide your 'Chunk' are, in columns.  To measure width,+Rainbox simply counts the number of characters in the 'Chunk'.+Therefore, if you need accented characters, be sure to use a single+character, not composed characters.  That is, to get á, use U+00E1,+not U+00B4 and U+0061.++Many things in Rainbox have height and width.  Both the height and+width of an object can be zero, but never less than zero.  A @core@+made from a 'Chunk' always has a height of 1, and its width is equal+to the number of characters in the 'Text's that make up the 'Chunk'.+A @core@ made from a blank box has the height and width that you give+it, though neither its height nor its width is ever smaller than zero.++The next biggest building block after the @core@ is @payload@.  There+are two different types of payloads: vertical payloads and horizontal+ones.  A vertical payload aligns itself next to other vertical+payloads on a vertical axis, creating a chain of payloads.  The+vertical payload also has an alignment.  The alignment determines+whether the payload lines up along the axis on its left side, right+side, or in the center of the payload.++The vertical payload also has a background color, which as type+'Radiant'.  Think of the background color as extending infinitely from+both the left and right side of the vertical payload.  When the+vertical payload is combined with other vertical payloads into a 'Box'+'Vertical', this background color is used as necessary so that the+'Box' 'Vertical' forms a rectangle.++The horizontal payload is similar to the vertical payload, but the+axis is horizontal rather than vertical.  The alignment determines+whether the payload aligns the axis along the top, center, or bottom+of the payload.  A horizontal payload also contains a background+color; it extends infinitely from both the top and bottom of the+horizontal payload.++Finally, the biggest building block of Rainbox is the box.  There are+two types of boxes: a 'Box' 'Horizontal', which holds zero or more horizontal+payloads, and a 'Box' 'Vertical', which holds zero or more vertical payloads.+Each kind of box is a 'Monoid', so you can combine it using the usual+monoid functions.  So, to give a visual, a 'Box' 'Vertical' with five payloads+might look like this:++@++-- function: 'box1'++           V Vertical axis++           +----++           | v1 |+           |    |+           +----+--------++           |     v2      |+           |             |+  +--------+-------------++  |        |+  |  v3    |+  |        |+  |        |+  +--------+----------++           |    v4    |+      +----+----+-----++      | v5      |+      |         |+      +---------+++@++Each payload is marked in the middle with @v1@, @v2@, etc.  Note how+each payload has a different size.  @v1@ and @v2@, and @v4@ have a+'left' alignment, as their left edge is lined up with the vertical+axis.  @v3@ has a 'right' alignment.  @v5@ has a 'center' alignment.+Think of each payload has having a background color extending+infinitely off of its left and right sides.  These five payloads put+together make a 'Box' 'Vertical'.  Since 'Box' 'Vertical' is a monoid,+you can combine various 'Box' 'Vertical'.  Indeed, the pictured 'Box'+'Vertical' can be built only by combining smaller 'Box' 'Vertical', as+when you create payloads they are always given to you as a single+payload wrapped in a 'Box' 'Vertical'.++Now, you want to render your 'Box' 'Vertical'.  You use the 'render' function,+which makes a sequence of Rainbow 'Chunk'.  This turns your 'Box' 'Vertical'+into a nice rectangle for on-screen rendering:++@++-- function: 'renderBox1'++  +--------+----+--------++  |        | v1 |        |+  |        |    |        |+  +--------+----+--------++  |        |     v2      |+  |        |             |+  +--------+-------------++  |        |             |+  |  v3    |             |+  |        |             |+  |        |             |+  +--------+----------+--++  |        |    v4    |  |+  +---+----+----+-----+--++  |   | v5      |        |+  |   |         |        |+  +---+---------+--------+++@++The spaces to the left and right of each payload are filled in with+the appropriate background color, which is the background+color of the adjoining payload.+++What if you want to place the 'Box' 'Vertical' alongside another box?  If you+want to put it next to another 'Box' 'Vertical', just use 'mappend' or '<>'. But+what if you want to put it next to a 'Box' 'Horizontal'?  Let's suppose you have a+'Box' 'Horizontal' that looks like this:++@++-- function: 'box2'++                        +----++                        | h1 |+                        |    |+Horizontal Axis >       +----+----------++                             |          |+                             |   h2     |+                             |          |+                             +----------+++@++The @h1@ payload has alignment 'bottom', because its bottom edge is+lined up along the horizontal axis.  The @h2@ payload has alignment+'top'.  You want to connect this 'Box' 'Horizontal' with the 'Box'+'Vertical' made above.  You can't connect them directly because they+are different types.  You can, however, take a complete 'Box' and wrap+it inside another 'Box'.  This allows you to wrap a 'Box' 'Vertical'+inside of a 'Box' 'Horizontal', and vice versa, or even wrap a 'Box'+'Horizontal' inside of another 'Box' 'Horizontal'.  You do this with+the 'wrap' function, which is applied to the alignment for the new+box, its background color, and the box you want to wrap.  ao, let's+say you take the 'Box' 'Vertical' created above and wrap it inside a+'Box' 'Horizontal' with 'top' alignment and a background color, and+then you combine it with the 'Box' 'Horizontal' created above.  The+result:++@++-- function: 'box3'++                         +----++                         | h1 |+                         |    |+  +--------+----+--------+----+----------++  |        | v1 |        |    |          |+  |        |    |        |    |    h2    |+  +--------+----+--------+    |          |+  |        |     v2      |    +----------++  |        |             |+  +--------+-------------++  |        |             |+  |  v3    |             |+  |        |             |+  |        |             |+  +--------+----------+--++  |        |    v4    |  |+  +---+----+----+-----+--++  |   | v5      |        |+  |   |         |        |+  +---+---------+--------+++@++The old 'Box' 'Vertical', which is now wrapped in a 'Box'+'Horizontal', now has a background color which extends infinitely from+the top and bottom of the box.  It is now just a payload inside of the+'Box' 'Horizontal'.  The other two payloads in the 'Box' 'Horizontal',+@h1@ and @h2@, also have background colors extending from their tops+and bottoms.++So, when you render this 'Box' 'Horizontal' with 'render', you get this:+++@++-- function: 'renderBox3'++  +----------------------+----+----------++  |                      | h1 |          |+  |                      |    |          |+  +--------+----+--------+----+----------++  |        | v1 |        |    |          |+  |        |    |        |    |    h2    |+  +--------+----+--------+    |          |+  |        |     v2      |    +----------++  |        |             |    |          |+  +--------+-------------+    |          |+  |        |             |    |          |+  |  v3    |             |    |          |+  |        |             |    |          |+  |        |             |    |          |+  +--------+----------+--+    |          |+  |        |    v4    |  |    |          |+  +---+----+----+-----+--+    |          |+  |   | v5      |        |    |          |+  |   |         |        |    |          |+  +---+---------+--------+----+----------+++@++The area above the old 'Box' 'Vertical' has the background color that we used in+the application of 'convert'.  The area below the @h1@ payload has its+background color, and the area above and below the @h2@ payload has+its background color.++What if you just want to create an empty space?  You can create entire+blank boxes with 'blank', but often it is enough to use 'spacer',+which gives you a one-dimensional 'Box' 'Horizontal' or 'Box'+'Vertical'.  If you are creating a 'Box' 'Horizontal', 'spacer' gives+you a box with width, but no height; for a 'Box' 'Vertical', you get a+box with height, but no width.  So, to return to the example 'Box'+'Horizontal', let's say you want to add a blank space a few columns+wide on the right side.  You 'mappend' a 'Box' 'Horizontal' created+with 'spacer' to get this:++@++-- function: 'box4'++                        +----++                        | h1 |+                        |    |+Horizontal Axis >       +----+----------+--++                             |          |+                             |   h2     |+                             |          |+                             +----------++@++On the right side you now have a payload with width, but no height.+But it does have a background color.  So when you 'render' the box, you+get this:+++@++-- function: 'renderBox4'++                        +----+----------+--++                        | h1 |          |  |+                        |    |          |  |+                        +----+----------+  |+                        |    |          |  |+                        |    |   h2     |  |+                        |    |          |  |+                        +----+----------+--+++@++You can also use 'spreader' to make a 'Box' 'Horizontal' taller or a+'Box' 'Vertical' wider.  'spreader' creates a one-dimensional 'Box'+that is perpendicular to the axis.  Pay attention to the alignment of+the 'spreader' as this will determine how your box expands.  Let's say+I want to take the 'Box' that contains the @h1@ and @h2@ payloads as+created above, but I also want to make sure the box is at least 12+rows tall.  To do this, 'mappend' a 'spreader' that is 12 rows tall.+The result upon 'render'ing is:++@+                        +-- functions: 'box5' 'renderBox5'++                        +----+----------+--++                        |    |          |  |+                        +----+          |  |+                        | h1 |          |  |+                        |    |          |  |+                        +----+----------+  |+                        |    |          |  |+                        |    |   h2     |  |+                        |    |          |  |+                        |    +----------+  |+                        |    |          |  |+                        +----+----------+--++@++Those are the basics of the Rainbox model, which should be enough to+get you started.  Also helpful are the 'tableByRows' and+'tableByColumns' functions, which will help you build a simple grid+that resembles a spreadsheet; see its documentation for hints to get+started with that.  You will also find an example using the+'tableByRows', as well as code to produce all of the examples shown+above, in the source code below.++== Why the use of 'Seq' everywhere, rather than lists?++Rainbox uses 'Seq' from "Data.Sequence" because lists can be+infinite.  Practically every function in Rainbox will not accept+infinite inputs, because Rainbox needs to know exactly how long and+wide various payloads and boxes are in order to line them up+correctly.  Use of the 'Seq' most accurately reflects the fact that+Rainbox does not work on infinite inputs.++-}+module Rainbox.Tutorial where++import Data.Foldable (toList)+import Data.Monoid+import Data.Sequence (Seq)+import qualified Data.Sequence as Seq+import Data.Text (Text)+import qualified Data.Text as X+import Rainbow+import Rainbox++-- | Create a 'Box' for the given text.  The default foreground and+-- background colors of the terminal are used for the 'Text'; the+-- given background is used as the background color for any added+-- padding.+textBox :: Radiant -> Text -> Box a+textBox r = fromChunk center r . chunkFromText++-- | Centers the given 'Box' within a larger 'Box' that has the given+-- height and width and background color.  The larger 'Box' has the+-- given 'Alignment'.+within+  :: Orientation a+  => Alignment a+  -> Int+  -- ^ Number of rows+  -> Int+  -- ^ Number of columns+  -> Radiant+  -- ^ Background color+  -> Box a+  -> Box a+within a r c b+  = wrap a b+  . mappend (spreader center r)+  . wrap centerH b+  . mappend (spreader center c)+  . wrap centerV b++-- | Puts the given text in the center of a box.  The resulting box is+-- center aligned.+textWithin+  :: Orientation a+  => Alignment a+  -> Int+  -- ^ Number of rows+  -> Int+  -- ^ Number of columns+  -> Radiant+  -- ^ Background color for smaller box+  -> Radiant+  -- ^ Background color for larger box+  -> Text+  -> Box a+textWithin a r c bs bl = wrap a bl . within a r c bs . textBox bs++box1 :: Box Vertical+box1 = mconcat+  [ textWithin left   4 6  blue      green   "v1"+  , textWithin left   4 15 red       magenta "v2"+  , textWithin right  6 10 yellow    blue    "v3"+  , textWithin left   3 12 green     red     "v4"+  , textWithin center 4 11 magenta   blue    "v5"+  ]++renderBox1 :: IO ()+renderBox1 = mapM_ putChunk . toList . render $ box1++box2 :: Box Horizontal+box2 = mconcat+  [ textWithin bottom 4 6  magenta green "h1"+  , textWithin top    5 12 blue    yellow "h2"+  ]++renderBox2 :: IO ()+renderBox2 = mapM_ putChunk . toList . render $ box2++box3 :: Box Horizontal+box3 = mconcat+  [ wrap top yellow box1+  , box2+  ]++renderBox3 :: IO ()+renderBox3 = mapM_ putChunk . toList . render $ box3++box4 :: Box Horizontal+box4 = box2 <> spacer cyan 3++renderBox4 :: IO ()+renderBox4 = mapM_ putChunk . toList . render $ box4++box5 :: Box Horizontal+box5 = box4 <> spreader center 12++renderBox5 :: IO ()+renderBox5 = mapM_ putChunk . toList . render $ box5++-- Sample code for 'tableByRows'+--+-- Here is a simple data type representing stations in the+-- Washington DC Metrorail system.++data Line+  = Red+  | Blue+  | Orange+  | Green+  | Yellow+  | Silver+  deriving (Eq, Ord, Show, Enum)++data Station = Station+  { name :: Text+  , metroLines :: [Line]+  , address :: [Text]+  , underground :: Bool+  }++nameCell :: Radiant -> Text -> Cell+nameCell bk nm+  = Cell (Seq.singleton . Seq.singleton $ (chunkFromText nm <> back bk))+         top left bk++linesCell :: Radiant -> [Line] -> Cell+linesCell bk lns = Cell (Seq.fromList . fmap (lineRow bk) $ lns)+                        top right bk++lineRow :: Radiant -> Line -> Seq Chunk+lineRow bk li = Seq.singleton ck+  where+    ck = chunkFromText (X.pack . show $ li) <> fore clr <> back bk+    clr = case li of+      Red -> red+      Blue -> blue+      Orange -> Radiant yellow8 (Just . Color256 . Just $ 220)+      Green -> green+      Yellow -> yellow+      Silver -> Radiant white8 (Just grey)+++addressCell :: Radiant -> [Text] -> Cell+addressCell bk lns = Cell (Seq.fromList . fmap addrRow $ lns) top center bk+  where+    addrRow txt = Seq.singleton $ chunkFromText txt <> back bk++undergroundCell :: Radiant -> Bool -> Cell+undergroundCell bk bl+  = Cell (Seq.singleton . Seq.singleton $ ck) top left bk+  where+    ck = (if bl then "Yes" else "No") <> back bk++-- | Converts a 'Station' to a list of 'Cell'.++stationCells :: Radiant -> Station -> [Cell]+stationCells b st =+  [ nameCell b . name $ st+  , linesCell b . metroLines $ st+  , addressCell b . address $ st+  , undergroundCell b . underground $ st+  ]++stationTable :: Box Vertical+stationTable+  = tableByRows+  . Seq.fromList+  . zipWith stationRow (cycle [coloredBack, noColorRadiant])+  $ stations+  where+    coloredBack = Radiant noColor8 (Just . Color256 . Just $ 195)+    stationRow bk+      = intersperse (separator bk 1)+      . Seq.fromList+      . stationCells bk++renderStationTable :: IO ()+renderStationTable = mapM_ putChunk . toList . render $ stationTable++stations :: [Station]+stations =+  [ Station "Metro Center" [Red, Orange, Silver, Blue]+            ["607 13th St NW", "Washington, DC 20005"] True++  , Station "L'Enfant Plaza" [Orange, Silver, Blue, Green, Yellow]+            ["600 Maryland Ave SW", "Washington, DC 20024"] True++  , Station "Silver Spring" [Red]+            ["8400 Colesville Rd", "Silver Spring, MD 20910"] False++  , Station "Court House" [Silver, Orange]+            ["2100 Wilson Blvd", "Arlington, VA 22201"] True++  , Station "Prince George's Plaza" [Green, Yellow]+            ["3575 East-West Hwy", "Hyattsville, MD 20782"] True+  ]
− lib/Rainbox/Tutorial.lhs
@@ -1,173 +0,0 @@-Rainbox tutorial - introduction-===============================--Rainbox (that is, *Rain*bow and *box*) helps you create colorful,-nicely formatted text boxes.  It is based on the `rainbow` package,-which provides all the color support, so read the [documentation for-that](http://hackage.haskell.org/package/rainbow) before continuing.--[boxes](http://hackage.haskell.org/package/boxes) is a similar-package but without color support.--This file is written in literate Haskell, so you can compile and run-it.  It also means that the compiler checks the examples, which-keeps them accurate.  However, HsColour does not fare so well with-literate Haskell, so this file will not look good from the hyperlinked-source in Haddock.  You're better off viewing it from a text editor or-through [the Gihub website](http://www.github.com/massysett/rainbox).--A grid of boxes-===============--`Rainbox` is built on `Rainbox.Box`, which contains the building-blocks to create rectangular boxes of text.  Each box is justified-as appropriate and is filled in with a background color to make it-rectangular.--If your needs are complex, use `Rainbox.Box`.  Using it you can-patch boxes together into sort of a crazy quilt.  For simpler needs-you can use `Rainbox`, which only allows you to create grids of-boxes, like a spreadsheet.  This tutorial will show you how to use-the `Rainbox` module.  Everything you need from this package will be-available from the `Rainbox` module.  You will also need to import-packages from `rainbow`:--> {-# LANGUAGE OverloadedStrings #-}-> -- | If you are viewing this module in Haddock, note-> -- that the tutorial is contained in the source code of the-> -- module, which is written in literate Haskell.-> -- It is best viewed in your text editor or through-> -- Github at-> ---> -- <https://github.com/massysett/rainbox/blob/master/lib/Rainbox/Tutorial.lhs>--> module Rainbox.Tutorial where->-> import Data.List (intersperse)-> import Data.Monoid-> import Data.String-> import Rainbow-> import Rainbox---Making a table of name data-===========================--For this example, we'll print a table of names, addresses, and-account balances.  This type holds the data we're interested in:--> data Record = Record->   { firstName :: String->   , lastName :: String->   , address :: [String]->   , phone :: String->   , email :: String->   , balance :: String->   } deriving Show--And let's make a list of some sample data:--> records :: [Record]-> records =->   [ Record->       { firstName = "Nell"->       , lastName = "Langston"->       , address = [ "Owings Mills, MD 21117" ]->       , phone = "800-588-2300"->       , email = "NellJLangston@dayrep.com"->       , balance = "0"->       }-> ->   , Record->       { firstName = "Sharon"->       , lastName = "Sutton"->       , address = [ "37 Church Street", "Flushing, NY 11354" ]->       , phone = "312-555-8100"->       , email = "SharonJSutton@teleworm.us"->       , balance = "1033.54"->       }-> ->   , Record->       { firstName = "Barack"->       , lastName = "Obama"->       , address = [ "1600 Pennsylvania Ave NW", "Washington, DC" ]->       , phone = "877-CASH-NOW"->       , email = "president@whitehouse.gov"->       , balance = "23562.00"->       }-> ->   , Record->       { firstName = "Bert and Ernie"->       , lastName = "Sesame"->       , address = [ "123 Sesame Street", "Lower Level",->                     "Sesame, WN V6B432" ]->       , phone = "+45-123-4567"->       , email = "lower@rhyta.com"->       , balance = "100,451.05"->       }->->   , Record->       { firstName = "Vip"->       , lastName = "Vipperman"->       , address = [ "10000 Smiley Lane", "Denver, CO 80266" ]->       , phone = "303-555-1212"->       , email = "vipperman@rhyta.com"->       , balance = "301.00"->       }->   ]--Building each row of cells-==========================--`Rainbox` works with rows of `Cell`s.  You build one list of `Cell`-for each row in your grid.  Here we will make the last name bold,-and the rest of the text will be plain.  We will also alternate each-row--every even row (starting with the first row) will be the-default color, and the odd rows will be yellow.  We'll make a-function that takes a `Record` and returns another function that,-when applied to a `Chunk` that contains the color for the row,-returns a list of `Cell`.  First let's make a small function that-will make a function that returns a `Cell` with our desired-defaults:--> cell :: [Chunk] -> Radiant -> Cell-> cell cks bck = Cell brs left top bck->   where->     brs = map Bar . map ((:[]) . (<> back bck)) $ cks---> recordToCells :: Record -> Radiant -> [Cell]-> recordToCells r rad = map ($ rad) $->   [ cell . (:[]) . fromString . firstName $ r->   , cell . (:[]) $ (fromString (lastName r) <> bold)->   , cell . map fromString . address $ r->   , cell . (:[]) . fromString . phone $ r->   , cell . (:[]) . fromString . email $ r->   , cell . (:[]) . fromString . balance $ r->   ]--Zipping to get rows of cells-============================--> cellRows :: [[Cell]]-> cellRows = zipWith recordToCells records (cycle [noColorRadiant, yellow])--Adding white space between columns-==================================--If we print the table like it is now, there will be no whitespace,-as `Rainbox` does not add whitespace for you.  Fortunately this is-easy to add.  The string literal, " ", becomes a Cell due to the use-of the Overloaded Strings extension; the cell will have the default-background color.--> spacedOutCells :: [[Cell]]-> spacedOutCells = map (intersperse " ") cellRows--Printing the cells-==================--To see the result, run this function in ghci:--> printSampleBox :: IO ()-> printSampleBox = printBox . gridByRows $ spacedOutCells
rainbox.cabal view
@@ -3,12 +3,12 @@ -- http://www.github.com/massysett/cartel -- -- Script name used to generate: genCabal.hs--- Generated on: 2015-03-22 11:44:44.05559 EDT--- Cartel library version: 0.14.2.0+-- Generated on: 2015-04-14 07:08:21.967655 EDT+-- Cartel library version: 0.14.2.6  name: rainbox-version: 0.10.0.2-cabal-version: >= 1.14+version: 0.12.0.0+cabal-version: >= 1.18 license: BSD3 license-file: LICENSE build-type: Simple@@ -34,10 +34,7 @@ Library   exposed-modules:     Rainbox-    Rainbox.Array2d-    Rainbox.Box-    Rainbox.Box.Primitives-    Rainbox.Reader+    Rainbox.Core     Rainbox.Tutorial   default-language: Haskell2010   ghc-options:@@ -45,147 +42,62 @@   hs-source-dirs:     lib   build-depends:-      base >= 4.5.0.0 && < 4.8.0.0+      base >= 4.5.0.0 && < 4.9.0.0     , rainbow >= 0.22 && < 0.23     , bytestring >= 0.10 && < 0.11+    , containers >= 0.5.5 && < 0.6     , text >= 0.11.3.1 && < 1.3.0.0-    , transformers >= 0.3.0.0 && < 0.5.0.0-    , array >= 0.4.0.0 && < 0.6.0.0 -Test-Suite rainbox-visual-  type: exitcode-stdio-1.0+source-repository head+  type: git+  location: https://github.com/massysett/rainbox.git++Test-Suite rainbox-properties+  main-is: rainbox-properties.hs+  build-depends:+      base >= 4.5.0.0 && < 4.9.0.0+    , rainbow >= 0.22 && < 0.23+    , bytestring >= 0.10 && < 0.11+    , containers >= 0.5.5 && < 0.6+    , text >= 0.11.3.1 && < 1.3.0.0+    , tasty >= 0.10.1 && < 0.11+    , tasty-quickcheck >= 0.8.1 && < 0.9+    , QuickCheck >= 2.7 && < 2.9   ghc-options:     -Wall+  default-language: Haskell2010+  hs-source-dirs:+    lib+    test   other-modules:     Rainbox-    Rainbox.Array2d-    Rainbox.Box-    Rainbox.Box.Primitives-    Rainbox.Reader+    Rainbox.Core     Rainbox.Tutorial     Rainbow.Instances-    Rainbox.Array2dTests-    Rainbox.Box.Instances-    Rainbox.Box.PrimitivesTests-    Rainbox.BoxDir-    Rainbox.BoxTests     Rainbox.Instances-    Rainbox.ReaderTests-    RainboxDir-    RainboxTests-    Visual+  type: exitcode-stdio-1.0++Test-Suite rainbox-visual   main-is: rainbox-visual.hs-  hs-source-dirs:-    test-    lib-  default-language: Haskell2010   build-depends:-      base >= 4.5.0.0 && < 4.8.0.0+      base >= 4.5.0.0 && < 4.9.0.0     , rainbow >= 0.22 && < 0.23     , bytestring >= 0.10 && < 0.11+    , containers >= 0.5.5 && < 0.6     , text >= 0.11.3.1 && < 1.3.0.0-    , transformers >= 0.3.0.0 && < 0.5.0.0-    , array >= 0.4.0.0 && < 0.6.0.0     , tasty >= 0.10.1 && < 0.11     , tasty-quickcheck >= 0.8.1 && < 0.9     , QuickCheck >= 2.7 && < 2.9-    , ChasingBottoms >= 1.3.0 && < 1.4--Executable rainbox-mosaic-  main-is: rainbox-mosaic.hs-  if flag(mosaic)-    ghc-options:-      -Wall-    other-modules:-      Rainbox-      Rainbox.Array2d-      Rainbox.Box-      Rainbox.Box.Primitives-      Rainbox.Reader-      Rainbox.Tutorial-      Rainbow.Instances-      Rainbox.Array2dTests-      Rainbox.Box.Instances-      Rainbox.Box.PrimitivesTests-      Rainbox.BoxDir-      Rainbox.BoxTests-      Rainbox.Instances-      Rainbox.ReaderTests-      RainboxDir-      RainboxTests-      Visual-    hs-source-dirs:-      test-      lib-    default-language: Haskell2010-    build-depends:-        base >= 4.5.0.0 && < 4.8.0.0-      , rainbow >= 0.22 && < 0.23-      , bytestring >= 0.10 && < 0.11-      , text >= 0.11.3.1 && < 1.3.0.0-      , transformers >= 0.3.0.0 && < 0.5.0.0-      , array >= 0.4.0.0 && < 0.6.0.0-      , tasty >= 0.10.1 && < 0.11-      , tasty-quickcheck >= 0.8.1 && < 0.9-      , QuickCheck >= 2.7 && < 2.9-      , ChasingBottoms >= 1.3.0 && < 1.4-  else-    buildable: False--Test-Suite rainbox-test   ghc-options:     -Wall-  type: exitcode-stdio-1.0+  default-language: Haskell2010   hs-source-dirs:-    test     lib-  default-language: Haskell2010-  build-depends:-      base >= 4.5.0.0 && < 4.8.0.0-    , rainbow >= 0.22 && < 0.23-    , bytestring >= 0.10 && < 0.11-    , text >= 0.11.3.1 && < 1.3.0.0-    , transformers >= 0.3.0.0 && < 0.5.0.0-    , array >= 0.4.0.0 && < 0.6.0.0-    , tasty >= 0.10.1 && < 0.11-    , tasty-quickcheck >= 0.8.1 && < 0.9-    , QuickCheck >= 2.7 && < 2.9-    , ChasingBottoms >= 1.3.0 && < 1.4-  main-is: rainbox-test.hs--source-repository head-  type: git-  location: https://github.com/massysett/rainbox.git--Executable rainbox-grid-  main-is: rainbox-grid.hs-  if flag(grid)-    ghc-options:-      -Wall-    hs-source-dirs:-      test-      lib-    default-language: Haskell2010-    build-depends:-        base >= 4.5.0.0 && < 4.8.0.0-      , rainbow >= 0.22 && < 0.23-      , bytestring >= 0.10 && < 0.11-      , text >= 0.11.3.1 && < 1.3.0.0-      , transformers >= 0.3.0.0 && < 0.5.0.0-      , array >= 0.4.0.0 && < 0.6.0.0-      , tasty >= 0.10.1 && < 0.11-      , tasty-quickcheck >= 0.8.1 && < 0.9-      , QuickCheck >= 2.7 && < 2.9-      , ChasingBottoms >= 1.3.0 && < 1.4-  else-    buildable: False--Flag grid-  description: Build the rainbox-grid executable-  default: False-  manual: True--Flag mosaic-  description: Build the rainbox-mosaic executable-  default: False-  manual: True+    test+  other-modules:+    Rainbox+    Rainbox.Core+    Rainbox.Tutorial+    Rainbow.Instances+    Rainbox.Instances+  type: exitcode-stdio-1.0
− test/Rainbox/Array2dTests.hs
@@ -1,402 +0,0 @@-module Rainbox.Array2dTests where--import Test.Tasty-import Test.QuickCheck-import Test.Tasty.QuickCheck (testProperty)-import Data.Array-import Rainbox.Array2d-import Test.ChasingBottoms---- | Generates a two-dimensional array of Int.  The size of the--- array depends on the size parameter.-genArray :: Gen (Array (Int, Int) Int)-genArray = do-  bnds <- genBounds-  let nElems = rangeSize bnds-  es <- vectorOf nElems arbitraryBoundedIntegral-  return $ listArray bnds es---- | Generates array bounds.  The size of the bounds depends on the--- size parameter.-genBounds :: Gen ((Int, Int), (Int, Int))-genBounds = do-  w:x:y:z:[] <- vectorOf 4 arbitrarySizedIntegral-  let (minC, maxC) | w < x = (w, x)-                   | otherwise = (x, w)-      (minR, maxR) | y < z = (y, z)-                   | otherwise = (z, y)-  return ((minC, minR), (maxC, maxR))--genTable :: Gen (Table (Int, [(Int, Int)]) (Int, [(Int, Int)]) Int Int Int)-genTable = do-  ay <- genArray-  return $ table (,) (,) ay--type LabelF-  = (Int, [(Int, Int)])-  -> (Int, [(Int, Int)])-  -> Int -> Int -> Int -> Int--type ChangeLabelF-  = (Int, [(Int, Int)])-  -> Int-  -> [((Int, [(Int, Int)]), Int, Int)]-  -> Int--genLabelF :: Gen LabelF-genLabelF = arbitrary--genChangeLabelF :: Gen ChangeLabelF-genChangeLabelF = arbitrary---- # Properties---- | Bounds of columns in a Table matches those of the cells-propTableColsBounds-  :: (Ix col, Ix row)-  => Table lCol lRow col row a-  -> Bool-propTableColsBounds tbl = bounds cls == tgtBounds-  where-    cls = lCols tbl-    ((minC, _), (maxC, _)) = bounds . cells $ tbl-    tgtBounds = (minC, maxC)---- | Bounds of rows in a Table matches those of the cells-propTableRowsBounds-  :: (Ix col, Ix row)-  => Table lCol lRow col row a-  -> Bool-propTableRowsBounds tbl = bounds rws == tgtBounds-  where-    rws = lRows tbl-    ((_, minR), (_, maxR)) = bounds . cells $ tbl-    tgtBounds = (minR, maxR)---- | Generating a table using the contents of the rows as labels--- allows reconstruction of the original array--propGenRebuildByRow-  :: (Ix col, Ix row, Eq a)-  => Array (col, row) a-  -> Bool-propGenRebuildByRow ay = ay == ay'-  where-    ay' = array (bounds ay) . concat . elems . lRows-      . table (\_ _ -> ()) fRow $ ay-    fRow rw ls = map g ls-      where-        g (col, a) = ((col, rw), a)---- | Generating a table using the contents of the columns as labels--- allows reconstruction of the original array--propGenRebuildByCol-  :: (Ix col, Ix row, Eq a)-  => Array (col, row) a-  -> Bool-propGenRebuildByCol ay = ay == ay'-  where-    ay' = array (bounds ay) . concat . elems . lCols-      . table fCol (\_ _ -> ()) $ ay-    fCol cl ls = map g ls-      where-        g (rw, a) = ((cl, rw), a)---- | Round-tripping through rows and arrayByRows-propRoundTripRows-  :: Eq a-  => Array (Int, Int) a-  -> Bool-propRoundTripRows ay = sameShape ay ay'-  where-    ay' = arrayByRows undefined . rows $ ay---- | Round-tripping through columns and arrayByCols-propRoundTripCols-  :: (Eq a, Show a)-  => Array (Int, Int) a-  -> Bool-propRoundTripCols ay = sameShape ay ay'-  where-    ay' = arrayByCols undefined . cols $ ay---- | True if both arrays have the same shape; that is, the same--- number of rows and the same number of columns and the same--- elements.--sameShape-  :: (Ix col, Ix row, Eq a)-  => Array (col, row) a-  -> Array (col, row) a-  -> Bool-sameShape x y = rx == ry && cx == cy && ex == ey-  where-    ((minCx, minRx), (maxCx, maxRx)) = bounds x-    ((minCy, minRy), (maxCy, maxRy)) = bounds y-    rx = rangeSize (minRx, maxRx)-    ry = rangeSize (minRy, maxRy)-    cx = rangeSize (minCx, maxCx)-    cy = rangeSize (minCy, maxCy)-    ex = elems x-    ey = elems y---- # mapTable properties---- | mapTable does not change lCols-mapTableNoChangeCols-  :: (Ix col, Ix row, Eq lCol)-  => (lCol -> lRow -> col -> row -> a -> b)-  -> Table lCol lRow col row a-  -> Bool-mapTableNoChangeCols f t = lCols t == lCols t'-  where-    t' = mapTable f t---- | mapTable does not change lRows-mapTableNoChangeRows-  :: (Ix col, Ix row, Eq lRow)-  => (lCol -> lRow -> col -> row -> a -> b)-  -> Table lCol lRow col row a-  -> Bool-mapTableNoChangeRows f t = lRows t == lRows t'-  where-    t' = mapTable f t---- | mapTable allows rebuild of original array-mapTableRebuildNoIndices-  :: (Ix col, Ix row, Eq a)-  => Table lCol lRow col row a-  -> Bool-mapTableRebuildNoIndices tbl = cells tbl == ay'-  where-    ay' = listArray (bounds . cells $ tbl) . elems . cells-        . mapTable f $ tbl-    f _ _ _ _ a = a--mapTableRebuildWithIndices-  :: (Ix col, Ix row, Eq a)-  => Table lCol lRow col row a-  -> Bool-mapTableRebuildWithIndices tbl = cells tbl == ay'-  where-    ay' = array (bounds . cells $ tbl) . elems . cells-      . mapTable f $ tbl-    f _ _ cl rw a = ((cl, rw), a)---- # labelRows and labelCols properties---- | labelCols allows rebuild of original array-propLabelColsRebuild-  :: (Ix col, Ix row, Eq a)-  => Array (col, row) a-  -> Bool-propLabelColsRebuild ay = ay == ay'-  where-    ay' = array (bounds ay) . concat . elems-      . labelCols f $ ay-    f cl ls = map g ls-      where-        g (rw, a) = ((cl, rw), a)---- | labelRows allows rebuild of original array-propLabelRowsRebuild-  :: (Ix col, Ix row, Eq a)-  => Array (col, row) a-  -> Bool-propLabelRowsRebuild ay = ay == ay'-  where-    ay' = array (bounds ay) . concat . elems-      . labelRows f $ ay-    f rw ls = map g ls-      where-        g (cl, a) = ((cl, rw), a)---- # mapRowLabels properties---- | mapRowLabels does not change column labels-propMapRowLabelsCols-  :: (Ix col, Ix row, Eq lCol)-  => (lRow -> row -> [(lCol, col, a)] -> lRow')-  -> Table lCol lRow col row a-  -> Bool-propMapRowLabelsCols f tb = lbls == lbls'-  where-    lbls = lCols tb-    tb' = mapRowLabels f tb-    lbls' = lCols tb'---- | mapRowLabels does not change cells-propMapRowLabelsCells-  :: (Ix col, Ix row, Eq a, Eq lCol)-  => (lRow -> row -> [(lCol, col, a)] -> lRow')-  -> Table lCol lRow col row a-  -> Bool-propMapRowLabelsCells f tb = ay == ay'-  where-    ay = cells tb-    tb' = mapRowLabels f tb-    ay' = cells tb'---- | mapRowLabels permits reconstruction of original array-propMapRowLabelsRebuild-  :: (Ix col, Ix row, Eq a)-  => Table lCol lRow col row a-  -> Bool-propMapRowLabelsRebuild t = ay == ay'-  where-    ay = cells t-    ay' = array (bounds ay) . concat . elems-      . lRows . mapRowLabels f $ t-    f _ rw ls = map g ls-      where-        g (_, cl, a) = ((cl, rw), a)---- | mapRowLabels gives the original row labels-propMapRowLabelsRelabel-  :: (Ix col, Ix row, Eq a, Eq lRow, Eq lCol)-  => Table lCol lRow col row a-  -> Bool-propMapRowLabelsRelabel t = t == t'-  where-    t' = mapRowLabels (\r _ _ -> r) t---- # mapColLabels properties---- | mapColLabels does not change row labels-propMapColLabelsCols-  :: (Ix col, Ix row, Eq lRow)-  => (lCol -> col -> [(lRow, row, a)] -> lCol')-  -> Table lCol lRow col row a-  -> Bool-propMapColLabelsCols f tb = lbls == lbls'-  where-    lbls = lRows tb-    tb' = mapColLabels f tb-    lbls' = lRows tb'---- | mapColLabels does not change cells-propMapColLabelsCells-  :: (Ix col, Ix row, Eq a, Eq lCol)-  => (lCol -> col -> [(lRow, row, a)] -> lCol')-  -> Table lCol lRow col row a-  -> Bool-propMapColLabelsCells f tb = ay == ay'-  where-    ay = cells tb-    tb' = mapColLabels f tb-    ay' = cells tb'---- | mapColLabels permits reconstruction of original array-propMapColLabelsRebuild-  :: (Ix col, Ix row, Eq a)-  => Table lCol lRow col row a-  -> Bool-propMapColLabelsRebuild t = ay == ay'-  where-    ay = cells t-    ay' = array (bounds ay) . concat . elems-      . lCols . mapColLabels f $ t-    f _ cl ls = map g ls-      where-        g (_, rw, a) = ((cl, rw), a)---- | mapColLabels gives the original row labels-propMapColLabelsRelabel-  :: (Ix col, Ix row, Eq a, Eq lRow, Eq lCol)-  => Table lCol lRow col row a-  -> Bool-propMapColLabelsRelabel t = t == t'-  where-    t' = mapColLabels (\r _ _ -> r) t--arrayHasNoBottoms :: Ix i => Array i e -> Bool-arrayHasNoBottoms = all (not . isBottom) . elems--tests :: TestTree-tests = testGroup "Array2d"-  [ testProperty "bounds of columns in Table matches those of cells" $-    forAll genTable $-    propTableColsBounds--  , testProperty "bounds of rows in Table matches those of cells" $-    forAll genTable $-    propTableRowsBounds--  , testProperty "propGenRebuildByRow" $-    forAll genArray propGenRebuildByRow--  , testProperty "propGenRebuildByCol" $-    forAll genArray propGenRebuildByCol--  , testProperty "propRoundTripRows" $-    forAll genArray propRoundTripRows--  , testProperty "propRoundTripCols" $-    forAll genArray propRoundTripCols--  , testProperty "arrayHasNoBottoms fails on arrays with a bottom" $-    expectFailure $ arrayHasNoBottoms (listArray (0 :: Int, 0) [])--  , testProperty "arrayByRows returns arrays with no bottoms" $-    \ls -> arrayHasNoBottoms (arrayByRows () ls)--  , testProperty "arrayByCols returns arrays with no bottoms" $-    \ls -> arrayHasNoBottoms (arrayByCols () ls)--  , testProperty "mapTableNoChangeCols" $-    forAll (fmap Blind genLabelF) $ \(Blind f) ->-    forAll genTable $ \t ->-    mapTableNoChangeCols f t--  , testProperty "mapTableNoChangeRows" $-    forAll (fmap Blind genLabelF) $ \(Blind f) ->-    forAll genTable $ \t ->-    mapTableNoChangeRows f t--  , testProperty "mapTableRebuildNoIndices" $-    forAll genTable mapTableRebuildNoIndices--  , testProperty "mapTableRebuildWithIndices" $-    forAll genTable mapTableRebuildWithIndices--  , testProperty "propLabelColsRebuild" $-    forAll genArray propLabelColsRebuild--  , testProperty "propLabelRowsRebuild" $-    forAll genArray propLabelRowsRebuild--  , testProperty "propMapRowLabelsCols" $-    forAll (fmap Blind genChangeLabelF) $ \(Blind f) ->-    forAll genTable $ \t ->-    propMapRowLabelsCols f t--  , testProperty "propMapRowLabelsCells" $-    forAll (fmap Blind genChangeLabelF) $ \(Blind f) ->-    forAll genTable $ \t ->-    propMapRowLabelsCells f t--  , testProperty "propMapRowLabelsRebuild" $-    forAll genTable propMapRowLabelsRebuild--  , testProperty "propMapRowLabelsRelabel" $-    forAll genTable propMapRowLabelsRelabel--  , testProperty "propMapColLabelsCols" $-    forAll (fmap Blind genChangeLabelF) $ \(Blind f) ->-    forAll genTable $ \t ->-    propMapColLabelsCols f t--  , testProperty "propMapColLabelsCells" $-    forAll (fmap Blind genChangeLabelF) $ \(Blind f) ->-    forAll genTable $ \t ->-    propMapColLabelsCells f t--  , testProperty "propMapColLabelsRebuild" $-    forAll genTable propMapColLabelsRebuild--  , testProperty "propMapColLabelsRelabel" $-    forAll genTable propMapColLabelsRelabel--  ]-
− test/Rainbox/Box/Instances.hs
@@ -1,5 +0,0 @@-module Rainbox.Box.Instances where--import Rainbow.Instances ()-import Test.QuickCheck-
− test/Rainbox/Box/PrimitivesTests.hs
@@ -1,218 +0,0 @@-module Rainbox.Box.PrimitivesTests where--import Control.Monad-import Control.Applicative-import Test.Tasty-import Test.Tasty.QuickCheck (testProperty)-import Test.QuickCheck-import Rainbow-import Rainbow.Instances ()-import Rainbow.Types-import qualified Data.Text as X-import Rainbox.Box.Primitives--genText :: Gen X.Text-genText = fmap X.pack $ listOf c-  where-    c = elements ['0'..'Z']--genChunk :: Gen Chunk-genChunk = arbitrary--genHeight :: Gen Height-genHeight = fmap Height $ frequency [(3, nonNeg), (1, neg)]-  where-    nonNeg = fmap abs arbitrarySizedIntegral-    neg = fmap (negate . abs) arbitrarySizedIntegral--genWidth :: Gen Width-genWidth = fmap Width $ frequency [(3, nonNeg), (1, neg)]-  where-    nonNeg = fmap abs arbitrarySizedIntegral-    neg = fmap (negate . abs) arbitrarySizedIntegral---- | Generates blank Box.-genBlankBox :: Gen Box-genBlankBox = liftM3 blank arbitrary rw cl-  where-    rw = fmap (Height . abs) arbitrarySizedIntegral-    cl = fmap (Width . abs) arbitrarySizedIntegral---- | Generates a box using chunks.-genChunkBox :: Gen Box-genChunkBox = fmap chunks $ listOf genChunk---- | Generates a box using catH.-genCatHBox :: Gen Box-genCatHBox = sized $ \s -> do-  bk <- arbitrary-  av <- genAlignVert-  bs <- listOf (resize (s `div` 2) genBox)-  return $ catH bk av bs---- | Generates a box using catV.-genCatVBox :: Gen Box-genCatVBox = sized $ \s -> do-  bk <- arbitrary-  ah <- genAlignHoriz-  bs <- listOf (resize (s `div` 2) genBox)-  return $ catV bk ah bs---- | Generates a random box.-genBox :: Gen Box-genBox = oneof [ genBlankBox, genCatHBox, genCatVBox, genChunkBox ]--genChunkLen :: Radiant -> Int -> Gen Chunk-genChunkLen bk l = do-  txt <- fmap X.pack $ vectorOf l (elements ['0'..'Z'])-  return $ (chunkFromText txt) <> back bk---- | Generates a box of text; its horizontal and vertical size--- depends on the size parameter.-genTextBox :: Gen Box-genTextBox = do-  w <- fmap abs arbitrarySizedIntegral-  h <- fmap abs arbitrarySizedIntegral-  bk <- arbitrary-  cks <- vectorOf h (genChunkLen bk w)-  let bxs = map (chunks . (:[])) cks-  bk' <- arbitrary-  return $ catV bk' left bxs------ # Alignment--genAlignVert :: Gen (Align Vert)-genAlignVert = elements-  [ center, top, bottom ]--genAlignHoriz :: Gen (Align Horiz)-genAlignHoriz = elements [ center, left, right ]--validBox :: Box -> Bool-validBox box = case unBox box of-  NoHeight i -> i > -1-  WithHeight rw -> case rw of-    [] -> False-    x:xs -> all (== width x) . map width $ xs--biggest :: Int -> Gen a -> Gen a-biggest m g = sized $ \s -> resize (min s m) g--data Inputs = Inputs-  { iChunks :: [Chunk]-  , iBackground :: Radiant-  , iHeight :: Height-  , iWidth :: Width-  , iVert :: Align Vert-  , iHoriz :: Align Horiz-  , iBoxes :: [Box]-  , iBox :: Box-  , iChunk :: Chunk-  } deriving Show--instance Arbitrary Inputs where-  arbitrary = Inputs-    <$> listOf genChunk-    <*> arbitrary-    <*> genHeight-    <*> genWidth-    <*> genAlignVert-    <*> genAlignHoriz-    <*> listOf genBlankBox-    <*> genBlankBox-    <*> genChunk--tests :: TestTree-tests = testGroup "BoxTests"-  [ testGroup "blank"-    [ testProperty "makes valid Box" $ \i ->-      validBox $ blank (iBackground i) (iHeight i)-        (iWidth i)--    , testProperty "has right number of rows" $ \i ->-      let ht = unHeight . iHeight $ i-      in (== max 0 ht) . height $ blank (iBackground i)-            (iHeight i) (iWidth i)--    , testProperty "has right number of columns" $ \i ->-      let wt = unWidth . iWidth $ i-      in (== max 0 wt) . width $ blank (iBackground i)-            (iHeight i) (iWidth i)-    ]--  , testGroup "chunks"-    [ testProperty "makes valid Box" $-      validBox . chunks . iChunks--    , testProperty "makes Box whose height is 1" $-      (== 1) . height . chunks . iChunks--    , testProperty "makes Box with cols == number of characters" $ \i ->-      let cks = iChunks i-          nChars = sum . map X.length . concat . map chunkTexts $ cks-      in (== nChars) . width $ chunks cks-    ]--  , testGroup "catH"-    [ testProperty "makes valid Box" $ \i ->-      validBox $ catH (iBackground i) (iVert i) (iBoxes i)--    , testProperty "is as tall as tallest box" $ \i ->-      let h = maximum . (0 :) . map height $ bs-          bs = iBoxes i-      in (== h) . height $ catH (iBackground i) (iVert i) bs--    , testProperty "is as wide as sum of all widths" $ \i ->-      let s = sum . map width $ bs-          bs = iBoxes i-      in (== s) . width $ catH (iBackground i) (iVert i) bs-    ]--  , testGroup "catV"-    [ testProperty "makes a valid Box" $ \i ->-      validBox $ catV (iBackground i) (iHoriz i) (iBoxes i)--    , testProperty "is as tall as the sum of all heights" $ \i ->-      let h = sum . map height $ bs-          bs = iBoxes i-      in (== h) . height $ catV (iBackground i) (iHoriz i) bs--    , testProperty "is as wide as the widest box" $ \i ->-      let w = maximum . (0:) . map width $ bs-          bs = iBoxes i-      in (== w) . width $ catV (iBackground i) (iHoriz i) bs-    ]--  , testGroup "viewH"-    [ testProperty "makes a valid Box" $ \i ->-      validBox $ viewH (unWidth . iWidth $ i) (iHoriz i) (iBox i)--    , testProperty "number of rows does not change" $ \i ->-      let b = iBox i-      in (== height b) . height $ viewH (unWidth . iWidth $ i)-                                    (iHoriz i) b--    , testProperty "number of columns <= number requested" $ \i ->-      let c = unWidth . iWidth $ i-          tgt = max 0 c-      in (<= tgt) . width $ viewH c (iHoriz i) (iBox i)-    ]--  , testGroup "viewV"-    [ testProperty "makes a valid Box" $ \i ->-      validBox $ viewV (unHeight . iHeight $ i) (iVert i) (iBox i)--    , testProperty "width does not change" $ \i ->-      let b = iBox i-      in (== width b) . width $ viewV (unHeight . iHeight $ i)-                                    (iVert i) b--    , testProperty "number of rows <= number requested" $ \i ->-      let r = unHeight . iHeight $ i-          tgt = max 0 r-      in (<= tgt) . height $ viewV r (iVert i) (iBox i)-    ]-  ]-
− test/Rainbox/BoxDir.hs
@@ -1,7 +0,0 @@-module Rainbox.BoxDir where--import qualified Rainbox.Box.PrimitivesTests-import Test.Tasty--tests :: TestTree-tests = testGroup "Box" [ Rainbox.Box.PrimitivesTests.tests ]
− test/Rainbox/BoxTests.hs
@@ -1,207 +0,0 @@-module Rainbox.BoxTests where--import Rainbox.Box-import Rainbox.Box.PrimitivesTests-import qualified Data.Text as X-import Test.Tasty.QuickCheck (testProperty)-import Test.Tasty-import Rainbow.Types-import Test.QuickCheck hiding (resize)--tests :: TestTree-tests = testGroup "RainboxTests"-  [ testGroup "blankH"-    [ testProperty "makes Box with no height" $ \i ->-      (== 0) . height $ blankH (iBackground i) (unWidth . iWidth $ i)--    , testProperty "makes Box with correct width" $ \i ->-      let w = unWidth . iWidth $ i-          tgt = max 0 w-      in (== tgt) . width $ blankH (iBackground i) w-    ]--  , testGroup "blankV"-    [ testProperty "makes Box with no width" $ \i ->-      (== 0) . width $ blankV (iBackground i) (unHeight . iHeight $ i)--    , testProperty "makes Box with correct height" $ \i ->-      let h = unHeight . iHeight $ i-          tgt = max 0 h-      in (== tgt) . height $ blankV (iBackground i) h-    ]--  , testGroup "chunk"-    [ testProperty "makes Box one high" $-      (== 1) . height . chunk . iChunk--    , testProperty "makes Box as wide as characters in chunk" $ \i ->-      let cs = sum . map X.length . chunkTexts . iChunk $ i-      in (== cs) . width . chunk . iChunk $ i-    ]--  , testGroup "growH"-    [ testProperty "does not change height" $ \i ->-      let bx = iBox i-      in (== height bx) . height $ growH (iBackground i)-            (unWidth . iWidth $ i) (iHoriz i) bx--    , testProperty "new Box is of correct width" $ \i ->-      let bx = iBox i-          tgt = max wdth (width bx)-          wdth = unWidth . iWidth $ i-      in (== tgt) . width $-            growH (iBackground i) wdth (iHoriz i) bx--    , testProperty "new Box is at least as wide as old Box" $ \i ->-      let bx = iBox i-      in (>= width bx) . width $ growH (iBackground i)-            (unWidth . iWidth $ i) (iHoriz i) bx-    ]--  , testGroup "growV"-    [ testProperty "does not change width" $ \i ->-      let bx = iBox i-      in (== width bx) . width $ growV (iBackground i)-            (unWidth . iWidth $ i) (iVert i) bx--    , testProperty "new Box is of correct height" $ \i ->-      let bx = iBox i-          tgt = max (height bx) hght-          hght = unHeight . iHeight $ i-      in (== tgt) . height $-            growV (iBackground i) hght (iVert i) bx--    , testProperty "new Box is at least as tall as old Box" $ \i ->-      let bx = iBox i-      in (>= height bx) . height $ growV (iBackground i)-            (unWidth . iWidth $ i) (iVert i) bx-    ]--  , testGroup "grow"-    [  testProperty "new Box is of correct width" $ \i ->-      let bx = iBox i-          tgt = unWidth . iWidth $ i-      in (\w -> w == width bx || w == tgt) . width $-            growH (iBackground i) tgt (iHoriz i) bx--    , testProperty "new Box is at least as wide as old Box" $ \i ->-      let bx = iBox i-      in (>= width bx) . width $ grow (iBackground i) (iHeight i)-            (iWidth i) (iVert i) (iHoriz i) (iBox i)--    , testProperty "new Box is of correct height" $ \i ->-      let bx = iBox i-          tgt = unHeight . iHeight $ i-      in (\h -> h == height bx || h == tgt) . height $-            grow (iBackground i) (iHeight i) (iWidth i)-                 (iVert i) (iHoriz i) (iBox i)--    , testProperty "new Box is at least as tall as old Box" $ \i ->-      let bx = iBox i-      in (>= height bx) . height $ grow (iBackground i)-            (iHeight i) (iWidth i) (iVert i) (iHoriz i) (iBox i)-    ]--  , testGroup "column"-    [ testProperty "number of inputs == number of outputs" $ \i ->-      let bs = iBoxes i-      in (== length bs) . length $ column (iBackground i) (iHoriz i) bs--    , testProperty "width of outputs is identical" $ \i ->-      case column (iBackground i) (iHoriz i) (iBoxes i) of-        [] -> True-        x:xs -> all (== width x) . map width $ xs--    , testProperty "width of output is as wide as widest input" $ \i ->-      let r = column (iBackground i) (iHoriz i) (iBoxes i)-      in case iBoxes i of-        [] -> null r-        xs -> width (head r) == (maximum . map width $ xs)-    ]--  , testGroup "resizeH"-    [ testProperty "height of resulting Box unchanged" $ \i ->-      let bx = iBox i-      in (== height bx) . height $ resizeH (iBackground i)-            (unWidth . iWidth $ i) (iHoriz i) bx--    , testProperty "result has desired width" $ \i ->-      let tgt = max 0 . unWidth . iWidth $ i-      in (== tgt) . width $ resizeH (iBackground i)-            (unWidth . iWidth $ i) (iHoriz i) (iBox i)-    ]--  , testGroup "resizeV"-    [ testProperty "width of resulting Box unchanged" $ \i ->-      let bx = iBox i-      in (== width bx) . width $ resizeV (iBackground i)-            (unHeight . iHeight $ i) (iVert i) bx--    , testProperty "result has desired height" $ \i ->-      let tgt = max 0 . unHeight . iHeight $ i-      in (== tgt) . height $ resizeV (iBackground i)-            (unHeight . iHeight $ i) (iVert i) (iBox i)-    ]--  , testGroup "resize"-    [ testProperty "result has desired height" $ \i ->-      let tgt = max 0 . unHeight . iHeight $ i-      in (== tgt) . height $ resize (iBackground i)-            (iHeight i) (iWidth i)-            (iVert i) (iHoriz i) (iBox i)--    , testProperty "result has desired width" $ \i ->-      let tgt = max 0 . unWidth . iWidth $ i-      in (== tgt) . width $ resize (iBackground i)-            (iHeight i) (iWidth i) (iVert i) (iHoriz i)-            (iBox i)-    ]--  , testGroup "punctuateH"-    [ testProperty "result has desired width" $ \i ->-      let tgt = (sum . map width $ bs)-            + width bx * (max 0 $ len - 1)-          len = length bs-          bs = iBoxes i-          bx = iBox i-      in (== tgt) . width $ punctuateH (iBackground i)-          (iVert i) bx bs-    ]--  , testGroup "punctuateV"-    [ testProperty "result has desired height" $ \i ->-      let tgt = (sum . map height $ bs)-            + height bx * (max 0 $ len - 1)-          len = length bs-          bs = iBoxes i-          bx = iBox i-      in (== tgt) . height $ punctuateV (iBackground i)-          (iHoriz i) bx bs-    ]--  -- Have to cap size on this one, which is not satisfying.  There-  -- are no apparent bugs.  Apparently what is taking so long is the-  -- Text.replicate in Box.blanks, which is applied from-  -- Box.padHoriz.-  , testGroup "sepH"-    [ testProperty "result has correct width" $-      forAll arbitrarySizedIntegral $ \len ->-      forAll arbitrary $ \i ->-      let tgt = (sum . map width $ bs)-            + max 0 len * max 0 (length bs - 1)-          bs = iBoxes i-      in (== tgt) . width $ sepH (iBackground i) len-          (iVert i) (iBoxes i)-    ]--  , testGroup "sepV"-    [ testProperty "result has correct height" $-      forAll arbitrarySizedIntegral $ \len ->-      forAll arbitrary $ \i ->-      let tgt = (sum . map height $ bs)-            + max 0 len * max 0 (length bs - 1)-          bs = iBoxes i-      in (== tgt) . height $ sepV (iBackground i) len-          (iHoriz i) (iBoxes i)-    ]-  ]
test/Rainbox/Instances.hs view
@@ -1,33 +1,64 @@-{-# LANGUAGE FlexibleInstances #-} {-# OPTIONS_GHC -fno-warn-orphans #-}---- | QuickCheck instances for all Rainbox modules. module Rainbox.Instances where  import Control.Monad import Test.QuickCheck+import Rainbox.Core import Rainbow.Instances ()-import Rainbox-import Rainbox.Box+import Data.Sequence (Seq)+import qualified Data.Sequence as Seq +instance Arbitrary a => Arbitrary (Alignment a) where+  arbitrary = oneof [ return Center, fmap NonCenter arbitrary ]++instance Arbitrary Horizontal where+  arbitrary = elements [ ATop, ABottom ]++instance Arbitrary Vertical where+  arbitrary = elements [ ALeft, ARight ]+ instance Arbitrary Height where-  arbitrary = fmap Height arbitrary+  arbitrary = fmap Height $ frequency+    [ (3, fmap getNonNegative arbitrary)+    , (1, arbitrary)+    ]  instance Arbitrary Width where-  arbitrary = fmap Width arbitrary+  arbitrary = fmap Width $ frequency+    [ (3, fmap getNonNegative arbitrary)+    , (1, arbitrary)+    ] -instance Arbitrary (Align Vert) where-  arbitrary = elements [center, top, bottom]+instance Arbitrary Core where+  arbitrary = fmap Core arbitrary -instance Arbitrary (Align Horiz) where-  arbitrary = elements [center, left, right]+instance Arbitrary Rod where+  arbitrary = fmap Rod arbitrary -instance Arbitrary Bar where-  arbitrary = fmap Bar arbitrary+instance Arbitrary a => Arbitrary (Seq a) where+  arbitrary = fmap Seq.fromList arbitrary --- | Creates a non-nested Box.-instance Arbitrary Box where-  arbitrary = liftM3 barsToBox arbitrary arbitrary arbitrary+newtype NonEmptySeq a = NonEmptySeq { getNonEmptySeq :: Seq a }+  deriving Show++instance Arbitrary a => Arbitrary (NonEmptySeq a) where+  arbitrary = do+    NonEmpty xs <- arbitrary+    return . NonEmptySeq . Seq.fromList $ xs++instance Arbitrary RodRows where+  arbitrary = sized $ \s -> resize (s `div` 10) $ oneof+    [ fmap (RodRowsWithHeight . getNonEmptySeq) arbitrary+    , frequency [ (1, fmap RodRowsNoHeight arbitrary)+                , (3, fmap (RodRowsNoHeight . getNonNegative) arbitrary)+                ]+    ]++instance Arbitrary a => Arbitrary (Payload a) where+  arbitrary = liftM3 Payload arbitrary arbitrary arbitrary++instance Arbitrary a => Arbitrary (Box a) where+  arbitrary = fmap Box arbitrary  instance Arbitrary Cell where   arbitrary = liftM4 Cell arbitrary arbitrary arbitrary arbitrary
− test/Rainbox/ReaderTests.hs
@@ -1,133 +0,0 @@-module Rainbox.ReaderTests where--import qualified Rainbox.Box as R-import Rainbox.Reader-import Rainbox.Box.PrimitivesTests-import Test.QuickCheck hiding (resize)-import Test.Tasty.QuickCheck (testProperty)-import Test.Tasty-import Data.Functor.Identity--tests :: TestTree-tests = testGroup "ReaderTests"-  [ testProperty "blankH" $ \(SpecPair i s) ->-    let p = R.blankH (iBackground i) (unWidth . iWidth $ i)-    in testEq s (blankH (unWidth . iWidth $ i)) p--  , testProperty "blankV" $ \(SpecPair i s) ->-    let p = R.blankV (iBackground i) (unHeight . iHeight $ i)-    in testEq s (blankV (unHeight . iHeight $ i)) p--  , testProperty "catH" $ \(SpecPair i s) ->-    let p = R.catH (iBackground i) (iVert i) (iBoxes i)-    in testEq s (catH (iBoxes i)) p--  , testProperty "catV" $ \(SpecPair i s) ->-    let p = R.catV (iBackground i) (iHoriz i) (iBoxes i)-    in testEq s (catV (iBoxes i)) p--  , testProperty "grow" $ \(SpecPair i s) ->-    let p = R.grow (iBackground i) (iHeight i) (iWidth i)-          (iVert i) (iHoriz i) (iBox i)-    in testEq s (grow (iHeight i) (iWidth i) (iBox i)) p--  , testProperty "growH" $ \(SpecPair i s) ->-    let p = R.growH (iBackground i) (unWidth . iWidth $ i)-          (iHoriz i) (iBox i)-    in testEq s (growH (unWidth . iWidth $ i)-        (iBox i)) p--  , testProperty "growV" $ \(SpecPair i s) ->-    let p = R.growV (iBackground i) (unHeight . iHeight $ i)-          (iVert i) (iBox i)-    in testEq s (growV (unHeight . iHeight $ i)-        (iBox i)) p--  , testProperty "column" $ \(SpecPair i s) ->-    let p = R.column (iBackground i) (iHoriz i) (iBoxes i)-    in testEq s (column (iBoxes i)) p--  , testProperty "resize" $ \(SpecPair i s) ->-    let p = R.resize (iBackground i) (iHeight i) (iWidth i)-          (iVert i) (iHoriz i) (iBox i)-    in testEq s (resize (iHeight i) (iWidth i) (iBox i)) p--  , testProperty "resizeH" $ \(SpecPair i s) ->-    let p = R.resizeH (iBackground i) (unWidth . iWidth $ i)-          (iHoriz i) (iBox i)-    in testEq s (resizeH (unWidth . iWidth $ i) (iBox i)) p--  , testProperty "resizeV" $ \(SpecPair i s) ->-    let p = R.resizeV (iBackground i) (unHeight . iHeight $ i)-          (iVert i) (iBox i)-    in testEq s (resizeV (unHeight . iHeight $ i) (iBox i)) p--  , testProperty "sepH" $ \(SpecPair i s) ->-    let p = R.sepH (iBackground i) (spaceH s) (iVert i) (iBoxes i)-    in testEq s (sepH (spaceH s) (iBoxes i)) p--  , testProperty "sepV" $ \(SpecPair i s) ->-    let p = R.sepV (iBackground i) (spaceV s) (iHoriz i) (iBoxes i)-    in testEq s (sepV (spaceV s) (iBoxes i)) p--  , testProperty "punctuateH" $ \(SpecPair i s) ->-    let p = R.punctuateH (iBackground i) (iVert i) (iBox i) (iBoxes i)-    in testEq s (punctuateH (iBox i) (iBoxes i)) p--  , testProperty "punctuateV" $ \(SpecPair i s) ->-    let p = R.punctuateV (iBackground i) (iHoriz i) (iBox i) (iBoxes i)-    in testEq s (punctuateV (iBox i) (iBoxes i)) p--  , testProperty "viewH" $ \(SpecPair i s) ->-    let p = R.viewH (unWidth . iWidth $ i) (iHoriz i) (iBox i)-    in testEq s (viewH (unWidth . iWidth $ i) (iBox i)) p--  , testProperty "viewV" $ \(SpecPair i s) ->-    let p = R.viewV (unHeight . iHeight $ i) (iVert i) (iBox i)-    in testEq s (viewV (unHeight . iHeight $ i) (iBox i)) p--  , testProperty "view" $ \(SpecPair i s) ->-    let p = R.view (iHeight i) (iWidth i) (iVert i) (iHoriz i)-          (iBox i)-    in testEq s (view (iHeight i) (iWidth i) (iBox i)) p-  ]--testEq :: Eq a => Specs -> Env Identity a -> a -> Bool-testEq s e a = r == a-  where-    r = runEnv s e--specs-  :: Int-  -- ^ Space for horizontal joins-  -> Int-  -- ^ Space for vertical joins-  -> Inputs-  -> Specs-specs h v i = Specs-  { background = iBackground i-  , alignH = iHoriz i-  , alignV = iVert i-  , spaceH = h-  , spaceV = v-  }--genSpecs :: Gen (Inputs, Specs)-genSpecs = do-  h <- frequency [(3, fmap abs arbitrarySizedIntegral),-                  (1, arbitrarySizedIntegral)]-  v <- frequency [(3, fmap abs arbitrarySizedIntegral),-                  (1, arbitrarySizedIntegral)]-  i <- arbitrary-  let ss = specs h v i-  return (i, ss)--data SpecPair = SpecPair-  { spInputs :: Inputs-  , spSpecs :: Specs-  } deriving Show--instance Arbitrary SpecPair where-  arbitrary = do-    (i, s) <- genSpecs-    return $ SpecPair i s
− test/RainboxDir.hs
@@ -1,13 +0,0 @@-module RainboxDir where--import qualified Rainbox.BoxTests-import qualified Rainbox.BoxDir-import qualified Rainbox.ReaderTests-import qualified Rainbox.Array2dTests-import Test.Tasty--tests :: TestTree-tests = testGroup "RainboxDir" [ Rainbox.BoxTests.tests-                               , Rainbox.BoxDir.tests-                               , Rainbox.ReaderTests.tests-                               , Rainbox.Array2dTests.tests ]
− test/RainboxTests.hs
@@ -1,7 +0,0 @@-module RainboxTests where--import Test.Tasty--- import Rainbox--tests :: TestTree-tests = testGroup "Rainbox" []
− test/Visual.hs
@@ -1,142 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-{-# OPTIONS_GHC -fno-warn-missing-signatures #-}-module Visual where--import Control.Monad-import Rainbox.Box-import Rainbow-import Test.QuickCheck hiding (resize)-import Test.QuickCheck.Gen hiding (resize)-import Test.QuickCheck.Random-import Rainbox.Box.PrimitivesTests-import Rainbow.Instances ()-import Data.String--colors = fore yellow <> back blue--narrow = "narrow box" <> colors--midwidth = "medium width box" <> colors--wide = "a wide box, see how wide I am?" <> colors--all3 = [narrow, midwidth, wide]--short = chunk narrow--midheight = catV green left . map chunk $ [narrow, midwidth]--tall = catV green left . map chunk $ [narrow, midwidth, wide]--sizeParam = 7--describe s b = do-  putStrLn (s ++ ":")-  printBox b-  putStrLn ""--testCompound :: String -> (Radiant -> [Box] -> Box) -> IO ()-testCompound d f = do-  g <- newQCGen-  let bxs = unGen (replicateM 5 genTextBox) g sizeParam -      bk = unGen arbitrary g sizeParam-  describe d $ f bk bxs--testVert-  :: String-  -> (Radiant -> Align Vert -> [Box] -> Box)-  -> IO ()-testVert d f = do-  testCompound (d ++ ", top align") (\bk bxs -> f bk top bxs)-  testCompound (d ++ ", center align") (\bk bxs -> f bk center bxs)-  testCompound (d ++ ", bottom align") (\bk bxs -> f bk bottom bxs)--testHoriz-  :: String-  -> (Radiant -> Align Horiz -> [Box] -> Box)-  -> IO ()-testHoriz d f = do-  testCompound (d ++ ", left align") (\bk bxs -> f bk left bxs)-  testCompound (d ++ ", center align") (\bk bxs -> f bk center bxs)-  testCompound (d ++ ", right align") (\bk bxs -> f bk right bxs)---- | Makes a 10x10 test box.-testBox :: Box-testBox = catV noColorRadiant left . map mkLine $ clrs-  where-    mkLine clr = chunk $ txt <> clr-    txt = fromString ['0'..'9']-    clrs = map back . take 10 . iterate (+6) $ (160 :: Word8)--singleH-  :: String-  -> (Align Horiz -> Box)-  -> IO ()-singleH desc f = do-  describe (desc ++ ", left") (f left)-  describe (desc ++ ", center") (f center)-  describe (desc ++ ", right") (f right)--singleV-  :: String-  -> (Align Vert -> Box)-  -> IO ()-singleV desc f = do-  describe (desc ++ ", top") (f top)-  describe (desc ++ ", center") (f center)-  describe (desc ++ ", bottom") (f bottom)--single-  :: String-  -> (Align Vert -> Align Horiz -> Box)-  -> IO ()-single desc f = do-  singleV (desc ++ ", left") (\av -> f av left)-  singleV (desc ++ ", center") (\av -> f av center)-  singleV (desc ++ ", right") (\av -> f av right)--  singleH (desc ++ ", top") (f top)-  singleH (desc ++ ", center") (f center)-  singleH (desc ++ ", bottom") (f bottom)---tests :: IO ()-tests = do-  describe "narrow box" . chunk $ narrow-  describe "medium box" . chunk $ midwidth-  describe "wide box" . chunk $ wide--  testHoriz "catV" catV-  testVert "catH" catH--  testVert "sepH" (\bk av bxs -> sepH bk 1 av bxs)-  testHoriz "sepV" (\bk ah bxs -> sepV bk 1 ah bxs)--  testVert "punctuateH" (\bk av bxs -> punctuateH bk av " " bxs)-  testHoriz "punctuateV" (\bk ah bxs -> punctuateV bk ah " " bxs)--  testHoriz "column" (\bk ah bxs -> catV noColorRadiant left-                        (column bk ah bxs))--  describe "original box for following tests, 10x10" testBox--  single "view, 3x3"-    (\av ah -> view (Height 3) (Width 3) av ah testBox)-  singleH "viewH, 3" (\ah -> viewH 3 ah testBox)-  singleV "viewV, 3" (\av -> viewV 3 av testBox)--  single "grow, 13x13"-    (\av ah -> grow green (Height 13) (Width 13) av ah testBox)-  singleH "growH, 13" (\ah -> growH green 13 ah testBox)-  singleV "growV, 13" (\av -> growV green 13 av testBox)--  single "resize, 13x13"-    (\av ah -> resize green (Height 13) (Width 13) av ah testBox)-  singleH "resizeH, 13" (\ah -> resizeH green 13 ah testBox)-  singleV "resizeV, 13" (\av -> resizeV green 13 av testBox)--  single "resize, 7x7"-    (\av ah -> resize green (Height 7) (Width 7) av ah testBox)-  singleH "resizeH, 7" (\ah -> resizeH green 7 ah testBox)-  singleV "resizeV, 7" (\av -> resizeV green 7 av testBox)-
− test/rainbox-grid.hs
@@ -1,13 +0,0 @@--- | Prints a random grid using the main Rainbox module.  Ignores all--- command line arguments.--module Main where--import Test.QuickCheck-import Rainbox.Instances ()-import Rainbox--main :: IO ()-main = do-  rows <- generate (resize 5 arbitrary)-  printBox $ gridByRows rows
− test/rainbox-mosaic.hs
@@ -1,16 +0,0 @@--- | Usage:------ Input the size parameter as $1.  Will generate a random box and print--- it out. Always uses colors.-module Main where--import Test.QuickCheck-import Rainbox.Box.PrimitivesTests-import System.Environment-import Rainbox.Box--main :: IO ()-main = do-  s:[] <- getArgs-  bx <- generate (Test.QuickCheck.resize (read s) genBox)-  printBox bx
+ test/rainbox-properties.hs view
@@ -0,0 +1,153 @@+module Main where++import Rainbox.Core+import Rainbox.Instances ()+import Rainbow.Types+import Test.Tasty+import Test.Tasty.QuickCheck+import Data.Sequence (Seq, viewl, ViewL(..))+import qualified Data.Sequence as Seq+import qualified Data.Foldable as F+import qualified Data.Text as X+import Control.Monad++main :: IO ()+main = defaultMain . testGroup "Rainbox tests" $+  [ testGroup "split" $+    [ testProperty "sum is equal to original number" $ \a ->+      let (x, y) = split a+      in x + y == a+    ]++  , testGroup "intersperse" $+    [ testProperty "makes no change to empty Seq" $+      intersperse undefined Seq.empty == (Seq.empty :: Seq ())++    , testProperty "makes no change to singleton Seq" $+      intersperse undefined (Seq.singleton ()) == Seq.singleton ()++    , testProperty "lengthens other Seq by length - 1" $ \i ->+      i > 1 ==>+      Seq.length (intersperse undefined (Seq.replicate i ())) ==+      i + (i - 1)+    ]++  , testGroup "HasHeight" $+    [ testGroup "never returns less than zero" $+      let go a = let h = height a in classify (h > 2) "h > 2" (h >= 0) in+      [ testProperty "RodRows" $+          \a -> go (a `asTypeOf` (undefined :: RodRows))+      , testProperty "Core" $+          \a -> go (a `asTypeOf` (undefined :: Core))+      , testProperty "Box Vertical" $+          \a -> go (a `asTypeOf` (undefined :: Box Vertical))+      , testProperty "Box Horizontal" $+          \a -> go (a `asTypeOf` (undefined :: Box Horizontal))+      , testProperty "Payload Vertical" $+          \a -> go (a `asTypeOf` (undefined :: Payload Vertical))+      , testProperty "Payload Horizontal" $+          \a -> go (a `asTypeOf` (undefined :: Payload Horizontal))+      ]+    ]++  , testGroup "HasWidth" $+    [ testGroup "never returns less than zero" $+      let go a = let w = width a in classify (w > 2) "w > 2" (w >= 0) in+      [ testProperty "Chunk" $+          \a -> go (a `asTypeOf` (undefined :: Chunk))+      , testProperty "RodRows" $+          \a -> go (a `asTypeOf` (undefined :: RodRows))+      , testProperty "Rod" $+          \a -> go (a `asTypeOf` (undefined :: Rod))+      , testProperty "Core" $+          \a -> go (a `asTypeOf` (undefined :: Core))+      , testProperty "Box Vertical" $+          \a -> go (a `asTypeOf` (undefined :: Box Vertical))+      , testProperty "Box Horizontal" $+          \a -> go (a `asTypeOf` (undefined :: Box Horizontal))+      , testProperty "Payload Vertical" $+          \a -> go (a `asTypeOf` (undefined :: Payload Vertical))+      , testProperty "Payload Horizontal" $+          \a -> go (a `asTypeOf` (undefined :: Payload Horizontal))+      ]+    ]++  , testGroup "chunk" $+    [ testProperty "height is always 1" $ \c ->+      let _types = c :: Chunk in height c == 1+    , testProperty "width is sum of number of characters" $ \c@(Chunk _ t) ->+      width c == F.sum (fmap X.length t)+    ]++  , testGroup "addVerticalPadding"+    [ testProperty "all RodRows same height" $+      allRodRowsSameHeight . addVerticalPadding+    ]++  , testGroup "UpDown"+    [ testGroup "above + below is same as height" $+      let sameAsHeight a = above a + below a == height a in+      [ testProperty "Box Horizontal"+          (\a -> sameAsHeight (a `asTypeOf` (undefined :: Box Horizontal)))+      , testProperty "Payload Horizontal"+          (\a -> sameAsHeight (a `asTypeOf` (undefined :: Payload Horizontal)))+      ]+    ]++  , testGroup "horizontalMerge"+    [ testProperty "Resulting RodRows has same height as inputs" $+      \rr i ->+      let lenR = case rr of+            RodRowsNoHeight _ -> 0+            RodRowsWithHeight sq -> Seq.length sq+      in height (horizontalMerge (Seq.replicate (getPositive i) rr)) == lenR+    ]++  , testGroup "addHorizontalPadding"+    [ testProperty "all RodRows same width" $+      allRodRowsSameWidth . addHorizontalPadding+    ]++  , testGroup "verticalMerge"+    [ testProperty "resulting RodRows same width as inputs" $ \rr i ->+      let lenR = width rr+          mrge = verticalMerge (Seq.replicate (getPositive i) rr)+          wdth = width mrge+      in counterexample (show (mrge, wdth, lenR)) $ wdth == lenR+    ]+++  ]++allRodRowsSameHeight :: Seq RodRows -> Bool+allRodRowsSameHeight sqnce = case viewl sqnce of+  EmptyL -> True+  x :< xs -> F.all (== height x) . fmap height $ xs++allRodRowsSameWidth :: Seq RodRows -> Property+allRodRowsSameWidth sqnce = +  case viewl sqnce of+    EmptyL -> property True+    x :< _ -> counterexample (show (sqnce, lengths, height1))+      $ F.all (== height1) lengths+      where+        lengths = join . fmap toLengths $ sqnce+        height1 = case x of+          RodRowsNoHeight w -> w+          RodRowsWithHeight sqn -> case viewl sqn of+            EmptyL -> 0+            y :< _ -> F.sum . fmap width $ y+        toLengths (RodRowsNoHeight w) = Seq.singleton w+        toLengths (RodRowsWithHeight sq) = fmap (F.sum . fmap width) sq++rodsLength :: Seq Rod -> Int+rodsLength = F.sum . fmap width++rodRowsLengths :: Seq (Seq Rod) -> Seq Int+rodRowsLengths = fmap rodsLength++seqRodsRowsLengths :: Seq RodRows -> Seq (Seq Int)+seqRodsRowsLengths sq = fmap calc sq+  where+    calc (RodRowsNoHeight w) = Seq.singleton (max 0 w)+    calc (RodRowsWithHeight sqn) = rodRowsLengths sqn
− test/rainbox-test.hs
@@ -1,11 +0,0 @@-module Main where--import qualified RainboxDir-import qualified RainboxTests-import Test.Tasty--main :: IO ()-main = defaultMain $ testGroup "rainbox"-  [ RainboxDir.tests-  , RainboxTests.tests-  ]
test/rainbox-visual.hs view
@@ -1,10 +1,24 @@--- | Rainbox visual tests.------ Tests that are intended to be run and then examined visually.+-- | Prints all the boxes in the tutorial.  The output must be+-- visually inspected.  module Main where -import Visual+import Rainbox.Tutorial +printBox :: String -> IO () -> IO ()+printBox lbl act = do+  putStrLn $ replicate 50 '='+  putStrLn ""+  putStrLn $ lbl ++ ":"+  putStrLn ""+  act+  putStrLn ""+ main :: IO ()-main = Visual.tests+main = do+  printBox "box1" renderBox1+  printBox "box2" renderBox2+  printBox "box3" renderBox3+  printBox "box4" renderBox4+  printBox "box5" renderBox5+  printBox "stationTable" renderStationTable