From e6c54b656ebaf696bec42b6b9de365938636a2bc Mon Sep 17 00:00:00 2001 From: Steven van den Broek Date: Fri, 13 Nov 2020 12:33:17 +0100 Subject: [PATCH] Attempt at adding basic image support Parsing does not work completely yet --- cards/images.txt | 15 +++++++++++ cards/pear.jpeg | Bin 0 -> 25513 bytes src/DeckHandling.hs | 4 +-- src/Import.hs | 4 +-- src/Parser.hs | 54 ++++++++++++++++++++++++++++++++------- src/Runners.hs | 2 +- src/States.hs | 49 ++++++++++++++++++++--------------- src/Types.hs | 61 ++++++++++++++++++++++++++++++++++++-------- src/UI.hs | 3 ++- src/UI/Cards.hs | 24 ++++++++--------- 10 files changed, 158 insertions(+), 58 deletions(-) create mode 100644 cards/images.txt create mode 100644 cards/pear.jpeg diff --git a/cards/images.txt b/cards/images.txt new file mode 100644 index 0000000..94e53f5 --- /dev/null +++ b/cards/images.txt @@ -0,0 +1,15 @@ +# What type of fruit is this? +![](pear.jpeg) +- Apple +- Pear +- Orange +- Banana +--- +# More tests +![](pear.jpeg) +This is a pear +--- +# Again +![](pear.jpeg) +This is a pear again +--- \ No newline at end of file diff --git a/cards/pear.jpeg b/cards/pear.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..5b67b801de5e395f477336044da461489369b5cd GIT binary patch literal 25513 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 BeT4u3 literal 0 HcmV?d00001 diff --git a/src/DeckHandling.hs b/src/DeckHandling.hs index 00534a0..7ab84a3 100644 --- a/src/DeckHandling.hs +++ b/src/DeckHandling.hs @@ -4,13 +4,13 @@ import Lens.Micro.Platform import States import Types -doRandomization :: GlobalState -> [Card] -> IO [Card] +doRandomization :: GlobalState -> [a] -> IO [a] doRandomization gs cards = let n = length cards in do cards' <- if gs^.parameters.pShuffle then sampleFrom (gs^.mwc) (shuffleN n cards) else return cards return $ maybe cards' (`take` cards') (gs^.parameters.pSubset) -doChunking :: Chunk -> [Card] -> [Card] +doChunking :: Chunk -> [a] -> [a] doChunking (Chunk i n) cards = splitIntoNChunks n cards !! (i-1) diff --git a/src/Import.hs b/src/Import.hs index aa8bfde..1489698 100644 --- a/src/Import.hs +++ b/src/Import.hs @@ -20,9 +20,9 @@ parseImportInput iType reverse input = let listToTuple [q, a] = Just $ if not reverse then (q, a) else (a, q) listToTuple _ = Nothing xs = mapM (listToTuple . splitOn "\t") (lines input) - makeOpen (header, body) = OpenQuestion header + makeOpen (header, body) = OpenQuestion header Nothing (P "" (NE.fromList (map (dropWhile isSpace) (splitOneOf ",/;" body))) (Normal "")) in case iType of - Def -> map (uncurry Definition) <$> xs + Def -> map (\(s1, s2) -> Definition s1 Nothing s2) <$> xs Open -> map makeOpen <$> xs \ No newline at end of file diff --git a/src/Parser.hs b/src/Parser.hs index d35ec83..66c7e49 100644 --- a/src/Parser.hs +++ b/src/Parser.hs @@ -12,7 +12,9 @@ import Data.Text (pack, unpack) import Types import qualified Data.List.NonEmpty as NE +-- Type synonyms for convenience type Parser = Parsec Void String +type CardParser = Parser (Either String Card) uncurry3 f (a, b, c) = f a b c @@ -27,42 +29,64 @@ pCards = (pCard `sepEndBy1` seperator) <* eof pCard :: Parser (Either String Card) pCard = try pMultChoice - <|> Right . uncurry MultipleAnswer <$> try pMultAnswer + <|> try pMultAnswer <|> try pReorder - <|> Right . uncurry OpenQuestion <$> try pOpen - <|> Right . uncurry Definition <$> pDef + <|> try pOpen + <|> pDef +pHeader :: Parser String pHeader = do many eol char '#' spaceChar many (noneOf ['\n', '\r']) +pImage :: Parser Image +pImage = do + many eol + char '!' + char '[' + alt <- manyTill anySingle (char ']') + char '(' + img <- manyTill anySingle (char ')') + return $ Image alt img + +pMaybeImage :: Parser (Maybe Image) +pMaybeImage = Just <$> try pImage + <|> pure Nothing + +pMultChoice :: CardParser pMultChoice = do header <- pHeader + img <- pMaybeImage many eol choices <- pChoice `sepBy1` lookAhead (try choicePrefix) msgOrResult <- makeMultipleChoice choices case msgOrResult of Left errMsg -> do pos <- getSourcePos return . Left $ sourcePosPretty pos <> "\n" <> errMsg - Right (correct, incorrects) -> return . Right $ MultipleChoice header correct incorrects + Right (correct, incorrects) -> return . Right $ MultipleChoice header img correct incorrects +pChoice :: Parser (Char, String) pChoice = do kind <- oneOf ['*','-'] spaceChar text <- manyTill anySingle $ lookAhead (try (try choicePrefix <|> seperator <|> eof')) return (kind, text) +choicePrefix :: Parser String choicePrefix = string "- " <|> string "* " +pMultAnswer :: CardParser pMultAnswer = do header <- pHeader + img <- pMaybeImage many eol options <- pOption `sepBy1` lookAhead (try (char '[')) - return (header, NE.fromList options) + return . Right $ MultipleAnswer header img (NE.fromList options) +pOption :: Parser Option pOption = do char '[' kind <- oneOf ['*','x',' '] @@ -70,38 +94,46 @@ pOption = do text <- manyTill anySingle $ lookAhead (try (seperator <|> string "[" <|> eof')) return $ makeOption kind (dropWhileEnd isSpace' text) +pReorder :: CardParser pReorder = do header <- pHeader + img <- pMaybeImage many eol elements <- pReorderElement `sepBy1` lookAhead (try pReorderPrefix) let numbers = map fst elements if all (`elem` numbers) [1..length numbers] - then return . Right $ Reorder header (NE.fromList elements) + then return . Right $ Reorder header img (NE.fromList elements) else do pos <- getSourcePos return . Left $ sourcePosPretty pos <> "\n" <> "A reordering question should have numbers starting from 1 and increase from there without skipping any numbers, but this is not the case:\n" <> unlines (map show numbers) +pReorderElement :: Parser (Int, String) pReorderElement = do int <- pReorderPrefix text <- manyTill anySingle $ lookAhead (try (try seperator <|> try pReorderPrefix <|> eof')) return (read int, dropWhileEnd isSpace' text) +pReorderPrefix :: Parser String pReorderPrefix = do int <- some digitChar string ". " return int +pOpen :: CardParser pOpen = do header <- pHeader + img <- pMaybeImage many eol (pre, gap) <- pGap sentence <- pSentence - return (header, P pre gap sentence) + return $ Right (OpenQuestion header img (P pre gap sentence)) +pSentence :: Parser Sentence pSentence = try pPerforated <|> pNormal - + +pPerforated :: Parser Sentence pPerforated = do (pre, gap) <- pGap Perforated pre gap <$> pSentence @@ -109,6 +141,7 @@ pPerforated = do chars = try escaped <|> anySingle escaped = char '\\' >> char '_' +pGap :: Parser (String, NE.NonEmpty String) pGap = do pre <- manyTill chars $ lookAhead (try (string "_" <|> seperator)) char '_' @@ -120,15 +153,18 @@ gappedSpecialChars = seperator <|> string "|" <|> string "_" +pNormal :: Parser Sentence pNormal = do text <- manyTill (noneOf ['_']) $ lookAhead $ try $ seperator <|> eof' return (Normal (dropWhileEnd isSpace' text)) +pDef :: CardParser pDef = do header <- pHeader + img <- pMaybeImage many eol descr <- manyTill chars $ lookAhead $ try $ seperator <|> eof' - return (header, dropWhileEnd isSpace' descr) + return $ Right (Definition header img (dropWhileEnd isSpace' descr)) eof' = eof >> return [] "end of file" diff --git a/src/Runners.hs b/src/Runners.hs index 292a084..847c1a2 100644 --- a/src/Runners.hs +++ b/src/Runners.hs @@ -47,7 +47,7 @@ cardsState doReview fp deck = do controls <- getShowControls let mFirstCard = safeHead deck - firstCard = fromMaybe (Definition "Empty deck" "Click enter to go back.") mFirstCard + firstCard = fromMaybe (Definition "Empty deck" Nothing "Click enter to go back.") mFirstCard deck' = maybe [firstCard] (const deck) mFirstCard initialState = diff --git a/src/States.hs b/src/States.hs index 7a0cbf7..74dea8c 100644 --- a/src/States.hs +++ b/src/States.hs @@ -103,43 +103,50 @@ data CardState = defaultCardState :: Card -> CardState defaultCardState Definition{} = DefinitionState { _flipped = False } -defaultCardState (MultipleChoice _ _ ics) = MultipleChoiceState +defaultCardState MultipleChoice{incorrects = ics} = MultipleChoiceState { _highlighted = 0 , _number = length ics + 1 , _tried = M.fromList [(i, False) | i <- [0..length ics]] } -defaultCardState (OpenQuestion _ perforated) = OpenQuestionState +defaultCardState OpenQuestion{perforated=perf} = OpenQuestionState { _gapInput = M.empty , _highlighted = 0 - , _number = nGapsInPerforated perforated + , _number = nGapsInPerforated perf , _entered = False - , _correctGaps = M.fromList [(i, False) | i <- [0..nGapsInPerforated perforated - 1]] + , _correctGaps = M.fromList [(i, False) | i <- [0..nGapsInPerforated perf - 1]] , _failed = False } -defaultCardState (MultipleAnswer _ answers) = MultipleAnswerState +defaultCardState MultipleAnswer{options=opts} = MultipleAnswerState { _highlighted = 0 - , _selected = M.fromList [(i, False) | i <- [0..NE.length answers-1]] + , _selected = M.fromList [(i, False) | i <- [0..NE.length opts-1]] , _entered = False - , _number = NE.length answers } -defaultCardState (Reorder _ elements) = ReorderState + , _number = NE.length opts } +defaultCardState Reorder{elements=elts} = ReorderState { _highlighted = 0 , _grabbed = False - , _order = M.fromList (zip [0..] (NE.toList elements)) + , _order = M.fromList (zip [0..] (NE.toList elts)) , _entered = False - , _number = NE.length elements } + , _number = NE.length elts } data CS = CS - { _cards :: [Card] -- list of flashcards - , _index :: Int -- current card index - , _nCards :: Int -- number of cards - , _currentCard :: Card - , _cardState :: CardState - , _showHints :: Bool - , _showControls :: Bool - , _reviewMode :: Bool - , _correctCards :: [Int] -- list of indices of correct cards - , _popup :: Maybe (Popup CS) - , _pathToFile :: FilePath + { _cards :: [Card] -- list of flashcards + , _index :: Int -- current card index + , _nCards :: Int -- number of cards + , _currentCard :: Card + , _cardState :: CardState + , _showHints :: Bool + , _showControls :: Bool + , _reviewMode :: Bool + , _correctCards :: [Int] -- list of indices of correct cards + , _popup :: Maybe (Popup CS) + , _pathToFile :: FilePath } +-- -- Lens for just accessing the cards +-- cards :: Lens' CS [Card] +-- cards = lens (map snd . _cardsAndImages) (\cs cards -> cs {_cardsAndImages = zip (map fst (_cardsAndImages cs)) cards}) + +-- currentCard :: Lens' CS Card +-- currentCard = lens (snd . _currentCardAndImage) (\cs card -> cs {_currentCardAndImage = (fst (_currentCardAndImage cs), card)}) + data Popup s = Popup { drawPopup :: s -> Widget Name , handlePopupEvent :: GlobalState -> s -> V.Event -> EventM Name (Next GlobalState) diff --git a/src/Types.hs b/src/Types.hs index a769149..6211a4a 100644 --- a/src/Types.hs +++ b/src/Types.hs @@ -2,34 +2,75 @@ module Types where import Data.Functor import Data.List import Data.List.NonEmpty (NonEmpty) +import System.FilePath +import System.Process (runCommand) +import System.Info import qualified Data.List.NonEmpty as NE +import qualified System.Directory as D -- Word Description -data Card = Definition String String - | OpenQuestion String Perforated +data Card = Definition { + question :: String, + image :: Maybe Image, + definition :: String } + | OpenQuestion { + question :: String, + image :: Maybe Image, + perforated :: Perforated } | MultipleChoice { question :: String, + image :: Maybe Image, correct :: CorrectOption, incorrects :: [IncorrectOption]} | MultipleAnswer { question :: String, + image :: Maybe Image, options :: NonEmpty Option } | Reorder { question :: String, + image :: Maybe Image, elements :: NonEmpty (Int, String) } instance Show Card where show card = let showHeader h = "# " <> h <> "\n" in case card of - Definition h descr -> showHeader h <> descr - OpenQuestion h p -> showHeader h <> show p - MultipleChoice h c inc -> - showHeader h <> showMultipleChoice c inc - MultipleAnswer h opts -> - showHeader h <> unlines' (NE.toList (NE.map show opts)) - Reorder h elts -> - showHeader h <> unlines' (NE.toList (NE.map showReorder elts)) + Definition h img descr -> showHeader h <> show img <> "\n" <> descr + OpenQuestion h img p -> showHeader h <> show img <> "\n" <> show p + MultipleChoice h img c inc -> + showHeader h <> show img <> "\n" <> showMultipleChoice c inc + MultipleAnswer h img opts -> + showHeader h <> show img <> "\n" <> unlines' (NE.toList (NE.map show opts)) + Reorder h img elts -> + showHeader h <> show img<> "\n" <> unlines' (NE.toList (NE.map showReorder elts)) + +-- alt file +data Image = Image String String + +instance Show Image where + show (Image alt file) = "![" <> alt <> "]" <> "(" <> file <> ")" + +openCommand :: String +openCommand = case os of + "darwin" -> "open" + "linux" -> "xdg-open" + _ -> error "Unkown OS for opening images" + +openImage :: FilePath -> Image -> IO () +openImage origin (Image _ relative) = openImage' (origin relative) + +openImage' :: FilePath -> IO () +openImage' fp = do + exists <- D.doesFileExist fp + if exists + then void $ runCommand (openCommand <> " \"" <> fp <> "\"") + else error $ "The image you were trying to open does not exist: " <> fp + +openCardImage :: FilePath -> Card -> IO () +openCardImage fp = flip whenJust (openImage fp) . image + +whenJust :: Applicative m => Maybe a -> (a -> m ()) -> m () +whenJust mg f = maybe (pure ()) f mg data Type = Incorrect | Correct deriving (Show, Eq) diff --git a/src/UI.hs b/src/UI.hs index e3f666d..eee101d 100644 --- a/src/UI.hs +++ b/src/UI.hs @@ -6,6 +6,7 @@ module UI , GenIO , Chunk(..) , Card +, Image , ImportType(..) , Parameters(..) @@ -26,7 +27,7 @@ import Glue import Import import States import StateManagement -import Types (Card, cardsToString) +import Types (Card, Image, cardsToString) runBrickFlashcards :: GlobalState -> IO () runBrickFlashcards gs = do diff --git a/src/UI/Cards.hs b/src/UI/Cards.hs index d8cffa5..092ed5c 100644 --- a/src/UI/Cards.hs +++ b/src/UI/Cards.hs @@ -63,23 +63,23 @@ drawCardUI :: CS -> Widget Name drawCardUI s = let p = 1 in joinBorders $ drawCardBox $ (<=> drawFooter s) $ case (s ^. cards) !! (s ^. index) of - Definition title descr -> drawHeader title + Definition title _ descr -> drawHeader title <=> B.hBorder <=> padLeftRight p (drawDef s descr <=> str " ") - MultipleChoice question correct others -> drawHeader question + MultipleChoice question _ correct others -> drawHeader question <=> B.hBorder <=> padLeftRight p (drawChoices s (listMultipleChoice correct others) <=> str " ") - OpenQuestion title perforated -> drawHeader title + OpenQuestion title _ perforated -> drawHeader title <=> B.hBorder <=> padLeftRight p (atLeastV 1 (drawPerforated s perforated) <=> str " ") - MultipleAnswer question options -> drawHeader question + MultipleAnswer question _ options -> drawHeader question <=> B.hBorder <=> padRight (Pad p) (drawOptions s options <=> str " ") - Reorder question _ -> drawHeader question + Reorder question _ _ -> drawHeader question <=> B.hBorder <=> padLeftRight p (drawReorder s <=> str " ") @@ -118,7 +118,7 @@ drawNormalDef s def = case s ^. cardState of drawChoices :: CS -> [String] -> Widget Name drawChoices s options = case (s ^. cardState, s ^. currentCard) of - (MultipleChoiceState {_highlighted=i, _tried=kvs}, MultipleChoice _ (CorrectOption k _) _) -> vBox formattedOptions + (MultipleChoiceState {_highlighted=i, _tried=kvs}, MultipleChoice _ _ (CorrectOption k _) _) -> vBox formattedOptions where formattedOptions :: [Widget Name] formattedOptions = [ prefix <+> coloring (drawDescr opt) | @@ -214,7 +214,7 @@ wrapStringWithPadding padding w s drawReorder :: CS -> Widget Name drawReorder s = case (s ^. cardState, s ^. currentCard) of - (ReorderState {_highlighted=j, _grabbed=g, _order=kvs, _number=n, _entered=submitted}, Reorder _ _) -> + (ReorderState {_highlighted=j, _grabbed=g, _order=kvs, _number=n, _entered=submitted}, Reorder{}) -> vBox . flip map (map (\i -> (i, kvs M.! i)) [0..n-1]) $ \(i, (k, text)) -> let color = case (i == j, g) of @@ -259,7 +259,7 @@ handleEvent gs s (VtyEvent e) = else continue' $ s & cardState.flipped %~ not _ -> continue' s - (MultipleChoiceState {_highlighted = i, _number = n, _tried = kvs}, MultipleChoice _ (CorrectOption j _) _) -> + (MultipleChoiceState {_highlighted = i, _number = n, _tried = kvs}, MultipleChoice _ _ (CorrectOption j _) _) -> case ev of V.EvKey V.KUp [] -> continue' up V.EvKey (V.KChar 'k') [] -> continue' up @@ -286,7 +286,7 @@ handleEvent gs s (VtyEvent e) = correctlyAnswered = i == j && M.size (M.filter (==True) kvs) == 1 - (MultipleAnswerState {_highlighted = i, _number = n, _entered = submitted, _selected = kvs}, MultipleAnswer _ opts) -> + (MultipleAnswerState {_highlighted = i, _number = n, _entered = submitted, _selected = kvs}, MultipleAnswer _ _ opts) -> case ev of V.EvKey V.KUp [] -> continue' up V.EvKey (V.KChar 'k') [] -> continue' up @@ -315,7 +315,7 @@ handleEvent gs s (VtyEvent e) = correctlyAnswered = NE.toList (NE.map isOptionCorrect opts) == map snd (M.toAscList kvs) - (OpenQuestionState {_highlighted = i, _number = n, _gapInput = kvs, _correctGaps = cGaps, _failed=fail}, OpenQuestion _ perforated) -> + (OpenQuestionState {_highlighted = i, _number = n, _gapInput = kvs, _correctGaps = cGaps, _failed=fail}, OpenQuestion _ _ perforated) -> let frozen = M.foldr (&&) True cGaps in case ev of V.EvKey (V.KFun 1) [] -> continue' $ @@ -364,7 +364,7 @@ handleEvent gs s (VtyEvent e) = backspace xs = init xs _ -> continue' s - (ReorderState {_highlighted = i, _entered = submitted, _grabbed=dragging, _number = n, _order = kvs }, Reorder _ elts) -> + (ReorderState {_highlighted = i, _entered = submitted, _grabbed=dragging, _number = n, _order = kvs }, Reorder _ _ elts) -> case ev of V.EvKey V.KUp [] -> continue' up V.EvKey (V.KChar 'k') [] -> continue' up @@ -406,7 +406,7 @@ handleEvent gs _ _ = continue gs next :: GlobalState -> CS -> EventM Name (Next GlobalState) next gs s - | s ^. index + 1 < length (s ^. cards) = continue . updateCS gs . straightenState $ s & index +~ 1 + | s ^. index + 1 < length (s ^. cards) = liftIO (openCardImage (takeDirectory (s^.pathToFile)) ((s^.cards) !! (s^.index + 1))) *> (continue . updateCS gs . straightenState $ s & index +~ 1) | s ^. reviewMode = let thePopup = if null (s^.correctCards) || length (s^. correctCards) == length (s^.cards)