diff --git a/LICENSE b/LICENSE index bb6bec8..bcb3972 100755 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,21 @@ -MIT License - -Copyright (c) 2018 m455 - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +MIT License + +Copyright (c) 2018 m455 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 7a0e56a..9d36b5f 100755 --- a/README.md +++ b/README.md @@ -1,111 +1,111 @@ -# rodo - -A simple to-do list tool for people who live on the command-line - -By: Jesse Laprade - -![](screenshot.png) - -# Table of Contents - -* [Legend](https://github.com/m455/rodo#legend) -* [Platforms](https://github.com/m455/rodo#platforms) -* [Requirements](https://github.com/m455/rodo#requirements) -* [Download](https://github.com/m455/rodo#download) - * [Via Browser](https://github.com/m455/rodo#via-browser) - * [Via Git](https://github.com/m455/rodo#via-git) -* [Setup](https://github.com/m455/rodo#setup) - * [GNU/Linux](https://github.com/m455/rodo#gnulinux) - * [Setup a $PATH](https://github.com/m455/rodo#setup-a-path) - * [Using the binary](https://github.com/m455/rodo#using-the-binary) - * [Creating a wrapper](https://github.com/m455/rodo#creating-a-wrapper) -* [Usage](https://github.com/m455/rodo#usage) -* [Examples](https://github.com/m455/rodo#examples) -* [Configuration](https://github.com/m455/rodo#configuring-rodo) - -## Legend - -`Items marked like this` are either instructions for running on the command line or code. Do not include the initial `$` when running them. - -**Items marked like this** are important. - -## Platforms - -* GNU/Linux - -## Requirements - -* [Racket 6.x](https://racket-lang.org/) -* [Git (Optional)](https://git-scm.com/) - -## Download - -### Via Browser - -Download this repository by clicking the **Clone or download** button at the top right and then choosing **Download ZIP** from the drop-down list - -### Via Git - -Run `git clone https://github.com/m455/rodo` at the command line if you use Git - -## Setup - -### GNU/Linux - -#### Setup a $PATH - -1. Create a directory for your `$PATH` by running `mkdir ~/bin/` - -2. Associate your `$PATH` with the ~/bin/ folder you created by running `echo "export PATH=~/bin:\$PATH" >> .bashrc` - -#### Using the binary - -1. Copy the rodo binary file to your `$PATH` folder by running `cp /path/to/rodo ~/bin/` - -2. Make the rodo binary file executable by running `chmod u+x ~/bin/rodo` - -#### Creating a wrapper - -1. Create an empty wrapper file by running `touch ~/bin/rodo`. - -2. Add the following contents show below to it - -``` -#!/usr/bin/env bash -racket ~/path/to/rodo.rkt "$@" -``` -3. Save the file - -For example, if you downloaded the project to your ~/downloads/ folder you would change the line `racket ~/path/to/rodo.rkt "$@"` to `racket ~/downloads/rodo/rodo.rkt "$@"` - -2. Make the rodo wrapper file executable by running`chmod u+x ~/bin/rodo` - -## Usage - -init - Initializes a file in ~/.rodo/todo-list by default - -ls - Lists items from the list - -add - Adds an entry to the list - -rm - Removes an item from the list - -**Note:** You may have to run `rodo ls` to see which number corresponds to which item when removing items. - -## Examples - -The examples below assume that you have rodo [set up](https://github.com/m455/rodo#setup-a-path) in your `$PATH` - -init - `rodo init` - -ls - `rodo ls` - -add (Single-word entry) - `rodo add bread` - -add (Multi-word entry) - `rodo add "go to the bank"` - -rm - `rodo rm 1` - -## Configuring rodo - -Right now, the configurations can be found in the config.rkt file. Settings such at program name, path and directory can be set here. +# rodo + +A simple to-do list tool for people who live on the command-line + +By: Jesse Laprade + +![](screenshot.png) + +# Table of Contents + +* [Legend](https://github.com/m455/rodo#legend) +* [Platforms](https://github.com/m455/rodo#platforms) +* [Requirements](https://github.com/m455/rodo#requirements) +* [Download](https://github.com/m455/rodo#download) + * [Via Browser](https://github.com/m455/rodo#via-browser) + * [Via Git](https://github.com/m455/rodo#via-git) +* [Setup](https://github.com/m455/rodo#setup) + * [GNU/Linux](https://github.com/m455/rodo#gnulinux) + * [Setup a $PATH](https://github.com/m455/rodo#setup-a-path) + * [Using the binary](https://github.com/m455/rodo#using-the-binary) + * [Creating a wrapper](https://github.com/m455/rodo#creating-a-wrapper) +* [Usage](https://github.com/m455/rodo#usage) +* [Examples](https://github.com/m455/rodo#examples) +* [Configuration](https://github.com/m455/rodo#configuring-rodo) + +## Legend + +`Items marked like this` are either instructions for running on the command line or code. Do not include the initial `$` when running them. + +**Items marked like this** are important. + +## Platforms + +* GNU/Linux + +## Requirements + +* [Racket 6.x](https://racket-lang.org/) +* [Git (Optional)](https://git-scm.com/) + +## Download + +### Via Browser + +Download this repository by clicking the **Clone or download** button at the top right and then choosing **Download ZIP** from the drop-down list + +### Via Git + +Run `git clone https://github.com/m455/rodo` at the command line if you use Git + +## Setup + +### GNU/Linux + +#### Setup a $PATH + +1. Create a directory for your `$PATH` by running `mkdir ~/bin/` + +2. Associate your `$PATH` with the ~/bin/ folder you created by running `echo "export PATH=~/bin:\$PATH" >> .bashrc` + +#### Using the binary + +1. Copy the rodo binary file to your `$PATH` folder by running `cp /path/to/rodo ~/bin/` + +2. Make the rodo binary file executable by running `chmod u+x ~/bin/rodo` + +#### Creating a wrapper + +1. Create an empty wrapper file by running `touch ~/bin/rodo`. + +2. Add the following contents show below to it + +``` +#!/usr/bin/env bash +racket ~/path/to/rodo.rkt "$@" +``` +3. Save the file + +For example, if you downloaded the project to your ~/downloads/ folder you would change the line `racket ~/path/to/rodo.rkt "$@"` to `racket ~/downloads/rodo/rodo.rkt "$@"` + +2. Make the rodo wrapper file executable by running`chmod u+x ~/bin/rodo` + +## Usage + +init - Initializes a file in ~/.rodo/todo-list by default + +ls - Lists items from the list + +add - Adds an entry to the list + +rm - Removes an item from the list + +**Note:** You may have to run `rodo ls` to see which number corresponds to which item when removing items. + +## Examples + +The examples below assume that you have rodo [set up](https://github.com/m455/rodo#setup-a-path) in your `$PATH` + +init - `rodo init` + +ls - `rodo ls` + +add (Single-word entry) - `rodo add bread` + +add (Multi-word entry) - `rodo add "go to the bank"` + +rm - `rodo rm 1` + +## Configuring rodo + +Right now, the configurations can be found in the config.rkt file. Settings such at program name, path and directory can be set here. diff --git a/args.rkt b/args.rkt index 568a6ef..5e4d56f 100644 --- a/args.rkt +++ b/args.rkt @@ -1,47 +1,44 @@ -#lang racket/base - -(require racket/vector - racket/list - "config.rkt" - "init.rkt" - "util.rkt" - "messages.rkt") - -(provide (all-defined-out)) - -(define (check-args args) - (let - ([args-length (vector-length args)]) - (cond - [(equal? args-length 0) - (d-hash-ref messages 'show-usage)] - - [(and - (equal? args-length 1) - (equal? (vector-member list-command args) 0)) - (show-list)] - - [(and - (equal? args-length 2) - (equal? (vector-ref args 0) add-command)) - (add-item args)] - - [(and - (equal? args-length 2) - (equal? (vector-member remove-command args) 0) - (not (equal? (vector-member "0" args) 1)) - (vector-member (vector-ref args 1) (list->vector (map number->string (rest (range (length (file->string-list path)))))))) - (remove-item args)] - - [(and - (equal? args-length 1) - (equal? (vector-member initialize-command args) 0)) - (initialize)] - - [(and - (equal? args-length 1) - (member (vector-ref args 0) help-command)) - (d-hash-ref messages 'show-help)] - - [else - (d-hash-ref messages 'show-usage)]))) +#lang racket/base + +(require (prefix-in vector: racket/vector) + (prefix-in list: racket/list) + (prefix-in config: "config.rkt") + (prefix-in init: "init.rkt") + (prefix-in util: "util.rkt") + (prefix-in messages: "messages.rkt")) + +(provide (all-defined-out)) + +(define (check-args args) + (let + ([args-length (vector-length args)]) + (cond + [(equal? args-length 0) + (util:display-hash-ref messages:messages 'show-usage)] + [(and + (equal? args-length 1) + (equal? (vector:vector-member config:list-command args) 0)) + (util:show-list)] + [(and + (equal? args-length 2) + (equal? (vector-ref args 0) config:add-command)) + (util:add-item args)] + [(and + (equal? args-length 2) + (equal? (vector:vector-member config:remove-command args) 0) + (not (equal? (vector:vector-member "0" args) 1)) + (vector:vector-member + (vector-ref args 1) + (list->vector + (map number->string (list:rest (list:range (length (util:file->string-list config:path)))))))) + (util:remove-item args)] + [(and + (equal? args-length 1) + (equal? (vector:vector-member config:initialize-command args) 0)) + (init:initialize)] + [(and + (equal? args-length 1) + (member (vector-ref args 0) config:help-command)) + (util:display-hash-ref messages:messages 'show-help)] + [else + (util:display-hash-ref messages:messages 'show-usage)]))) diff --git a/config.rkt b/config.rkt index 7c3fb23..ecd9548 100644 --- a/config.rkt +++ b/config.rkt @@ -1,19 +1,19 @@ -#lang racket/base -(require racket/file) -(provide (all-defined-out)) - -(define program-name "rodo") -(define program-directory ".rodo/") -(define program-path "~/") -(define program-file "todo-list") -(define remove-command "rm") -(define add-command "add") -(define list-command "ls") -(define initialize-command "init") -(define help-command '("-h" "--help")) -(define path - (expand-user-path - (string-append - program-path - program-directory - program-file))) +#lang racket/base +(require racket/file) +(provide (all-defined-out)) + +(define program-name "rodo") +(define program-directory ".rodo/") +(define program-path "~/") +(define program-file "todo-list") +(define remove-command "rm") +(define add-command "add") +(define list-command "ls") +(define initialize-command "init") +(define help-command '("-h" "--help")) +(define path + (expand-user-path + (string-append + program-path + program-directory + program-file))) diff --git a/init.rkt b/init.rkt index 020bedb..4da065d 100644 --- a/init.rkt +++ b/init.rkt @@ -1,47 +1,46 @@ -#lang racket/base - -(require racket/file - "config.rkt" - "util.rkt" - "messages.rkt" - "io.rkt") - -(provide (all-defined-out)) - -(define (initialize-file) - (display-to-file - "--Do not edit this file--\n" - path - #:mode 'text - #:exists 'replace)) - -(define (init-prompt hash-list key) - (d-hash-ref hash-list key) - (display "> ") - (let - ([user-input (read-line)]) - (cond - [(member user-input (hash-ref y/n 'yes)) - (d-hash-ref messages 'creating-folder) - (d-hash-ref messages 'creating-file) - (create-folder) - (create-file) - (initialize-file) - (if - (and - (check-for-folder) - (check-for-file)) - (d-hash-ref messages 'successfully-created) - (d-hash-ref messages 'creation-error))] - - [(member user-input (hash-ref y/n 'no)) - (d-hash-ref messages 'terminating)] - - [else - (init-prompt messages 'choose-y/n)]))) - -(define (initialize) - (if (check-for-file) - (d-hash-ref messages 'file-already-exists) - (begin - (init-prompt messages 'init-y/n)))) +#lang racket/base + +(require racket/file + "config.rkt" + "util.rkt" + "messages.rkt") + +(provide (all-defined-out)) + +(define (initialize-file) + (display-to-file + "--Do not edit this file--\n" + path + #:mode 'text + #:exists 'replace)) + +(define (init-prompt hash-list key) + (display-hash-ref hash-list key) + (display "> ") + (let + ([user-input (read-line)]) + (cond + [(member user-input (hash-ref y/n 'yes)) + (display-hash-ref messages 'creating-folder) + (display-hash-ref messages 'creating-file) + (create-folder) + (create-file) + (initialize-file) + (if + (and + (check-for-folder) + (check-for-file)) + (display-hash-ref messages 'successfully-created) + (display-hash-ref messages 'creation-error))] + + [(member user-input (hash-ref y/n 'no)) + (display-hash-ref messages 'terminating)] + + [else + (init-prompt messages 'choose-y/n)]))) + +(define (initialize) + (if (check-for-file) + (display-hash-ref messages 'file-already-exists) + (begin + (init-prompt messages 'init-y/n)))) diff --git a/io.rkt b/io.rkt deleted file mode 100644 index 86373aa..0000000 --- a/io.rkt +++ /dev/null @@ -1,32 +0,0 @@ -#lang racket/base - -(require racket/file - "config.rkt") - -(provide (all-defined-out)) - -(define (check-for-file) - (file-exists? path)) - -(define (create-file) - (let - ([opened-file - (open-output-file - path - #:mode 'text - #:exists 'can-update)]) - (close-output-port opened-file))) - -(define (check-for-folder) - (directory-exists? - (expand-user-path - (string-append - program-path - program-directory)))) - -(define (create-folder) - (make-directory - (expand-user-path - (string-append - program-path - program-directory)))) diff --git a/messages.rkt b/messages.rkt index 3e21ceb..297c8ef 100644 --- a/messages.rkt +++ b/messages.rkt @@ -1,145 +1,145 @@ -#lang racket/base - -(require "config.rkt") - -(provide (all-defined-out)) - -(define messages - (hash - 'show-help - (string-append - "* " initialize-command ": " - "initialize a file in " - program-path - program-directory - program-file - "\n" - "\x09Example: " - "rodo init\n\n" - - "* " list-command ": " - "lists items on the list" - "\n" - "\x09Example: " - "rodo ls\n\n" - - "* " add-command ": " - "adds an item to the list" - "\n" - "\x09Example: " - "rodo add bread\n\n" - "\x09Note: For multi-word items you will need to\n" - "\x09surround your item in double quotes as so:\n" - "\x09rodo add \"go to the bank\"\n" - - "* " remove-command ": " - "removes an item from the list\n" - "\x09Example: " - "rodo rm 1\n" - "\x09Note: You may have to run `rodo ls` to see which\n" - "\x09number corresponds to which item to remove it.\n") - - 'empty-todo-list - "> There is nothing in your list \n" - - 'show-usage - (string-append - "> For usage type " - "`" program-name " -h`" - " or " - "`" program-name " --help`\n") - - 'creating-folder - (string-append - "> creating a " - program-directory - " folder in " - program-path " ...\n") - - 'creating-file - (string-append - "> creating a " - program-file - " file in " - program-path - program-directory " ...\n") - - 'creation-error - (string-append - "> Error: Could not create " - program-file - " in " - program-directory - program-path ".\n" - "> This may be due to directory permissions\n") - - 'file-already-exists - (string-append - "> Error: " - program-name - " already exists in " - program-path - program-directory - program-file "\n") - - 'successfully-created - (string-append - "> " - program-path - program-directory - program-file - " has been successfully created\n") - - 'file-not-found - (string-append - "> Error: Could not find " - program-path - program-directory - program-file "\n") - - 'init-y/n - (string-append - "> A " - program-file - " file will be created in " - program-path - program-directory "\n" - "> Are you sure you want to continue? [y/n]\n") - - 'try-init - (string-append - "> Try typing " - "`" program-name " init` " - "to set it up\n") - - 'terminating - (string-append - "> Exiting " - program-name - " ...\n") - - 'choose-y/n - "> Error: Please choose y or n\n" - - 'not-in-list - "> Error: Item does not exist\n" - - 'item-added-prefix - "> Added " - - 'item-added-suffix - " to list\n" - - 'item-removed-prefix - "> Removed " - - 'item-removed-suffix - " from list\n")) - -(define y/n - (hash - 'yes - '("yes" "Yes" "y" "Y") - - 'no - '("no" "No" "n" "N"))) +#lang racket/base + +(require "config.rkt") + +(provide (all-defined-out)) + +(define messages + (hash + 'show-help + (string-append + "* " initialize-command ": " + "initialize a file in " + program-path + program-directory + program-file + "\n" + "\x09Example: " + "rodo init\n\n" + + "* " list-command ": " + "lists items on the list" + "\n" + "\x09Example: " + "rodo ls\n\n" + + "* " add-command ": " + "adds an item to the list" + "\n" + "\x09Example: " + "rodo add bread\n\n" + "\x09Note: For multi-word items you will need to\n" + "\x09surround your item in double quotes as so:\n" + "\x09rodo add \"go to the bank\"\n" + + "* " remove-command ": " + "removes an item from the list\n" + "\x09Example: " + "rodo rm 1\n" + "\x09Note: You may have to run `rodo ls` to see which\n" + "\x09number corresponds to which item to remove it.\n") + + 'empty-todo-list + "> There is nothing in your list \n" + + 'show-usage + (string-append + "> For usage type " + "`" program-name " -h`" + " or " + "`" program-name " --help`\n") + + 'creating-folder + (string-append + "> creating a " + program-directory + " folder in " + program-path " ...\n") + + 'creating-file + (string-append + "> creating a " + program-file + " file in " + program-path + program-directory " ...\n") + + 'creation-error + (string-append + "> Error: Could not create " + program-file + " in " + program-directory + program-path ".\n" + "> This may be due to directory permissions\n") + + 'file-already-exists + (string-append + "> Error: " + program-name + " already exists in " + program-path + program-directory + program-file "\n") + + 'successfully-created + (string-append + "> " + program-path + program-directory + program-file + " has been successfully created\n") + + 'file-not-found + (string-append + "> Error: Could not find " + program-path + program-directory + program-file "\n") + + 'init-y/n + (string-append + "> A " + program-file + " file will be created in " + program-path + program-directory "\n" + "> Are you sure you want to continue? [y/n]\n") + + 'try-init + (string-append + "> Try typing " + "`" program-name " init` " + "to set it up\n") + + 'terminating + (string-append + "> Exiting " + program-name + " ...\n") + + 'choose-y/n + "> Error: Please choose y or n\n" + + 'not-in-list + "> Error: Item does not exist\n" + + 'item-added-prefix + "> Added " + + 'item-added-suffix + " to list\n" + + 'item-removed-prefix + "> Removed " + + 'item-removed-suffix + " from list\n")) + +(define y/n + (hash + 'yes + '("yes" "Yes" "y" "Y") + + 'no + '("no" "No" "n" "N"))) diff --git a/rodo b/rodo deleted file mode 100755 index b67fb3c..0000000 Binary files a/rodo and /dev/null differ diff --git a/rodo.rkt b/rodo.rkt index 04f7b96..5263262 100755 --- a/rodo.rkt +++ b/rodo.rkt @@ -1,9 +1,9 @@ -#! /usr/bin/env racket -#lang racket/base - -(require "args.rkt") - -(define (main) - (check-args (current-command-line-arguments))) - -(main) +#! /usr/bin/env racket +#lang racket/base + +(require (prefix-in args: "args.rkt")) + +(define (main) + (args:check-args (current-command-line-arguments))) + +(main) diff --git a/util.rkt b/util.rkt index 3259ac6..1644406 100644 --- a/util.rkt +++ b/util.rkt @@ -1,116 +1,134 @@ -#lang racket/base - -(require racket/list - racket/file - racket/string - "config.rkt" - "io.rkt" - "messages.rkt") - -(provide (all-defined-out)) - -(define (d-hash-ref hash-list key) - (display (hash-ref hash-list key))) - -(define (d-vector-ref args key) - (display (vector-ref args key))) - -(define (file->string-list path-to-file) - (let - ([todo-list - (file->lines - path-to-file - #:mode 'text - #:line-mode 'any)]) - todo-list)) - -(define (list-empty? lst) - (empty? (rest (file->string-list lst)))) - -(define (get-removed-item lst args) - (list-ref lst (string->number args))) - -(define (quote-item args) - (display - (string-append "\"" args "\""))) - -(define (number-list lst) - (map string-append - (map number->string (rest (range (length lst)))) - (rest lst))) - -(define (indent-list lst) - (string-append ". " lst)) - -(define (prettify-list) - (display - (string-join - (number-list (map indent-list (file->string-list path))) - "\n" - #:after-last "\n"))) - -(define (show-list) - (cond - [(and - (check-for-folder) - (check-for-file)) - (if - (list-empty? path) - (d-hash-ref messages 'empty-todo-list) - (prettify-list))] - [else - (d-hash-ref messages 'file-not-found) - (d-hash-ref messages 'try-init)])) - -(define (add-item-to-file args) - (let ([new-list - (reverse - (cons args - (reverse (file->string-list path))))]) - (display-to-file - (string-join new-list "\n" #:after-last "\n") - path - #:mode 'text - #:exists 'replace) - (d-hash-ref messages 'item-added-prefix) - (quote-item args) - (d-hash-ref messages 'item-added-suffix))) - -(define (add-item args) - (if - (and - (check-for-folder) - (check-for-file)) - (add-item-to-file (vector-ref args 1)) - (begin - (d-hash-ref messages 'file-not-found) - (d-hash-ref messages 'try-init)))) - -(define (remove-item-from-file args) - (let ([removed-item - (get-removed-item (file->string-list path) args)] - [new-list - (remove - (list-ref (file->string-list path) (string->number args)) - (file->string-list path))]) - (display-to-file - (string-join new-list "\n" #:after-last "\n") - path - #:mode 'text - #:exists 'replace) - (d-hash-ref messages 'item-removed-prefix) - (quote-item removed-item) - (d-hash-ref messages 'item-removed-suffix))) - -(define (remove-item args) - (cond - [(list-empty? path) - (d-hash-ref messages 'empty-todo-list)] - [(and - (check-for-folder) - (check-for-file)) - (remove-item-from-file (vector-ref args 1))] - [(and (not (check-for-folder)) (not (check-for-file))) - (begin - (d-hash-ref messages 'file-not-found) - (d-hash-ref messages 'try-init))])) +#lang racket/base + +(require racket/list + racket/file + racket/string + "config.rkt" + "messages.rkt") + +(provide (all-defined-out)) + +(define (check-for-file) + (file-exists? path)) + +(define (create-file) + (let ([opened-file + (open-output-file path + #:mode 'text + #:exists 'can-update)]) + (close-output-port opened-file))) + +(define (check-for-folder) + (directory-exists? (expand-user-path + (string-append + program-path + program-directory)))) + +(define (create-folder) + (make-directory (expand-user-path + (string-append + program-path + program-directory)))) + +(define (display-hash-ref hash-list key) + (display (hash-ref hash-list key))) + +(define (d-vector-ref args key) + (display (vector-ref args key))) + +(define (file->string-list path-to-file) + (let ([todo-list (file->lines path-to-file + #:mode 'text + #:line-mode 'any)]) + todo-list)) + +(define (list-empty? lst) + (empty? (rest (file->string-list lst)))) + +(define (get-removed-item lst args) + (list-ref lst (string->number args))) + +(define (quote-item args) + (display + (string-append "\"" args "\""))) + +(define (prefix-with-number lst) + (map string-append + (map number->string (rest (range (length lst)))) + (rest lst))) + +(define (prefix-with-period lst) + (string-append ". " lst)) + +(define (prettify-list) + (display + (string-join (prefix-with-number (map prefix-with-period (file->string-list path))) + "\n" + #:after-last "\n"))) + +(define (append-to-end args lst) + (reverse (cons args (reverse (file->string-list lst))))) + +(define (display-item-added args) + (display-hash-ref messages 'item-added-prefix) + (quote-item args) + (display-hash-ref messages 'item-added-suffix)) + +(define (display-item-removed args) + (display-hash-ref messages 'item-removed-prefix) + (quote-item args) + (display-hash-ref messages 'item-removed-suffix)) + +(define (show-list) + (cond [(and + (check-for-folder) + (check-for-file)) + (if + (list-empty? path) + (display-hash-ref messages 'empty-todo-list) + (prettify-list))] + [else + (display-hash-ref messages 'file-not-found) + (display-hash-ref messages 'try-init)])) + +(define (add-item-to-file args) + (let ([new-list (append-to-end args path)]) + (display-to-file + (string-join new-list "\n" #:after-last "\n") + path + #:mode 'text + #:exists 'replace) + (display-item-added args))) + +(define (add-item args) + (if (and + (check-for-folder) + (check-for-file)) + (add-item-to-file (vector-ref args 1)) + (begin + (display-hash-ref messages 'file-not-found) + (display-hash-ref messages 'try-init)))) + +(define (remove-item-from-file args) + (let ([removed-item (get-removed-item (file->string-list path) args)] + [new-list (remove + (list-ref (file->string-list path) (string->number args)) + (file->string-list path))]) + (display-to-file + (string-join new-list "\n" #:after-last "\n") + path + #:mode 'text + #:exists 'replace) + (display-item-removed removed-item))) + +(define (remove-item args) + (cond [(list-empty? path) + (display-hash-ref messages 'empty-todo-list)] + [(and + (check-for-folder) + (check-for-file)) + (remove-item-from-file (vector-ref args 1))] + [(and (not (check-for-folder)) (not (check-for-file))) + (begin + (display-hash-ref messages 'file-not-found) + (display-hash-ref messages 'try-init))]))