diff --git a/docs/DeveloperGuide.adoc b/docs/DeveloperGuide.adoc index fa7e9c6ef..b3c4ebb13 100644 --- a/docs/DeveloperGuide.adoc +++ b/docs/DeveloperGuide.adoc @@ -376,77 +376,57 @@ Step 3. The user executes `view month March` command to view the calendar for Ma === Undo/Redo feature ==== Current Implementation -The undo/redo mechanism is facilitated by `VersionedAddressBook`. -It extends `AddressBook` with an undo/redo history, stored internally as an `addressBookStateList` and `currentStatePointer`. +The undo/redo mechanism is facilitated by `VersionedPlanner`. +It extends `Planner` with an undo/redo history, stored internally as an `plannerStateList` and `currentStatePointer`. Additionally, it implements the following operations: -* `VersionedAddressBook#commit()` -- Saves the current address book state in its history. -* `VersionedAddressBook#undo()` -- Restores the previous address book state from its history. -* `VersionedAddressBook#redo()` -- Restores a previously undone address book state from its history. +* `VersionedPlanner#commit()` -- Saves the current planner state in its history. +* `VersionedPlanner#undo()` -- Restores the previous planner state from its history. +* `VersionedPlanner#redo()` -- Restores a previously undone planner state from its history. -These operations are exposed in the `Model` interface as `Model#commitAddressBook()`, `Model#undoAddressBook()` and `Model#redoAddressBook()` respectively. +These operations are exposed in the `Model` interface as `Model#commitPlanner()`, `Model#undoPlanner()` and `Model#redoPlanner()` respectively. Given below is an example usage scenario and how the undo/redo mechanism behaves at each step. -Step 1. The user launches the application for the first time. The `VersionedAddressBook` will be initialized with the initial address book state, and the `currentStatePointer` pointing to that single address book state. +Step 1. The user launches the application for the first time. The `VersionedPlanner` will be initialized with the initial planner state, and the `currentStatePointer` pointing to that single planner state. -image::UndoRedoStartingStateListDiagram.png[width="800"] +Step 2. The user executes `delete 5` command to delete the 5th slot in the planner. The `delete` command calls `Model#commitPlanner()`, causing the modified state of the planner after the `delete 5` command executes to be saved in the `plannerStateList`, and the `currentStatePointer` is shifted to the newly inserted planner state. -Step 2. The user executes `delete 5` command to delete the 5th person in the address book. The `delete` command calls `Model#commitAddressBook()`, causing the modified state of the address book after the `delete 5` command executes to be saved in the `addressBookStateList`, and the `currentStatePointer` is shifted to the newly inserted address book state. - -image::UndoRedoNewCommand1StateListDiagram.png[width="800"] - -Step 3. The user executes `add n/David ...` to add a new person. The `add` command also calls `Model#commitAddressBook()`, causing another modified address book state to be saved into the `addressBookStateList`. - -image::UndoRedoNewCommand2StateListDiagram.png[width="800"] +Step 3. The user executes `add n/CS2113T ...` to add a new slot. The `add` command also calls `Model#commitPlanner()`, causing another modified planner state to be saved into the `plannerStateList`. [NOTE] -If a command fails its execution, it will not call `Model#commitAddressBook()`, so the address book state will not be saved into the `addressBookStateList`. +If a command fails its execution, it will not call `Model#commitPlanner()`, so the planner state will not be saved into the `plannerStateList`. -Step 4. The user now decides that adding the person was a mistake, and decides to undo that action by executing the `undo` command. The `undo` command will call `Model#undoAddressBook()`, which will shift the `currentStatePointer` once to the left, pointing it to the previous address book state, and restores the address book to that state. - -image::UndoRedoExecuteUndoStateListDiagram.png[width="800"] +Step 4. The user now decides that adding the slot was a mistake, and decides to undo that action by executing the `undo` command. The `undo` command will call `Model#undoPlanner()`, which will shift the `currentStatePointer` once to the left, pointing it to the previous planner state, and restores the planner to that state. [NOTE] -If the `currentStatePointer` is at index 0, pointing to the initial address book state, then there are no previous address book states to restore. The `undo` command uses `Model#canUndoAddressBook()` to check if this is the case. If so, it will return an error to the user rather than attempting to perform the undo. - -The following sequence diagram shows how the undo operation works: +If the `currentStatePointer` is at index 0, pointing to the initial planner state, then there are no previous planner states to restore. The `undo` command uses `Model#canUndoPlanner()` to check if this is the case. If so, it will return an error to the user rather than attempting to perform the undo. -image::UndoRedoSequenceDiagram.png[width="800"] - -The `redo` command does the opposite -- it calls `Model#redoAddressBook()`, which shifts the `currentStatePointer` once to the right, pointing to the previously undone state, and restores the address book to that state. +The `redo` command does the opposite -- it calls `Model#redoPlanner()`, which shifts the `currentStatePointer` once to the right, pointing to the previously undone state, and restores the planner to that state. [NOTE] -If the `currentStatePointer` is at index `addressBookStateList.size() - 1`, pointing to the latest address book state, then there are no undone address book states to restore. The `redo` command uses `Model#canRedoAddressBook()` to check if this is the case. If so, it will return an error to the user rather than attempting to perform the redo. - -Step 5. The user then decides to execute the command `list`. Commands that do not modify the address book, such as `list`, will usually not call `Model#commitAddressBook()`, `Model#undoAddressBook()` or `Model#redoAddressBook()`. Thus, the `addressBookStateList` remains unchanged. - -image::UndoRedoNewCommand3StateListDiagram.png[width="800"] - -Step 6. The user executes `clear`, which calls `Model#commitAddressBook()`. Since the `currentStatePointer` is not pointing at the end of the `addressBookStateList`, all address book states after the `currentStatePointer` will be purged. We designed it this way because it no longer makes sense to redo the `add n/David ...` command. This is the behavior that most modern desktop applications follow. - -image::UndoRedoNewCommand4StateListDiagram.png[width="800"] +If the `currentStatePointer` is at index `plannerStateList.size() - 1`, pointing to the latest planner state, then there are no undone planner states to restore. The `redo` command uses `Model#canRedoPlanner()` to check if this is the case. If so, it will return an error to the user rather than attempting to perform the redo. -The following activity diagram summarizes what happens when a user executes a new command: +Step 5. The user then decides to execute the command `list`. Commands that do not modify the planner, such as `list`, will usually not call `Model#commitPlanner()`, `Model#undoPlanner()` or `Model#redoPlanner()`. Thus, the `plannerStateList` remains unchanged. -image::UndoRedoActivityDiagram.png[width="650"] +Step 6. The user executes `clear`, which calls `Model#commitPlanner()`. Since the `currentStatePointer` is not pointing at the end of the `plannerStateList`, all planner states after the `currentStatePointer` will be purged. We designed it this way because it no longer makes sense to redo the `add n/CS2113T ...` command. This is the behavior that most modern desktop applications follow. ==== Design Considerations ===== Aspect: How undo & redo executes -* **Alternative 1 (current choice):** Saves the entire address book. +* **Alternative 1 (current choice):** Saves the entire planner. ** Pros: Easy to implement. ** Cons: May have performance issues in terms of memory usage. * **Alternative 2:** Individual command knows how to undo/redo by itself. -** Pros: Will use less memory (e.g. for `delete`, just save the person being deleted). +** Pros: Will use less memory (e.g. for `delete`, just save the slot being deleted). ** Cons: We must ensure that the implementation of each individual command are correct. ===== Aspect: Data structure to support the undo/redo commands -* **Alternative 1 (current choice):** Use a list to store the history of address book states. +* **Alternative 1 (current choice):** Use a list to store the history of planner states. ** Pros: Easy for new Computer Science student undergraduates to understand, who are likely to be the new incoming developers of our project. -** Cons: Logic is duplicated twice. For example, when a new command is executed, we must remember to update both `HistoryManager` and `VersionedAddressBook`. +** Cons: Logic is duplicated twice. For example, when a new command is executed, we must remember to update both `HistoryManager` and `VersionedPlanner`. * **Alternative 2:** Use `HistoryManager` for undo/redo ** Pros: We do not need to maintain a separate list, and just reuse what is already in the codebase. ** Cons: Requires dealing with commands that have already been undone: We must remember to skip these commands. Violates Single Responsibility Principle and Separation of Concerns as `HistoryManager` now needs to do two different things. diff --git a/docs/UserGuide.adoc b/docs/UserGuide.adoc index 60d57fbbc..4a24377c5 100644 --- a/docs/UserGuide.adoc +++ b/docs/UserGuide.adoc @@ -163,32 +163,35 @@ Delete the second _slot_ shown via the `list` command. === Listing Slots: `list` / `l` -List _slots_ in the planner. + -Format: `list [past] [next] [all]` +Lists all slots whose name directly matches the specified keyword (not case-sensitive). + +//Format: `list [past] [next] [all]` +Format: `list n/NAME` -[NOTE] -==== -If you do not specify a keyword, then the `all` keyword will be used and all _slots_ will be listed. -==== +//[NOTE] +//==== +//If you do not specify a keyword, then the `all` keyword will be used and all _slots_ will be listed. +//==== Examples: -* `list next` + -List all _slots_ that has yet to occurred in the planner. +* `list n/CS2113T` + +List all _slots_ that is named `CS2113T` in the planner. === Locating Slots: `find` / `f` -Find all _slots_ that have a similar name and or contains specified _tags_. + -Format: `find [n/SLOT_NAMES] [t/TAG]...` +Find all _slots_ whose part of their name contains the specified keyword and displays them as a list. + +Format: `find [KEYWORD]...` -[NOTE] -==== -TODO -==== +//[NOTE] +//==== +//TODO +//==== Example: -* TODO +* `find CS` +Find all _slots_ whose name contains `CS` (eg. CS1010, CS2113T, SCS1010) + === View the Planner : `view` diff --git a/src/planmysem/commands/FindCommand.java b/src/planmysem/commands/FindCommand.java index 099b2384e..6cebde0f9 100644 --- a/src/planmysem/commands/FindCommand.java +++ b/src/planmysem/commands/FindCommand.java @@ -25,9 +25,9 @@ public class FindCommand extends Command { public static final String MESSAGE_SUCCESS = "%1$s Slots listed.\n%2$s"; public static final String MESSAGE_SUCCESS_NONE = "0 Slots listed.\n"; public static final String MESSAGE_USAGE = COMMAND_WORD + ":\n" + "Finds all slots whose name " - + "contains the specified keywords (case-sensitive).\n\t" + + "contains the specified keywords (not case-sensitive).\n\t" + "Parameters: KEYWORD [MORE_KEYWORDS]...\n\t" - + "Example: " + COMMAND_WORD + "CS2113T"; + + "Example: " + COMMAND_WORD + "CS"; private final Set keywords; diff --git a/src/planmysem/commands/ListCommand.java b/src/planmysem/commands/ListCommand.java index 16f2c751a..623afba7e 100644 --- a/src/planmysem/commands/ListCommand.java +++ b/src/planmysem/commands/ListCommand.java @@ -20,10 +20,11 @@ public class ListCommand extends Command { public static final String COMMAND_WORD_SHORT = "l"; public static final String MESSAGE_SUCCESS = "%1$s Slots listed.\n%2$s"; public static final String MESSAGE_SUCCESS_NONE = "0 Slots listed.\n"; - public static final String MESSAGE_USAGE = COMMAND_WORD + ": Lists all slots." + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Lists all slots whose name " + + "directly matches the specified keyword (not case-sensitive)." //+ "\n\tOptional Parameters: [past] [next] [all]" //+ "\n\tDefault: list all" - + "\n\tExample: " + COMMAND_WORD + " CS1010"; + + "\n\tExample: " + COMMAND_WORD + " n/CS1010"; private final String name;