taskell 1.3.5.0 → 1.3.6.0
raw patch · 34 files changed
+173/−168 lines, 34 files
Files
- README.md +16/−4
- app/Main.hs +1/−1
- src/App.hs +8/−8
- src/Config.hs +1/−1
- src/Data/Taskell/List/Internal.hs +1/−1
- src/Data/Taskell/Lists/Internal.hs +2/−2
- src/Data/Taskell/Seq.hs +1/−1
- src/Events/Actions.hs +1/−1
- src/Events/Actions/Insert.hs +3/−3
- src/Events/Actions/Modal.hs +1/−1
- src/Events/Actions/Modal/Detail.hs +1/−1
- src/Events/Actions/Modal/Help.hs +1/−1
- src/Events/Actions/Modal/MoveTo.hs +1/−1
- src/Events/Actions/Normal.hs +2/−2
- src/Events/Actions/Search.hs +2/−2
- src/Events/State.hs +5/−5
- src/Events/State/Modal/Detail.hs +5/−5
- src/IO/Config.hs +33/−35
- src/IO/Config/General.hs +1/−1
- src/IO/Config/GitHub.hs +1/−1
- src/IO/Config/Layout.hs +1/−1
- src/IO/Config/Markdown.hs +1/−1
- src/IO/Config/Trello.hs +1/−1
- src/IO/HTTP/Aeson.hs +1/−1
- src/IO/HTTP/GitHub.hs +20/−20
- src/IO/HTTP/Trello.hs +9/−9
- src/IO/Markdown/Internal.hs +5/−5
- src/IO/Taskell.hs +18/−18
- src/UI/CLI.hs +8/−13
- src/UI/Draw.hs +11/−11
- src/UI/Field.hs +8/−8
- src/UI/Modal/Detail.hs +1/−1
- src/UI/Modal/MoveTo.hs +1/−1
- taskell.cabal +1/−1
README.md view
@@ -51,12 +51,22 @@ ### Fedora -Run `sudo dnf install ncurses-compat-libs` then download and run binary as described below.+Not officially supported, but try running `sudo dnf install ncurses-compat-libs` then download and run the binary as described below. If that doesn't work you may need to build from scratch ([Cabal](#cabal)/[Stack](#stack)). ### Binaries -[A binary is available for Mac and Linux](https://github.com/smallhadroncollider/taskell/releases). Download it and copy it to a directory in your `$PATH` (e.g. `/usr/local/bin` or `/usr/bin`).+[A binary is available for Mac and Debian/Ubuntu](https://github.com/smallhadroncollider/taskell/releases). Download it and copy it to a directory in your `$PATH` (e.g. `/usr/local/bin` or `/usr/bin`). +### Cabal++You can install Taskell with `cabal`:++```bash+cabal install taskell+```++Make sure you run `cabal update` if you haven't run it recently.+ ### Stack If none of the above options work you can build taskell using [Stack](https://docs.haskellstack.org/en/stable/README/). First [install Stack on your machine](https://docs.haskellstack.org/en/stable/README/#how-to-install). Then clone the repo and run `stack build && stack install`: this will build taskell and then install it in `~/.local/bin` (so make sure that directory is in your `$PATH`). Building from scratch can take a long time and occasionally doesn't work the first time (if this happens try running it again).@@ -192,8 +202,10 @@ ## Configuration -You can edit Taskell's settings by editing `~/.taskell/config.ini`:+Taskell uses the [XDG Base Directory Specification](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html), so it will look for an `$XDG_CONFIG_HOME` environmental variable and create a directory named `taskell` inside it. If this variable is not found it will create the `taskell` directory in `~/.config/`. (If you've been using Taskell since <= 1.3.5 then it will be in a `~/.taskell` directory, feel free to move this to the XDG directory.) +Taskell has a `config.ini` file:+ ```ini [general] ; the default filename to create/look for@@ -241,7 +253,7 @@ ### Theming -You can edit Taskell's colour-scheme by editing `~/.taskell/theme.ini`:+You can edit Taskell's colour-scheme by editing `theme.ini`: ```ini [other]
app/Main.hs view
@@ -15,6 +15,6 @@ config <- setup next <- runReaderT load config case next of- Exit -> return ()+ Exit -> pure () Output text -> putStrLn text Load path lists -> go config $ create path lists
src/App.hs view
@@ -56,7 +56,7 @@ } Debounce.def let send = Debounce.send trigger- return (send, trigger)+ pure (send, trigger) -- cache clearing clearCache :: State -> EventM ResourceName ()@@ -88,19 +88,19 @@ Search _ _ -> invalidateCache (Modal MoveTo) -> clearAllTitles previousState (Insert ITask ICreate _) -> clearList previousState- _ -> return ()+ _ -> pure () case state ^. mode of- Shutdown -> liftIO (Debounce.close trigger) >> Brick.halt state- (Modal MoveTo) -> clearAllTitles state >> next send state- (Insert ITask ICreate _) -> clearList state >> next send state- _ -> clearCache previousState >> clearCache state >> next send state+ Shutdown -> liftIO (Debounce.close trigger) *> Brick.halt state+ (Modal MoveTo) -> clearAllTitles state *> next send state+ (Insert ITask ICreate _) -> clearList state *> next send state+ _ -> clearCache previousState *> clearCache state *> next send state handleEvent :: (DebouncedWrite, Trigger) -> State -> BrickEvent ResourceName e -> EventM ResourceName (Next State)-handleEvent _ state (VtyEvent (EvResize _ _)) = invalidateCache >> Brick.continue state+handleEvent _ state (VtyEvent (EvResize _ _)) = invalidateCache *> Brick.continue state handleEvent db state (VtyEvent ev) = handleVtyEvent db state ev handleEvent _ state _ = Brick.continue state @@ -111,7 +111,7 @@ vty <- getVtyHandle let output = outputIface vty when (supportsMode output BracketedPaste) $ liftIO $ setMode output BracketedPaste True- return state+ pure state -- | Sets up Brick go :: Config -> State -> IO ()
src/Config.hs view
@@ -9,7 +9,7 @@ import Data.FileEmbed (embedFile) version :: Text-version = "1.3.5"+version = "1.3.6" usage :: Text usage = decodeUtf8 $(embedFile "templates/usage.txt")
src/Data/Taskell/List/Internal.hs view
@@ -44,7 +44,7 @@ extract :: Int -> List -> Maybe (List, T.Task) extract idx list = do (xs, x) <- S.extract idx (list ^. tasks)- return (list & tasks .~ xs, x)+ pure (list & tasks .~ xs, x) updateFn :: Int -> T.Update -> Update updateFn idx fn = tasks %~ adjust' fn idx
src/Data/Taskell/Lists/Internal.hs view
@@ -32,7 +32,7 @@ let next = list + dir (from, task) <- extract idx =<< tasks !? list -- extract current task to <- append task <$> tasks !? next -- get next list and append task- return . updateLists next to $ updateLists list from tasks -- update lists+ pure . updateLists next to $ updateLists list from tasks -- update lists newList :: Text -> Update newList title = (|> empty title)@@ -54,7 +54,7 @@ fromMaybe lists $ do let idx = length lists - 1 list <- append task <$> lists !? idx- return $ updateLists idx list lists+ pure $ updateLists idx list lists analyse :: Text -> Lists -> Text analyse filepath lists =
src/Data/Taskell/Seq.hs view
@@ -12,4 +12,4 @@ shiftBy :: Int -> Int -> Seq a -> Maybe (Seq a) shiftBy idx dir xs = do (a, current) <- extract idx xs- return $ insertAt (idx + dir) current a+ pure $ insertAt (idx + dir) current a
src/Events/Actions.hs view
@@ -27,7 +27,7 @@ Search {} -> Search.event e state Insert {} -> Insert.event e state Modal {} -> Modal.event e state- _ -> return state+ _ -> pure state -- returns new state if successful event :: Event -> State -> State
src/Events/Actions/Insert.hs view
@@ -24,15 +24,15 @@ (write =<<) . (below =<<) . (removeBlank =<<) . (store =<<) $ finishTask state Insert ITask IEdit _ -> (write =<<) . (removeBlank =<<) . (normalMode =<<) $ finishTask state- _ -> return state+ _ -> pure state event (EvKey KEsc _) state = case state ^. mode of Insert IList ICreate _ -> (normalMode =<<) . (write =<<) $ createList state Insert IList IEdit _ -> (write =<<) . (normalMode =<<) $ finishListTitle state Insert ITask _ _ -> (write =<<) . (removeBlank =<<) . (normalMode =<<) $ finishTask state- _ -> return state+ _ -> pure state event e state =- return $+ pure $ case state ^. mode of Insert iType iMode field -> state & mode .~ Insert iType iMode (F.event e field) _ -> state
src/Events/Actions/Modal.hs view
@@ -22,4 +22,4 @@ Modal Help -> Help.event e s Modal (Detail _ _) -> Detail.event e s Modal MoveTo -> MoveTo.event e s- _ -> return s+ _ -> pure s
src/Events/Actions/Modal/Detail.hs view
@@ -26,7 +26,7 @@ normal (EvKey (KChar 'D') _) = (write =<<) . (Detail.remove =<<) . store normal (EvKey (KChar 'u') _) = (write =<<) . undo normal (EvKey (KChar '@') _) = (editDue =<<) . store-normal _ = return+normal _ = pure insert :: Event -> Stateful insert (EvKey KEsc _) s = do
src/Events/Actions/Modal/Help.hs view
@@ -12,4 +12,4 @@ event :: Event -> Stateful event (EvKey (KChar 'q') _) = quit event (EvKey _ _) = normalMode-event _ = return+event _ = pure
src/Events/Actions/Modal/MoveTo.hs view
@@ -13,4 +13,4 @@ event (EvKey KEsc _) = normalMode event (EvKey KEnter _) = normalMode event (EvKey (KChar c) _) = (normalMode =<<) . (write =<<) . (moveTo c =<<) . store-event _ = return+event _ = pure
src/Events/Actions/Normal.hs view
@@ -58,6 +58,6 @@ -- selecting lists event (EvKey (KChar n) _) | isDigit n = selectList n- | otherwise = return+ | otherwise = pure -- fallback-event _ = return+event _ = pure
src/Events/Actions/Search.hs view
@@ -19,7 +19,7 @@ search :: Event -> Stateful search (EvKey KEnter _) s = searchEntered s search e s =- return $+ pure $ case s ^. mode of Search ent field -> s & mode .~ Search ent (F.event e field) _ -> s@@ -34,4 +34,4 @@ else Normal.event) e s- _ -> return s+ _ -> pure s
src/Events/State.hs view
@@ -113,7 +113,7 @@ editListStart :: Stateful editListStart state = do f <- textToField . (^. title) <$> getList state- return $ state & mode .~ Insert IList IEdit f+ pure $ state & mode .~ Insert IList IEdit f deleteCurrentList :: Stateful deleteCurrentList state =@@ -136,7 +136,7 @@ startEdit :: Stateful startEdit state = do field <- textToField . (^. name) <$> getCurrentTask state- return $ state & mode .~ Insert ITask IEdit field+ pure $ state & mode .~ Insert ITask IEdit field finishTask :: Stateful finishTask state =@@ -173,7 +173,7 @@ clearItem = setCurrentTaskText "" bottom :: Stateful-bottom = return . selectLast+bottom = pure . selectLast selectLast :: InternalStateful selectLast state = setIndex state (countCurrent state - 1)@@ -183,7 +183,7 @@ currentTask <- getCurrentTask state (if isBlank currentTask then delete- else return)+ else pure) state -- moving@@ -311,7 +311,7 @@ then Nothing else do s <- move' (li - cur) state- return . selectLast $ setCurrentList s li+ pure . selectLast $ setCurrentList s li -- move lists listMove :: Int -> Stateful
src/Events/State/Modal/Detail.hs view
@@ -38,7 +38,7 @@ updateField :: (Field -> Field) -> Stateful updateField fieldEvent s =- return $+ pure $ case s ^. mode of Modal (Detail detailItem (DetailInsert field)) -> s & mode .~ Modal (Detail detailItem (DetailInsert (fieldEvent field)))@@ -67,7 +67,7 @@ showDetail s = do _ <- getCurrentTask s let i = fromMaybe 0 $ getCurrentSubtask s- return $ s & mode .~ Modal (Detail (DetailItem i) DetailNormal)+ pure $ s & mode .~ Modal (Detail (DetailItem i) DetailNormal) getCurrentSubtask :: State -> Maybe Int getCurrentSubtask state =@@ -120,13 +120,13 @@ editDescription state = do summ <- (^. description) <$> getCurrentTask state let summ' = fromMaybe "" summ- return $ state & mode .~ Modal (Detail DetailDescription (DetailInsert (textToField summ')))+ pure $ state & mode .~ Modal (Detail DetailDescription (DetailInsert (textToField summ'))) editDue :: Stateful editDue state = do day <- (^. due) <$> getCurrentTask state let day' = maybe "" dayToOutput day- return $ state & mode .~ Modal (Detail DetailDate (DetailInsert (textToField day')))+ pure $ state & mode .~ Modal (Detail DetailDate (DetailInsert (textToField day'))) newItem :: Stateful newItem state = do@@ -159,4 +159,4 @@ | i > lst = lst | i < 0 = 0 | otherwise = i- return $ state & mode .~ Modal (Detail (DetailItem newIndex) m)+ pure $ state & mode .~ Modal (Detail (DetailItem newIndex) m)
src/IO/Config.hs view
@@ -6,8 +6,10 @@ import ClassyPrelude -import qualified Data.Text.IO as T (readFile)-import System.Directory (createDirectoryIfMissing, doesFileExist, getHomeDirectory)+import qualified Data.Text.IO as T (readFile)+import System.Directory (createDirectoryIfMissing, doesDirectoryExist, doesFileExist,+ getHomeDirectory)+import System.Environment (lookupEnv) import Brick (AttrMap) import Brick.Themes (loadCustomizations, themeToAttrMap)@@ -33,21 +35,32 @@ defaultConfig :: Config defaultConfig = Config- { general = General.defaultConfig- , layout = Layout.defaultConfig- , markdown = Markdown.defaultConfig- , trello = Trello.defaultConfig- , github = GitHub.defaultConfig- }+ General.defaultConfig+ Layout.defaultConfig+ Markdown.defaultConfig+ Trello.defaultConfig+ GitHub.defaultConfig +directoryName :: FilePath+directoryName = "taskell"++legacyConfigPath :: IO FilePath+legacyConfigPath = (</> "." <> directoryName) <$> getHomeDirectory++xdgDefaultConfig :: IO FilePath+xdgDefaultConfig = (</> ".config" </> directoryName) <$> getHomeDirectory++xdgConfigPath :: IO FilePath+xdgConfigPath = fromMaybe <$> xdgDefaultConfig <*> lookupEnv "XDG_CONFIG_HOME"+ getDir :: IO FilePath-getDir = (++ "/.taskell") <$> getHomeDirectory+getDir = legacyConfigPath >>= doesDirectoryExist >>= bool xdgConfigPath legacyConfigPath getThemePath :: IO FilePath-getThemePath = (++ "/theme.ini") <$> getDir+getThemePath = (<> "/theme.ini") <$> getDir getConfigPath :: IO FilePath-getConfigPath = (++ "/config.ini") <$> getDir+getConfigPath = (<> "/config.ini") <$> getDir setup :: IO Config setup = do@@ -75,36 +88,21 @@ createConfig = create getConfigPath writeConfig configParser :: IniParser Config-configParser = do- generalCf <- General.parser- layoutCf <- Layout.parser- markdownCf <- Markdown.parser- trelloCf <- Trello.parser- githubCf <- GitHub.parser- return- Config- { general = generalCf- , layout = layoutCf- , markdown = markdownCf- , trello = trelloCf- , github = githubCf- }+configParser =+ Config <$> General.parser <*> Layout.parser <*> Markdown.parser <*> Trello.parser <*>+ GitHub.parser getConfig :: IO Config getConfig = do content <- getConfigPath >>= T.readFile- let config = parseIniFile content configParser- case config of- Right c -> return c- Left s -> putStrLn (pack $ "config.ini: " ++ s) >> return defaultConfig+ case parseIniFile content configParser of+ Right config -> pure config+ Left s -> putStrLn (pack $ "config.ini: " <> s) *> pure defaultConfig -- generate theme generateAttrMap :: IO AttrMap generateAttrMap = do- path <- getThemePath- customizedTheme <- loadCustomizations path defaultTheme+ customizedTheme <- flip loadCustomizations defaultTheme =<< getThemePath case customizedTheme of- Right theme -> return $ themeToAttrMap theme- Left s -> do- putStrLn (pack $ "theme.ini: " ++ s)- return $ themeToAttrMap defaultTheme+ Right theme -> pure $ themeToAttrMap theme+ Left s -> putStrLn (pack $ "theme.ini: " <> s) $> themeToAttrMap defaultTheme
src/IO/Config/General.hs view
@@ -20,4 +20,4 @@ "general" (do filenameCf <- maybe (filename defaultConfig) unpack . (noEmpty =<<) <$> fieldMb "filename"- return Config {filename = filenameCf})+ pure Config {filename = filenameCf})
src/IO/Config/GitHub.hs view
@@ -19,4 +19,4 @@ sectionMb "github" (do tokenCf <- fieldMb "token"- return Config {token = tokenCf})+ pure Config {token = tokenCf})
src/IO/Config/Layout.hs view
@@ -27,7 +27,7 @@ descriptionIndicatorCf <- fromMaybe (descriptionIndicator defaultConfig) . (noEmpty . parseText =<<) <$> fieldMb "description_indicator"- return+ pure Config { columnWidth = columnWidthCf , columnPadding = columnPaddingCf
src/IO/Config/Markdown.hs view
@@ -42,7 +42,7 @@ subtaskOutputCf <- fromMaybe (subtaskOutput defaultConfig) . (noEmpty . parseText =<<) <$> fieldMb "subtask"- return+ pure Config { titleOutput = titleOutputCf , taskOutput = taskOutputCf
src/IO/Config/Trello.hs view
@@ -19,4 +19,4 @@ sectionMb "trello" (do tokenCf <- fieldMb "token"- return Config {token = tokenCf})+ pure Config {token = tokenCf})
src/IO/HTTP/Aeson.hs view
@@ -15,4 +15,4 @@ deriveFromJSON = TH.deriveFromJSON defaultOptions {fieldLabelModifier = drop 1} parseError :: String -> Text-parseError err = decodeUtf8 $(embedFile "templates/api-error.txt") ++ "\n\n" ++ pack err+parseError err = decodeUtf8 $(embedFile "templates/api-error.txt") <> "\n\n" <> pack err
src/IO/HTTP/GitHub.hs view
@@ -51,10 +51,10 @@ headers :: ReaderGitHubToken [(HeaderName, ByteString)] headers = do token <- ask- return+ pure [ ("User-Agent", "smallhadroncollider/taskell") , ("Accept", "application/vnd.github.inertia-preview+json")- , ("Authorization", encodeUtf8 ("token " ++ token))+ , ("Authorization", encodeUtf8 ("token " <> token)) ] getNextLink :: [ByteString] -> Maybe Text@@ -62,7 +62,7 @@ lnks <- splitOn "," . decodeUtf8 <$> headMay bs let rel = "rel=\"next\"" next <- find (isSuffixOf rel) lnks- stripPrefix "<" =<< stripSuffix (">; " ++ rel) (strip next)+ stripPrefix "<" =<< stripSuffix (">; " <> rel) (strip next) fetch' :: [ByteString] -> Text -> ReaderGitHubToken (Int, [ByteString]) fetch' bs url = do@@ -70,9 +70,9 @@ rHeaders <- headers let request = initialRequest {requestHeaders = rHeaders} response <- lift $ httpBS request- let responses = bs ++ [getResponseBody response]+ let responses = bs <> [getResponseBody response] case getNextLink (getResponseHeader "Link" response) of- Nothing -> return (getResponseStatusCode response, responses)+ Nothing -> pure (getResponseStatusCode response, responses) Just lnk -> fetch' responses lnk fetch :: Text -> ReaderGitHubToken (Int, [ByteString])@@ -81,24 +81,24 @@ getCards :: Text -> ReaderGitHubToken (Either Text [Card]) getCards url = do (status, body) <- fetch url- return $+ pure $ case status of 200 -> case concatEithers (eitherDecodeStrict <$> body) of Right cards -> Right cards Left err -> Left (parseError err) 429 -> Left "Too many cards"- _ -> Left $ tshow status ++ " error while fetching " ++ url+ _ -> Left $ tshow status <> " error while fetching " <> url addCard :: Column -> ReaderGitHubToken (Either Text List) addCard column = do cards <- getCards $ column ^. cardsURL- return $ columnToList column <$> cards+ pure $ columnToList column <$> cards addCards :: [Column] -> ReaderGitHubToken (Either Text Lists) addCards columns = do cols <- sequence (addCard <$> columns)- return $ fromList <$> sequence cols+ pure $ fromList <$> sequence cols getColumns :: Text -> ReaderGitHubToken (Either Text Lists) getColumns url = do@@ -108,10 +108,10 @@ 200 -> case concatEithers (eitherDecodeStrict <$> body) of Right columns -> addCards columns- Left err -> return $ Left (parseError err)- 404 -> return . Left $ "Could not find GitHub project ."- 401 -> return . Left $ "You do not have permission to view GitHub project " ++ url- _ -> return . Left $ tshow status ++ " error. Cannot fetch columns from GitHub."+ Left err -> pure $ Left (parseError err)+ 404 -> pure . Left $ "Could not find GitHub project ."+ 401 -> pure . Left $ "You do not have permission to view GitHub project " <> url+ _ -> pure . Left $ tshow status <> " error. Cannot fetch columns from GitHub." printProjects :: Seq Project -> Text printProjects projects = unlines $ toList display@@ -127,7 +127,7 @@ chosen <- lift $ prompt "Import project" let project = (projects' !?) =<< (-) 1 <$> readMay chosen case project of- Nothing -> return $ Left "Invalid project selected"+ Nothing -> pure $ Left "Invalid project selected" Just proj -> getColumns (proj ^. columnsURL) getLists :: GitHubIdentifier -> ReaderGitHubToken (Either Text Lists)@@ -140,13 +140,13 @@ case concatEithers (eitherDecodeStrict <$> body) of Right projects -> if null projects- then return . Left $ concat ["\nNo projects found for ", identifier, "\n"]+ then pure . Left $ concat ["\nNo projects found for ", identifier, "\n"] else chooseProject projects- Left err -> return $ Left (parseError err)+ Left err -> pure $ Left (parseError err) 404 ->- return . Left $+ pure . Left $ "Could not find GitHub org/repo. For organisation make sure you use 'orgs/<org-name>' and for repos use 'repos/<username>/<repo-name>'" 401 ->- return . Left $- "You do not have permission to view the GitHub projects for " ++ identifier- _ -> return . Left $ tshow status ++ " error. Cannot fetch projects from GitHub."+ pure . Left $+ "You do not have permission to view the GitHub projects for " <> identifier+ _ -> pure . Left $ tshow status <> " error. Cannot fetch projects from GitHub."
src/IO/HTTP/Trello.hs view
@@ -39,7 +39,7 @@ fullURL :: Text -> ReaderTrelloToken String fullURL uri = do token <- ask- return . unpack $ concat [root, uri, "&key=", key, "&token=", token]+ pure . unpack $ concat [root, uri, "&key=", key, "&token=", token] boardURL :: TrelloBoardID -> ReaderTrelloToken String boardURL board =@@ -64,20 +64,20 @@ fetch url = do request <- parseRequest url response <- httpBS request- return (getResponseStatusCode response, getResponseBody response)+ pure (getResponseStatusCode response, getResponseBody response) getChecklist :: TrelloChecklistID -> ReaderTrelloToken (Either Text [ChecklistItem]) getChecklist checklist = do url <- checklistURL checklist (status, body) <- lift $ fetch url- return $+ pure $ case status of 200 -> case (^. checkItems) <$> eitherDecodeStrict body of Right ls -> Right ls Left err -> Left $ parseError err 429 -> Left "Too many checklists"- _ -> Left $ tshow status ++ " error while fetching checklist " ++ checklist+ _ -> Left $ tshow status <> " error while fetching checklist " <> checklist updateCard :: Card -> ReaderTrelloToken (Either Text Card) updateCard card = (setChecklists card . concat <$>) . sequence <$> checklists@@ -100,9 +100,9 @@ 200 -> case eitherDecodeStrict body of Right raw -> fmap (trelloListsToLists timezone) <$> getChecklists raw- Left err -> return . Left $ parseError err+ Left err -> pure . Left $ parseError err 404 ->- return . Left $- "Could not find Trello board " ++ board ++ ". Make sure the ID is correct"- 401 -> return . Left $ "You do not have permission to view Trello board " ++ board- _ -> return . Left $ tshow status ++ " error. Cannot fetch from Trello."+ pure . Left $+ "Could not find Trello board " <> board <> ". Make sure the ID is correct"+ 401 -> pure . Left $ "You do not have permission to view Trello board " <> board+ _ -> pure . Left $ tshow status <> " error. Cannot fetch from Trello."
src/IO/Markdown/Internal.hs view
@@ -77,7 +77,7 @@ Just (Just set) -> (set current, errs) _ -> if not (null (strip text))- then (current, errs ++ [line])+ then (current, errs <> [line]) else (current, errs) decodeError :: String -> Maybe Word8 -> Maybe Char@@ -91,12 +91,12 @@ let (lists, errs) = foldl' fn acc $ zip lns [1 ..] if null errs then Right lists- else Left $ "could not parse line(s) " ++ intercalate ", " (tshow <$> errs)+ else Left $ "could not parse line(s) " <> intercalate ", " (tshow <$> errs) -- stringify code subtaskStringify :: Config -> Text -> ST.Subtask -> Text subtaskStringify config t st =- foldl' (++) t [subtaskOutput config, " ", pre, " ", st ^. ST.name, "\n"]+ foldl' (<>) t [subtaskOutput config, " ", pre, " ", st ^. ST.name, "\n"] where pre = if st ^. ST.complete@@ -117,7 +117,7 @@ taskStringify :: Config -> Text -> T.Task -> Text taskStringify config s t = foldl'- (++)+ (<>) s [ nameStringify config (t ^. T.name) , maybe "" (dueStringify config) (t ^. T.due)@@ -128,7 +128,7 @@ listStringify :: Config -> Text -> List -> Text listStringify config text list = foldl'- (++)+ (<>) text [ if null text then ""
src/IO/Taskell.hs view
@@ -33,14 +33,14 @@ | Exit parseArgs :: [Text] -> ReaderConfig Next-parseArgs ["-v"] = return $ Output version-parseArgs ["-h"] = return $ Output usage+parseArgs ["-v"] = pure $ Output version+parseArgs ["-h"] = pure $ Output usage parseArgs ["-t", boardID, file] = loadTrello boardID file parseArgs ["-g", identifier, file] = loadGitHub identifier file parseArgs ["-i", file] = fileInfo file parseArgs [file] = loadFile file parseArgs [] = (pack . filename . general <$> ask) >>= loadFile-parseArgs _ = return $ Output (unlines ["Invalid options", "", usage])+parseArgs _ = pure $ Output (unlines ["Invalid options", "", usage]) load :: ReaderConfig Next load = getArgs >>= parseArgs@@ -49,20 +49,20 @@ loadFile filepath = do mPath <- exists filepath case mPath of- Nothing -> return Exit+ Nothing -> pure Exit Just path -> do content <- readData path- return $+ pure $ case content of Right lists -> Load path lists- Left err -> Output $ pack path ++ ": " ++ err+ Left err -> Output $ pack path <> ": " <> err loadRemote :: (token -> FilePath -> ReaderConfig Next) -> token -> Text -> ReaderConfig Next loadRemote createFn identifier filepath = do let path = unpack filepath exists' <- fileExists path if exists'- then return $ Output (filepath ++ " already exists")+ then pure $ Output (filepath <> " already exists") else createFn identifier path loadTrello :: Trello.TrelloBoardID -> Text -> ReaderConfig Next@@ -78,11 +78,11 @@ if exists' then do content <- readData path- return $+ pure $ case content of Right lists -> Output $ analyse filepath lists- Left err -> Output $ pack path ++ ": " ++ err- else return Exit+ Left err -> Output $ pack path <> ": " <> err+ else pure Exit createRemote :: (Config -> Maybe token)@@ -95,18 +95,18 @@ config <- ask let maybeToken = tokenFn config case maybeToken of- Nothing -> return $ Output missingToken+ Nothing -> pure $ Output missingToken Just token -> do lists <- lift $ runReaderT (getFn identifier) token case lists of- Left txt -> return $ Output txt+ Left txt -> pure $ Output txt Right ls -> do create <- promptCreate path if create then do lift $ writeData config ls path- return $ Load path ls- else return Exit+ pure $ Load path ls+ else pure Exit createTrello :: Trello.TrelloBoardID -> FilePath -> ReaderConfig Next createTrello =@@ -127,14 +127,14 @@ let path = unpack filepath exists' <- fileExists path if exists'- then return $ Just path+ then pure $ Just path else do create <- promptCreate path if create then do createPath path- return $ Just path- else return Nothing+ pure $ Just path+ else pure Nothing fileExists :: FilePath -> ReaderConfig Bool fileExists path = lift $ doesFileExist path@@ -159,4 +159,4 @@ readData path = do config <- ask content <- readFile path- return $ parse config content+ pure $ parse config content
src/UI/CLI.hs view
@@ -2,22 +2,17 @@ {-# LANGUAGE OverloadedStrings #-} module UI.CLI- ( prompt- , promptYN- ) where+ ( prompt+ , promptYN+ ) where -import ClassyPrelude-import Data.Text.IO (hPutStrLn)-import System.Exit (exitFailure)+import ClassyPrelude prompt :: Text -> IO Text prompt s = do- putStr $ s ++ ": "- hFlush stdout -- prevents buffering- getLine+ putStr $ s <> ": "+ hFlush stdout -- prevents buffering+ getLine promptYN :: Text -> IO Bool-promptYN s = (==) "y" <$> prompt (s ++ " (y/n)")--exitWithErrorMessage :: Text -> IO ()-exitWithErrorMessage str = hPutStrLn stderr str >> void exitFailure+promptYN s = (==) "y" <$> prompt (s <> " (y/n)")
src/UI/Draw.hs view
@@ -51,7 +51,7 @@ today <- dsToday <$> ask -- get the value of `today` from DrawState let attr = withAttr . dlToAttr . deadline today <$> dueDay -- create a `Maybe (Widget -> Widget)` attribute function widget = txt . dayToText today <$> dueDay -- get the formatted due date `Maybe Text`- return $ attr <*> widget+ pure $ attr <*> widget -- | Renders the appropriate completed sub task count e.g. "[2/3]" renderSubtaskCount :: T.Task -> Widget ResourceName@@ -63,7 +63,7 @@ indicators task = do dateWidget <- renderDate (task ^. T.due) -- get the due date widget descIndicator <- descriptionIndicator . dsLayout <$> ask- return . hBox $+ pure . hBox $ padRight (Pad 1) <$> catMaybes [ const (txt descIndicator) <$> task ^. T.description -- show the description indicator if one is set@@ -82,7 +82,7 @@ name = RNTask (listIndex, taskIndex) widget = textField text widget' = widgetFromMaybe widget taskField- return $+ pure $ cached name . (if selected && not eTitle then visible@@ -104,15 +104,15 @@ if moveTo m then do let col = chr (i + ord 'a')- return $+ pure $ if i /= selectedList && i >= 0 && i <= 26- then singleton col ++ ". "+ then singleton col <> ". " else "" else do let col = i + 1- return $+ pure $ if col >= 1 && col <= 9- then tshow col ++ ". "+ then tshow col <> ". " else "" -- | Renders the title for a list@@ -134,7 +134,7 @@ if editing then widget' else widget- return $+ pure $ if editing || selectedList /= listIndex || selectedTask == 0 then visible title' else title'@@ -155,7 +155,7 @@ hLimit (columnWidth layout) . viewport (RNList listIndex) Vertical . vBox . (titleWidget :) $ toList taskWidgets- return $+ pure $ if currentList == listIndex then visible widget else widget@@ -173,8 +173,8 @@ then taskCurrentAttr else taskAttr let widget = attr . padTopBottom 1 . padLeftRight colPad $ txt "/" <+> field searchField- return $ mainWidget <=> widget- _ -> return mainWidget+ pure $ mainWidget <=> widget+ _ -> pure mainWidget -- | Renders the main widget main :: ReaderDrawState (Widget ResourceName)
src/UI/Field.hs view
@@ -48,13 +48,13 @@ let (start, end) = T.splitAt cursor text in case fromNullable start of Nothing -> Field end cursor- Just start' -> Field (init start' ++ end) (cursor - 1)+ Just start' -> Field (init start' <> end) (cursor - 1) insertCharacter :: Char -> Field -> Field insertCharacter char (Field text cursor) = Field newText newCursor where (start, end) = T.splitAt cursor text- newText = snoc start char ++ end+ newText = snoc start char <> end newCursor = cursor + 1 insertText :: Text -> Field -> Field@@ -112,10 +112,10 @@ spl' :: [Text] -> Char -> [Text] spl' ts c- | c == ' ' = ts ++ [" "] ++ [""]+ | c == ' ' = ts <> [" "] <> [""] | otherwise = case fromNullable ts of- Just ts' -> init ts' ++ [snoc (last ts') c]+ Just ts' -> init ts' <> [snoc (last ts') c] Nothing -> [singleton c] spl :: Text -> [Text]@@ -125,8 +125,8 @@ combine width (acc, offset) s | newline && s == " " = (acc, offset + 1) | T.takeEnd 1 l == " " && s == " " = (acc, offset + 1)- | newline = (acc ++ [s], offset)- | otherwise = (append (l ++ s) acc, offset)+ | newline = (acc <> [s], offset)+ | otherwise = (append (l <> s) acc, offset) where l = maybe "" last (fromNullable acc) newline = B.textWidth l + B.textWidth s > width@@ -134,5 +134,5 @@ append :: Text -> [Text] -> [Text] append s l = case fromNullable l of- Just l' -> init l' ++ [s]- Nothing -> l ++ [s]+ Just l' -> init l' <> [s]+ Nothing -> l <> [s]
src/UI/Modal/Detail.hs view
@@ -81,4 +81,4 @@ w | null sts = withAttr disabledAttr $ txt "No sub-tasks" | otherwise = vBox . toList $ renderSubtask f i `mapWithIndex` sts- return (task ^. name, renderDate today f i task <=> renderSummary f i task <=> w)+ pure (task ^. name, renderDate today f i task <=> renderSummary f i task <=> w)
src/UI/Modal/MoveTo.hs view
@@ -27,6 +27,6 @@ letter a = padRight (Pad 1) . hBox $ [txt "[", withAttr taskCurrentAttr $ txt (singleton a), txt "]"] letters = letter <$> ['a' ..]- remove i l = take i l ++ drop (i + 1) l+ remove i l = take i l <> drop (i + 1) l output (l, t) = l <+> t widget = vBox $ output <$> remove skip (zip letters titles)
taskell.cabal view
@@ -1,6 +1,6 @@ cabal-version: 1.12 name: taskell-version: 1.3.5.0+version: 1.3.6.0 license: BSD3 license-file: LICENSE copyright: 2019 Mark Wales