diff --git a/.gitignore b/.gitignore
index f69985ef1..76264e19c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,3 +15,4 @@ bin/
/text-ui-test/ACTUAL.txt
text-ui-test/EXPECTED-UNIX.TXT
+data/
diff --git a/README.md b/README.md
index 8715d4d91..0dc173b2e 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# Duke project template
+# duke.command.Duke project template
This is a project template for a greenfield Java project. It's named after the Java mascot _Duke_. Given below are instructions on how to use it.
@@ -13,7 +13,7 @@ Prerequisites: JDK 11, update Intellij to the most recent version.
1. If there are any further prompts, accept the defaults.
1. Configure the project to use **JDK 11** (not other versions) as explained in [here](https://www.jetbrains.com/help/idea/sdk.html#set-up-jdk).
In the same dialog, set the **Project language level** field to the `SDK default` option.
-3. After that, locate the `src/main/java/Duke.java` file, right-click it, and choose `Run Duke.main()` (if the code editor is showing compile errors, try restarting the IDE). If the setup is correct, you should see something like the below as the output:
+3. After that, locate the `src/main/java/duke.command.Duke.java` file, right-click it, and choose `Run duke.command.Duke.main()` (if the code editor is showing compile errors, try restarting the IDE). If the setup is correct, you should see something like the below as the output:
```
Hello from
____ _
diff --git a/docs/README.md b/docs/README.md
index 8077118eb..837626489 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -2,28 +2,136 @@
## Features
-### Feature-ABC
+### Listing
+List out all tasks in the tasklist.
-Description of the feature.
+### Add/Delete tasks
+Add different type of tasks into the tasklist.
+Delete a task anytime.
-### Feature-XYZ
+### Find
+Find a task using keywords.
-Description of the feature.
+### Mark done
+Mark a task as done.
+
+### Auto-saving
+Tasks in the tasklist are saved automatically when program exits, ready to be loaded next time.
## Usage
-### `Keyword` - Describe action
+### `list`
+List out all tasks in the tasklist.
+
+Example of usage:
+
+`list`
+
+Expected outcome:
+
+```
+1.[T][X] do wct
+2.[E][ ] finish inquiry(at: 6pm)
+```
+
+### `done`
+Mark a task as done.
+
+Example of usage:
+
+`done 2`
+
+Expected outcome:
+
+```
+Nice! I've marked this task as done:
+ [X] finish inquiry
+```
+
+### `todo`
+Add a "todo" type task.
+
+Example of usage:
+
+`todo do cs2113 ip`
+
+Expected outcome:
+
+```
+Got it. I've added this task:
+ [T][ ] do cs2113 ip
+Now you have 3 tasks in the list
+```
+
+### `deadline`
+Add a "deadline" type task.
+
+Example of usage:
+
+`deadline cs2113 ip /by tonight`
-Describe the action and its outcome.
+Expected outcome:
+
+```
+Got it. I've added this task:
+ [D][ ] cs2113 ip(by: tonight)
+Now you have 5 tasks in the list
+```
-Example of usage:
+### `event`
+Add a "event" type task.
-`keyword (optional arguments)`
+Example of usage:
+
+`event go to party /at tonight`
Expected outcome:
-Description of the outcome.
+```
+Got it. I've added this task:
+ [E][ ] go to party(at: tonight)
+Now you have 6 tasks in the list
+```
+
+### `find`
+Find a task in the tasklist that contains the keyword.
+
+Example of usage:
+`find cs2113`
+
+Expected outcome:
+
+```
+Here are the matching tasks in your list:
+1.[T][ ] do cs2113 ip
+2.[D][ ] cs2113 ip(by: tonight)
```
-expected output
+
+### `delete`
+Delete a task in the task list using the index.
+
+Example of usage:
+
+`delete 1`
+
+Expected outcome:
+
+```
+Noted. I've removed this task:
+ [T][X] do wct
+Now you have 4 tasks in the list
+```
+
+### `bye`
+Terminates the program, saves all changes.
+
+Example of usage:
+
+`bye`
+
+Expected outcome:
+
```
+Bye. Hope to see you again soon!
+```
\ No newline at end of file
diff --git a/docs/_config.yml b/docs/_config.yml
new file mode 100644
index 000000000..c4192631f
--- /dev/null
+++ b/docs/_config.yml
@@ -0,0 +1 @@
+theme: jekyll-theme-cayman
\ No newline at end of file
diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java
deleted file mode 100644
index 5d313334c..000000000
--- a/src/main/java/Duke.java
+++ /dev/null
@@ -1,10 +0,0 @@
-public class Duke {
- public static void main(String[] args) {
- String logo = " ____ _ \n"
- + "| _ \\ _ _| | _____ \n"
- + "| | | | | | | |/ / _ \\\n"
- + "| |_| | |_| | < __/\n"
- + "|____/ \\__,_|_|\\_\\___|\n";
- System.out.println("Hello from\n" + logo);
- }
-}
diff --git a/src/main/java/META-INF/MANIFEST.MF b/src/main/java/META-INF/MANIFEST.MF
new file mode 100644
index 000000000..d749a8d57
--- /dev/null
+++ b/src/main/java/META-INF/MANIFEST.MF
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Main-Class: duke.command.Duke
+
diff --git a/src/main/java/duke/command/Duke.java b/src/main/java/duke/command/Duke.java
new file mode 100644
index 000000000..e38170bb8
--- /dev/null
+++ b/src/main/java/duke/command/Duke.java
@@ -0,0 +1,46 @@
+package duke.command;
+import java.util.Scanner;
+
+public class Duke {
+ private static Parser parser = new Parser();
+ private static Ui ui = new Ui();
+ private static TaskList taskList = new TaskList();
+ private static Storage s = new Storage();
+
+ public static void running() {
+ s.loadFromFile(taskList);
+ String line;
+ Scanner in = new Scanner(System.in);
+ while (true) {
+ line = in.nextLine();
+
+ if (line.equals("list")) {
+ taskList.listOut();
+ } else if (line.startsWith("find")) {
+ taskList.find(line);
+ } else if (line.startsWith("done")) {
+ taskList.markDone(line);
+ } else if (line.startsWith("delete")) {
+ int index = parser.getIndexForDelete(line);
+ taskList.deleteTask(index);
+ } else if (line.startsWith("todo")) {
+ taskList.addTodo(line);
+ } else if (line.startsWith("deadline")) {
+ taskList.addDeadline(line);
+ } else if (line.startsWith("event")) {
+ taskList.addEvent(line);
+ } else if (line.equals("bye")) {
+ s.saveToFile("data/data.txt", taskList);
+ return;
+ } else {
+ System.out.println("☹ OOPS!!! I'm sorry, but I don't know what that means :-(");
+ }
+ }
+ }
+
+ public static void main(String[] args) {
+ ui.sayHi();
+ running();
+ ui.sayBye();
+ }
+}
diff --git a/src/main/java/duke/command/Parser.java b/src/main/java/duke/command/Parser.java
new file mode 100644
index 000000000..79487c463
--- /dev/null
+++ b/src/main/java/duke/command/Parser.java
@@ -0,0 +1,45 @@
+package duke.command;
+
+public class Parser {
+ public static Integer getIndexForDelete(String line) {
+ return Integer.parseInt(line.substring(7)) - 1;
+ }
+
+ public static Integer getIndexForDone(String line) {
+ return Integer.parseInt(line.substring(5)) - 1;
+ }
+
+ /**
+ * Returns the index of the word "/by" in the array of strings generated from the input line.
+ * @param line The whole line of input from the command line.
+ * @return Index position of the the word "/by"
+ */
+ public static Integer getIndexForDeadline(String line) {
+ String[] words = line.split(" ");
+ int index = 0;
+ for (int i = 0; i < words.length; i++) {
+ if (words[i].equals("/by")) {
+ index = i;
+ break;
+ }
+ }
+ return index;
+ }
+
+ /**
+ * Returns the index of the word "/at" in the array of strings generated from the input line.
+ * @param line The whole line of input from the command line.
+ * @return Index position of the the word "/at"
+ */
+ public static Integer getIndexForEvent(String line) {
+ String[] words = line.split(" ");
+ int index = 0;
+ for (int i = 0; i < words.length; i++) {
+ if (words[i].equals("/at")) {
+ index = i;
+ break;
+ }
+ }
+ return index;
+ }
+}
diff --git a/src/main/java/duke/command/Storage.java b/src/main/java/duke/command/Storage.java
new file mode 100644
index 000000000..8a893f039
--- /dev/null
+++ b/src/main/java/duke/command/Storage.java
@@ -0,0 +1,108 @@
+package duke.command;
+
+import duke.task.Deadline;
+import duke.task.Event;
+import duke.task.Task;
+import duke.task.ToDo;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.Scanner;
+
+/**
+ * Represents a storage space for a TaskList object.
+ */
+public class Storage {
+ protected File directory;
+ protected File file;
+
+ public Storage() {
+
+ directory = new File("data");
+ if (!directory.exists()) {
+ directory.mkdirs();
+ }
+ //create txt file if file does not exist
+ file = new File("data/data.txt");
+ if(!file.exists()) {
+ try {
+ file.createNewFile();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ /**
+ * Checks the text file set earlier for storage,
+ * loads the tasks into the input TaskList object.
+ *
+ * @param taskList The list of tasks initialized without any content yet.
+ * @throws IOException If an error occurs from reading or writing to the text file.
+ */
+ public void loadFromFile(TaskList taskList) {
+ int taskNumber = 0;
+ try {
+ File f = new File("data/data.txt");
+ if (!f.createNewFile()) {
+ Scanner s = new Scanner(f);
+ while (s.hasNext()) {
+ String task = s.nextLine();
+ String[] words = task.split(" \\| ");
+ switch (words[0]) {
+ case "T":
+ taskList.tasks[taskNumber] = new ToDo(Integer.parseInt(words[1]), words[2]);
+ taskNumber++;
+ break;
+ case "D":
+ taskList.tasks[taskNumber] = new Deadline(Integer.parseInt(words[1]), words[2], words[3]);
+ taskNumber++;
+ break;
+ case "E":
+ taskList.tasks[taskNumber] = new Event(Integer.parseInt(words[1]), words[2], words[3]);
+ taskNumber++;
+ break;
+ }
+ }
+ }
+ taskList.taskNumber = taskNumber;
+ return;
+ } catch (IOException e) {
+ System.out.println("An error occurred.");
+ e.printStackTrace();
+ return;
+ }
+ }
+
+ /**
+ * Saves the current tasks in a TaskList object
+ * into a text file when the program terminates.
+ *
+ * @param filePath Path of the text file used to store the list of tasks.
+ * @param taskList The list of tasks.
+ * @throws IOException If an error occurs from reading or writing to the text file.
+ */
+ public static void saveToFile(String filePath, TaskList taskList) {
+ try {
+ FileWriter fw = new FileWriter(filePath);
+ for (int i = 0; i < taskList.taskNumber; i++) {
+ switch (taskList.tasks[i].getType()) {
+ case "todo":
+ fw.write("T" + " | " + taskList.tasks[i].getStatusNumber() + " | " + taskList.tasks[i].getDescription() + System.lineSeparator());
+ break;
+ case "deadline":
+ fw.write("D" + " | " + taskList.tasks[i].getStatusNumber() + " | " + taskList.tasks[i].getDescription() + " | " + taskList.tasks[i].getTime() + System.lineSeparator());
+ break;
+ case "event":
+ fw.write("E" + " | " + taskList.tasks[i].getStatusNumber() + " | " + taskList.tasks[i].getDescription() + " | " + taskList.tasks[i].getTime() + System.lineSeparator());
+ break;
+ }
+ }
+ fw.close();
+ } catch (IOException e) {
+ System.out.println("Something went wrong: " + e.getMessage());
+ }
+ }
+
+}
diff --git a/src/main/java/duke/command/TaskList.java b/src/main/java/duke/command/TaskList.java
new file mode 100644
index 000000000..246b45796
--- /dev/null
+++ b/src/main/java/duke/command/TaskList.java
@@ -0,0 +1,160 @@
+package duke.command;
+
+import duke.task.Deadline;
+import duke.task.Event;
+import duke.task.Task;
+import duke.task.ToDo;
+
+/**
+ * Represents a list of all tasks added.
+ */
+public class TaskList {
+ protected Task[] tasks;
+ protected int taskNumber;
+
+ public TaskList() {
+ tasks = new Task[100];
+ taskNumber = 0;
+ }
+
+
+ /**
+ * Prints out all the tasks in a TaskList object.
+ */
+
+ public void listOut() {
+ for (int i = 0; i < taskNumber; i++) {
+ System.out.println((i + 1) + "." + tasks[i]);
+ }
+ }
+
+
+ /**
+ * Marks a particular task done, from index of the task
+ * inputted from the command lind.
+ *
+ * @param line The whole line of input from the command line.
+ * @throws StringIndexOutOfBoundsException If there is no index contained in the line.
+ */
+
+ public void find(String line) {
+ Ui ui = new Ui();
+ try {
+ int j = 1;
+ String keyword = line.substring(5);
+ ui.find();
+ for (int i = 0; i < taskNumber; i++) {
+ if (tasks[i].getDescription().contains(keyword)) {
+ System.out.println(j + "." + tasks[i]);
+ j++;
+ }
+ }
+ } catch (StringIndexOutOfBoundsException e) {
+ ui.findFailed();
+ }
+ }
+
+
+ public void markDone(String line) {
+ Ui ui = new Ui();
+ Parser parser = new Parser();
+ try {
+ int index = parser.getIndexForDone(line);
+ tasks[index].setDone();
+ ui.markDone(index, tasks);
+ } catch (StringIndexOutOfBoundsException e) {
+ ui.markDoneFailed();
+ }
+ }
+
+ /**
+ * Delete a particular task from a TaskList object.
+ * @param index Index of the task to be deleted
+ * @throws StringIndexOutOfBoundsException If there is no index contained in the line.
+ */
+ public void deleteTask(int index) {
+ Ui ui = new Ui();
+ try {
+ ui.deleteTask(index, tasks, taskNumber);
+ for (int i = index; i < taskNumber - 1; i++) {
+ tasks[i] = tasks[i + 1];
+ }
+ taskNumber--;
+ } catch (StringIndexOutOfBoundsException e) {
+ ui.deleteTaskFailed();
+ }
+ }
+
+ /**
+ * Add a "todo" type of task into a TaskList object.
+ * @param line The whole line of input from the command line.
+ * @throws StringIndexOutOfBoundsException If there is no argument after the "todo" command
+ */
+ public void addTodo(String line) {
+ Ui ui = new Ui();
+ try {
+ tasks[taskNumber] = new ToDo(line.substring(5));
+ taskNumber++;
+ ui.addTask(tasks, taskNumber);
+ } catch (StringIndexOutOfBoundsException e) {
+ ui.addTodoFailed();
+ }
+ }
+
+ /**
+ * Add a "deadline" type of task into a TaskList object.
+ * @param line The whole line of input from the command line.
+ * @throws StringIndexOutOfBoundsException If there is no argument after the "deadline" command
+ */
+ public void addDeadline(String line) {
+ Ui ui = new Ui();
+ Parser parser = new Parser();
+ String[] words = line.split(" ");
+ int index = parser.getIndexForDeadline(line);
+ if (index == 0) {
+ ui.addDeadlineFailed();
+ }
+ String deadlineDescription = "";
+ String by = "";
+ for (int i = 1; i < index; i++) {
+ deadlineDescription = deadlineDescription + words[i] + " ";
+ }
+ deadlineDescription = deadlineDescription.substring(0, deadlineDescription.length() - 1);
+ for (int i = index + 1; i < words.length; i++) {
+ by = by + words[i] + " ";
+ }
+ by = by.substring(0, by.length() - 1);
+ tasks[taskNumber] = new Deadline(deadlineDescription, by);
+ taskNumber++;
+ ui.addTask(tasks, taskNumber);
+ }
+
+ /**
+ * Add a "event" type of task into a TaskList object.
+ * @param line The whole line of input from the command line.
+ * @throws StringIndexOutOfBoundsException If there is no argument after the "event" command
+ */
+
+ public void addEvent(String line) {
+ Ui ui = new Ui();
+ Parser parser = new Parser();
+ String[] words = line.split(" ");
+ int index = parser.getIndexForEvent(line);
+ if (index == 0) {
+ ui.addEventFailed();
+ }
+ String eventDescription = "";
+ String at = "";
+ for (int i = 1; i < index; i++) {
+ eventDescription = eventDescription + words[i] + " ";
+ }
+ eventDescription = eventDescription.substring(0, eventDescription.length() - 1);
+ for (int i = index + 1; i < words.length; i++) {
+ at = at + words[i] + " ";
+ }
+ at = at.substring(0, at.length() - 1);
+ tasks[taskNumber] = new Event(eventDescription, at);
+ taskNumber++;
+ ui.addTask(tasks, taskNumber);
+ }
+}
diff --git a/src/main/java/duke/command/Ui.java b/src/main/java/duke/command/Ui.java
new file mode 100644
index 000000000..51ad57dbb
--- /dev/null
+++ b/src/main/java/duke/command/Ui.java
@@ -0,0 +1,59 @@
+package duke.command;
+
+import duke.task.Task;
+
+public class Ui {
+ public static void sayHi() {
+ System.out.println("Hello I'm Duke");
+ System.out.println("What can I do for you?");
+ }
+
+ public static void find() {
+ System.out.println("Here are the matching tasks in your list:");
+ }
+
+ public static void findFailed() {
+ System.out.println("OOPS!!! The description of a find cannot be empty.");
+ }
+
+ public static void markDone(int index, Task[] tasks) {
+ System.out.println("Nice! I've marked this task as done:");
+ System.out.println(" " + tasks[index].getStatusIcon() + " " + tasks[index].getDescription());
+ }
+
+ public static void markDoneFailed() {
+ System.out.println("OOPS!!! The description of a mark-done cannot be empty.");
+ }
+
+ public static void deleteTask(int index, Task[] tasks, int taskNumber) {
+ System.out.println("Noted. I've removed this task:");
+ System.out.println(" " + tasks[index]);
+ System.out.println("Now you have " + (taskNumber - 1) + " tasks in the list");
+ }
+
+ public static void deleteTaskFailed() {
+ System.out.println("OOPS!!! The description of a deletion cannot be empty.");
+ }
+
+ public static void addTask(Task[] tasks, int taskNumber) {
+ System.out.println("Got it. I've added this task:");
+ System.out.println(" " + tasks[taskNumber - 1]);
+ System.out.println("Now you have " + taskNumber + " tasks in the list");
+ }
+
+ public static void addTodoFailed() {
+ System.out.println("OOPS!!! The description of a todo cannot be empty.");
+ }
+
+ public static void addDeadlineFailed() {
+ System.out.println("OOPS!!! The description of a deadline cannot be empty.");
+ }
+
+ public static void addEventFailed() {
+ System.out.println("OOPS!!! The description of an event cannot be empty.");
+ }
+
+ public static void sayBye() {
+ System.out.println("Bye. Hope to see you again soon!");
+ }
+}
diff --git a/src/main/java/duke/task/Deadline.java b/src/main/java/duke/task/Deadline.java
new file mode 100644
index 000000000..713d9b31b
--- /dev/null
+++ b/src/main/java/duke/task/Deadline.java
@@ -0,0 +1,35 @@
+package duke.task;
+
+/**
+ * Represents an deadline that has a description, a status inherited from Task class,
+ * and an extra at attribute indicating the deadline of the task.
+ */
+public class Deadline extends Task {
+
+ protected String by;
+
+ public Deadline(String description, String by) {
+ super(description);
+ this.by = by;
+ }
+
+ public Deadline(int status, String description, String by) {
+ super(status, description);
+ this.by = by;
+ }
+
+ @Override
+ public String getType() {
+ return "deadline";
+ }
+
+ @Override
+ public String getTime() {
+ return this.by;
+ }
+
+ @Override
+ public String toString() {
+ return "[D]" + super.toString() + "(by: " + by + ")";
+ }
+}
diff --git a/src/main/java/duke/task/Event.java b/src/main/java/duke/task/Event.java
new file mode 100644
index 000000000..2e603484c
--- /dev/null
+++ b/src/main/java/duke/task/Event.java
@@ -0,0 +1,38 @@
+package duke.task;
+
+/**
+ * Represents an event that has a description, a status inherited from Task class,
+ * and an extra at attribute indicating the time of the task.
+ */
+public class Event extends Task {
+ protected String at;
+
+ public Event(String description, String at) {
+ super(description);
+ this.at = at;
+ }
+
+ public Event(int status, String description, String at) {
+ super(status, description);
+ this.at = at;
+ }
+
+ @Override
+ public String getType() {
+ return "event";
+ }
+
+ @Override
+ public String getStatusNumber() {
+ return super.getStatusNumber();
+ }
+
+ public String getTime() {
+ return this.at;
+ }
+
+ @Override
+ public String toString() {
+ return "[E]" + super.toString() + "(at: " + at + ")";
+ }
+}
diff --git a/src/main/java/duke/task/Task.java b/src/main/java/duke/task/Task.java
new file mode 100644
index 000000000..47d7388e1
--- /dev/null
+++ b/src/main/java/duke/task/Task.java
@@ -0,0 +1,53 @@
+package duke.task;
+
+/**
+ * Represent a general task that might be a "todo", a "deadline", or a "event".
+ * The task has a description attribute, and a status attribute indicating whether it is done.
+ */
+public class Task {
+ protected String description;
+ protected boolean isDone;
+
+ public Task(String description) {
+ this.description = description;
+ this.isDone = false;
+ }
+
+ public Task(int status, String description) {
+ this.description = description;
+ if (status == 1) {
+ this.isDone = true;
+ } else {
+ this.isDone = false;
+ }
+ }
+
+ public void setDone() {
+ this.isDone = true;
+ }
+
+ public String getDescription() {
+ return this.description;
+ }
+
+ public String getStatusIcon() {
+ return (isDone ? "[X]" : "[ ]"); // mark done task with X
+ }
+
+ public String getStatusNumber() {
+ return (isDone ? "1" : "0"); // mark done task with 1
+ }
+
+ public String getTime() {
+ return "";
+ };
+
+ public String getType() {
+ return "";
+ }
+
+ @Override
+ public String toString() {
+ return this.getStatusIcon() + " " + this.getDescription();
+ }
+}
diff --git a/src/main/java/duke/task/ToDo.java b/src/main/java/duke/task/ToDo.java
new file mode 100644
index 000000000..d66c113d3
--- /dev/null
+++ b/src/main/java/duke/task/ToDo.java
@@ -0,0 +1,17 @@
+package duke.task;
+/**
+ * Represents an todo that has a description, a status inherited from Task class.
+ */
+public class ToDo extends Task{
+ public ToDo(String description) {
+ super(description);
+ }
+ public ToDo(int status, String description) { super(status, description);}
+ public String getType() {
+ return "todo";
+ }
+ @Override
+ public String toString() {
+ return "[T]" + super.toString();
+ }
+}
diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT
index 657e74f6e..5c6addc53 100644
--- a/text-ui-test/EXPECTED.TXT
+++ b/text-ui-test/EXPECTED.TXT
@@ -1,7 +1,17 @@
-Hello from
- ____ _
-| _ \ _ _| | _____
-| | | | | | | |/ / _ \
-| |_| | |_| | < __/
-|____/ \__,_|_|\_\___|
-
+Hello I'm duke.command.Duke
+What can I do for you?
+Got it. I've added this task:
+ [T][ ] do wct
+Now you have 1 tasks in the list
+Got it. I've added this task:
+ [D][ ] inquiry (by: now )
+Now you have 2 tasks in the list
+Got it. I've added this task:
+ [E][ ] wct (at: tomorrow 2pm )
+Now you have 3 tasks in the list
+Nice! I've marked this task as done:
+ [X] do wct
+1.[T][X] do wct
+2.[D][ ] inquiry (by: now )
+3.[E][ ] wct (at: tomorrow 2pm )
+Bye. Hope to see you again soon!
diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt
index e69de29bb..d1a747a5c 100644
--- a/text-ui-test/input.txt
+++ b/text-ui-test/input.txt
@@ -0,0 +1,6 @@
+todo do wct
+deadline inquiry /by now
+event wct /at tomorrow 2pm
+done 1
+list
+bye