summaryrefslogtreecommitdiff
path: root/src/Main.hs
diff options
context:
space:
mode:
Diffstat (limited to 'src/Main.hs')
-rw-r--r--src/Main.hs150
1 files changed, 37 insertions, 113 deletions
diff --git a/src/Main.hs b/src/Main.hs
index eff9a92..834cefd 100644
--- a/src/Main.hs
+++ b/src/Main.hs
@@ -1,4 +1,4 @@
1{-# LANGUAGE FlexibleContexts, ConstraintKinds, ViewPatterns, OverloadedStrings #-} 1{-# LANGUAGE FlexibleContexts, ViewPatterns, OverloadedStrings #-}
2 2
3import Data.PQueue.Prio.Max (MaxPQueue) 3import Data.PQueue.Prio.Max (MaxPQueue)
4import qualified Data.PQueue.Prio.Max as MaxPQueue 4import qualified Data.PQueue.Prio.Max as MaxPQueue
@@ -6,122 +6,46 @@ import qualified Data.PQueue.Prio.Max as MaxPQueue
6import Data.Sequence (Seq, (|>), (<|), viewl, viewr, ViewL(..), ViewR(..)) 6import Data.Sequence (Seq, (|>), (<|), viewl, viewr, ViewL(..), ViewR(..))
7import qualified Data.Sequence as Seq 7import qualified Data.Sequence as Seq
8 8
9import Data.List (delete) 9import Data.Default.Class
10import Data.Bool 10import Data.Maybe (fromMaybe)
11 11
12import Data.Monoid (Monoid(..), (<>)) 12import System.Environment.XDG.BaseDir (getUserCacheFile)
13 13import System.Environment (getProgName)
14import Data.Default.Class 14import System.Exit
15
16import System.Console.Readline (readline)
17
18import Control.Monad.State.Lazy (MonadState(..), runStateT, modify)
19import Control.Monad.IO.Class (MonadIO(..))
20
21import Control.Monad (void, join)
22
23import Control.Lens hiding ((|>), (:<))
24
25import Data.Text (Text)
26import qualified Data.Text as Text
27import qualified Data.Text.IO as Text
28
29import Text.Read (readMaybe)
30
31import Data.CaseInsensitive ( CI )
32import qualified Data.CaseInsensitive as CI
33
34import System.Exit (exitSuccess)
35 15
16import System.Console.Haskeline
36 17
18import Control.Monad.State.Strict
19import Control.Monad.Writer.Strict
20import Control.Monad.IO.Class
21
37import Types 22import Types
23import Utils
24import Command
38 25
39type SequenceM m = (MonadIO m, MonadState SequenceState m) 26type SequenceT m a = WriterT History (InputT (StateT Sequence m)) a
40
41prompt :: String
42prompt = "→ "
43 27
44main :: IO () 28main :: IO ()
45main = void $ runStateT acceptCmd def 29main = do
46 30 historyFile <- flip getUserCacheFile "history" =<< getProgName
47acceptCmd :: SequenceM m => m () 31 let
48acceptCmd = join $ liftIO (readline prompt) >>= handleCmd 32 settings = setComplete noCompletion {- TODO -} . (\x -> x { historyFile = Just historyFile }) $ defaultSettings
49 33 void . flip runStateT def . runInputT settings . runWriterT $ runCli
50terminate :: MonadIO m => m a 34
51terminate = liftIO exitSuccess 35runCli :: (MonadIO m, MonadException m) => SequenceT m ()
52 36runCli = do
53handleCmd :: SequenceM m => Maybe String -> m (m ()) 37 input <- lift $ getInputLine "→ "
54handleCmd Nothing = return terminate -- EOF 38 cmnd <- maybe (return undefined) parseCmd input
55handleCmd (Just "") = return acceptCmd -- Empty input 39 case input of
56handleCmd (Just (Text.words . Text.pack -> (CI.mk -> cmd) : args)) -- Input containing at least one word 40 Nothing -> liftIO exitSuccess
57 | cmd `elem` ["exit", "quit"] = return $ return () 41 Just _ -> do
58 | cmd `elem` ["insert", "add"] = acceptCmd <$ case args of 42 case cmnd of
59 (name : (readMaybe . Text.unpack -> Just val) : comment) -> bool (liftIO $ Text.putStrLn "Did not insert new entity") (return ()) =<< withNewName (\e -> modify $ update (Insert e val, parseComment comment)) name 43 UnknownCommand cmd -> do
60 _ -> liftIO . Text.putStrLn $ "Malformed arguments to insert.\nExpecting: insert <name> <sequence value> [<comment> […]]" 44 lift . outputStrLn $ "Unknown command: »" <> cmd <> "«"
61 | cmd == "dump" = acceptCmd <$ (get >>= liftIO . print) 45 ParseError err -> do
62 | otherwise = acceptCmd <$ do 46 lift . outputStrLn $ "Error parsing input »" <> (fromMaybe "" input) <> "«: " <> err
63 liftIO $ Text.putStrLn ("No such command known: " <> CI.original cmd) 47
64handleCmd _ = error "Could not parse input String to Text" 48 Quit -> liftIO exitSuccess
65 49 PerformAlt alt -> apply' alt
66askBool :: MonadIO m => Text -> m Bool 50 Step -> undefined
67askBool question = do 51 runCli
68 str <- liftIO $ readline (Text.unpack question <> " (Yes/No)?\n" <> prompt)
69 case str of
70 Nothing -> terminate
71 Just (CI.mk -> "yes") -> return True
72 Just (CI.mk -> "y") -> return True
73 Just (CI.mk -> "no") -> return False
74 Just (CI.mk -> "n") -> return False
75 _ -> askBool question
76
77withNewName :: SequenceM m => (Entity -> m ()) -> Text -> m Bool
78withNewName callback name = do
79 -- names <- getNames <$> use history
80 entity <- getEntity
81 maybe False (const True) <$> maybe (return Nothing) (\e -> Just <$> callback e) entity
82 where
83 number :: MaxPQueue Integer Entity -> MaxPQueue Integer Entity
84 number = fst . MaxPQueue.foldrWithKey accum (MaxPQueue.empty, 0)
85 accum key val o@(queue, maxNmbr)
86 | (Entity (CI.mk -> val') _) <- val
87 , val' == CI.mk name = (MaxPQueue.insert key (Entity (CI.original val') (Just $ succ maxNmbr)) queue, succ maxNmbr)
88 | otherwise = o
89 getEntity = do
90 names <- MaxPQueue.elems <$> use queue -- we have semantic guarantees that names is a set
91 let
92 takenNumbers = map (\(Entity _ n) -> n) $ filter (\(Entity (CI.mk -> id) _) -> id == CI.mk name) names
93 case takenNumbers of
94 [] -> return . Just $ Entity name Nothing
95 [Nothing] -> do
96 liftIO $ Text.putStrLn ("Name »" <> name <> "« is already taken.")
97 useNumber <- liftIO $ askBool "Introduce a numbering scheme"
98 if useNumber
99 then queue <~ (number) >> getEntity
100 else return Nothing
101 (maximum -> (Just maxNmbr)) -> do
102 let
103 entity = Entity name (Just $ succ maxNmbr)
104 liftIO $ Text.putStrLn ("Changed name to »" <> showEntity entity <> "«")
105 return $ Just entity
106
107parseComment :: [Text] -> Maybe Comment
108parseComment (Text.unwords -> c)
109 | c == "" = Nothing
110 | otherwise = Just c
111
112update :: (Alteration, Maybe Comment) -> SequenceState -> SequenceState
113update loggable@(alt, _) = affect alt . over history (|> loggable)
114
115affect :: Alteration -> SequenceState -> SequenceState
116affect (Modify id by) = over queue $ affect' (\(k, v) -> if v == id then Just (k + by, id) else Just (k, v))
117affect (Override id new) = over queue $ affect' (\(k, v) -> if v == id then Just (new, id) else Just (k, v))
118affect (Drop id) = over queue $ MaxPQueue.filter (/= id)
119affect (Insert id v) = over queue $ MaxPQueue.insert v id
120affect _ = error "Modification not implemented yet"
121
122affect' :: ((Integer, Entity) -> Maybe (Integer, Entity)) -> MaxPQueue Integer Entity -> MaxPQueue Integer Entity
123affect' f = MaxPQueue.foldrWithKey accum MaxPQueue.empty
124 where
125 accum key val
126 | (Just (key', val')) <- f (key, val) = MaxPQueue.insert key' val'
127 | otherwise = id