App.hs 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. {-# LANGUAGE OverloadedStrings #-}
  2. module Bunyan.App (runApp) where
  3. import qualified Brick
  4. import qualified Data.Map as M
  5. import Data.Monoid ((<>))
  6. import qualified Data.Sequence as S
  7. import qualified Data.Text as T
  8. import qualified Graphics.Vty as Vty
  9. import qualified Graphics.Vty.Input.Events as Vty
  10. import qualified Bunyan.Log as Log
  11. data Modal
  12. = AddSectionModal ()
  13. | ConfirmQuitModal
  14. deriving (Eq, Show)
  15. data Annot
  16. = Annot T.Text Log.Entry
  17. | Skip Log.Entry
  18. deriving (Eq, Show)
  19. data State = State
  20. { stateSections :: M.Map T.Text (S.Seq (S.Seq T.Text))
  21. , stateCommits :: S.Seq Log.Entry
  22. , stateFinished :: S.Seq Annot
  23. , stateKeys :: M.Map Char T.Text
  24. , stateModal :: Maybe Modal
  25. , stateStatus :: T.Text
  26. } deriving (Eq, Show)
  27. defaultSections :: [(Char, T.Text)]
  28. defaultSections =
  29. [ ('f', "New features")
  30. , ('b', "Bug fixes")
  31. , ('p', "Package changes")
  32. , ('d', "Documentation changes")
  33. , ('i', "Performance improvements")
  34. ]
  35. newState :: S.Seq Log.Entry -> State
  36. newState commits = State
  37. { stateSections = M.fromList
  38. [ (name, mempty) | (_, name) <- defaultSections ]
  39. , stateKeys = M.fromList defaultSections
  40. , stateCommits = commits
  41. , stateFinished = S.empty
  42. , stateModal = Nothing
  43. , stateStatus = ""
  44. }
  45. runApp :: S.Seq Log.Entry -> IO (M.Map T.Text (S.Seq (S.Seq T.Text)))
  46. runApp entries = do
  47. let state = newState entries
  48. vty = Vty.mkVty Vty.defaultConfig
  49. final <- Brick.customMain vty Nothing app state
  50. return (stateSections final)
  51. app :: Brick.App State () ()
  52. app = Brick.App
  53. { Brick.appDraw = draw
  54. , Brick.appChooseCursor = Brick.showFirstCursor
  55. , Brick.appHandleEvent = event
  56. , Brick.appStartEvent = return
  57. , Brick.appAttrMap = \ _ -> Brick.forceAttrMap mempty
  58. }
  59. draw :: State -> [Brick.Widget ()]
  60. draw st
  61. | Just ConfirmQuitModal <- stateModal st =
  62. [ Brick.txt "Are you sure you want to quit? (y/n)" ]
  63. | otherwise =
  64. [ Brick.vBox
  65. [ Brick.str (show (stateModal st))
  66. , Brick.txt (stateStatus st)
  67. ]
  68. ]
  69. type EventHandler =
  70. State -> Vty.Key -> [Vty.Modifier] -> Brick.EventM () (Brick.Next State)
  71. event :: State -> Brick.BrickEvent () () -> Brick.EventM () (Brick.Next State)
  72. event st (Brick.VtyEvent (Vty.EvKey key mod)) =
  73. case stateModal st of
  74. Nothing -> mainEvent st key mod
  75. Just ConfirmQuitModal -> confirmQuitEvent st key mod
  76. Just (AddSectionModal ()) -> addSectionEvent st key mod
  77. event st _ = Brick.continue st
  78. mainEvent :: EventHandler
  79. mainEvent st (Vty.KChar ' ') [] = do
  80. let car S.:< cdr = S.viewl (stateCommits st)
  81. Brick.continue st
  82. { stateFinished = Skip car S.<| stateFinished st
  83. , stateCommits = cdr
  84. , stateStatus = "skipped"
  85. }
  86. mainEvent st (Vty.KChar 'q') [] =
  87. Brick.continue st { stateModal = Just ConfirmQuitModal }
  88. mainEvent st (Vty.KChar 'a') [] =
  89. Brick.continue st { stateModal = Just (AddSectionModal ()) }
  90. mainEvent st (Vty.KChar c) []
  91. | Just annot <- M.lookup c (stateKeys st) = do
  92. let car S.:< cdr = S.viewl (stateCommits st)
  93. Brick.continue st
  94. { stateFinished = Annot annot car S.<| stateFinished st
  95. , stateCommits = cdr
  96. , stateSections =
  97. M.adjust ((Log.logMessage car) S.<|) annot (stateSections st)
  98. , stateStatus =
  99. "Added " <> Log.logCommit car <> " to section " <> annot
  100. }
  101. | otherwise =
  102. Brick.continue st { stateStatus = "Unknown keybindings: " <> T.pack (show c) }
  103. mainEvent st _ _ = Brick.continue st
  104. confirmQuitEvent :: EventHandler
  105. confirmQuitEvent st (Vty.KChar 'y') _ = Brick.halt st
  106. confirmQuitEvent st _ _ = Brick.continue st { stateModal = Nothing }
  107. addSectionEvent :: EventHandler
  108. addSectionEvent st _ _ = Brick.continue st { stateModal = Nothing }