Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement command history feature #162

Merged
merged 10 commits into from
Apr 2, 2024
65 changes: 65 additions & 0 deletions src/main/java/seedu/address/logic/CommandHistory.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package seedu.address.logic;

import java.util.ArrayList;

/**
* Represents an abstraction for a command history data structure.
*/
public class CommandHistory {

private final ArrayList<String> commandHistory = new ArrayList<>();
private int index = 0;

/**
* Get the previous command text.
*
* @return the previous command text.
*/
public String getPrevious() {
assert -1 <= index && index <= commandHistory.size();

if (index < 0) {
return "";
}

index -= 1;

if (index < 0) {
return "";
}

return commandHistory.get(index);
}

/**
* Get the next command text.
*
* @return the next command text.
*/
public String getNext() {
assert -1 <= index && index <= commandHistory.size();

if (index >= commandHistory.size()) {
return "";
}

index += 1;

if (index >= commandHistory.size()) {
return "";
}

return commandHistory.get(index);
}

/**
* Add a valid command text to the command history.
*
* @param commandText the command text string.
*/
public void add(String commandText) {
commandHistory.add(commandText);
index = commandHistory.size();
}

}
17 changes: 16 additions & 1 deletion src/main/java/seedu/address/logic/Logic.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,30 @@
* API of the Logic component
*/
public interface Logic {

/**
* Executes the command and returns the result.
* @param commandText The command as entered by the user.
* @return the result of the command execution.
* @throws CommandException If an error occurs during command execution.
* @throws ParseException If an error occurs during parsing.
*/

String execute(String commandText) throws CommandException, ParseException, StorageException;

/**
* Gets the previous command's text.
*
* @return the previous command's text.
*/
String getPreviousCommandText();

/**
* Gets the next command's text.
*
* @return the next command's text.
*/
String getNextCommandText();

/** Returns an unmodifiable view of the filtered list of persons */
ObservableList<Person> getFilteredPersonList();

Expand All @@ -40,4 +54,5 @@ public interface Logic {
* Set the user prefs' GUI settings.
*/
void setGuiSettings(GuiSettings guiSettings);

}
16 changes: 16 additions & 0 deletions src/main/java/seedu/address/logic/LogicManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
private final Model model;
private final Storage storage;

private final CommandHistory commandHistory = new CommandHistory();

/**
* Constructs a {@code LogicManager} with the given {@code Model} and {@code Storage}.
*/
Expand All @@ -41,9 +43,23 @@
String commandResult = command.execute(model);
storage.saveAddressBook(model.getAddressBook());

// keep track of valid commands
commandHistory.add(commandText);
logger.info("\"" + commandText + "\" pushed to stack, commandTextFuture stack cleared");

return commandResult;
}

@Override
public String getPreviousCommandText() {
return commandHistory.getPrevious();

Check warning on line 55 in src/main/java/seedu/address/logic/LogicManager.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/logic/LogicManager.java#L55

Added line #L55 was not covered by tests
}

@Override
public String getNextCommandText() {
return commandHistory.getNext();

Check warning on line 60 in src/main/java/seedu/address/logic/LogicManager.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/logic/LogicManager.java#L60

Added line #L60 was not covered by tests
}

@Override
public ObservableList<Person> getFilteredPersonList() {
return model.getFilteredPersonList();
Expand Down
44 changes: 34 additions & 10 deletions src/main/java/seedu/address/ui/CommandBox.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
package seedu.address.ui;

import java.util.logging.Logger;

import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.Region;
import seedu.address.commons.core.LogsCenter;
import seedu.address.logic.commands.exceptions.CommandException;
import seedu.address.logic.util.exceptions.ParseException;
import seedu.address.storage.exceptions.StorageException;
Expand All @@ -13,7 +18,9 @@
*/
public class CommandBox extends UiPart<Region> {

public static final String ERROR_STYLE_CLASS = "error";
private static final Logger logger = LogsCenter.getLogger(CommandBox.class);

Check warning on line 21 in src/main/java/seedu/address/ui/CommandBox.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/ui/CommandBox.java#L21

Added line #L21 was not covered by tests

private static final String ERROR_STYLE_CLASS = "error";
private static final String FXML = "CommandBox.fxml";

private final CommandExecutor commandExecutor;
Expand All @@ -27,8 +34,12 @@
public CommandBox(CommandExecutor commandExecutor) {
super(FXML);
this.commandExecutor = commandExecutor;

// calls #setStyleToDefault() whenever there is a change to the text of the command box.
commandTextField.textProperty().addListener((unused1, unused2, unused3) -> setStyleToDefault());

// add listener for key presses
commandTextField.setOnKeyPressed(this::handleKeyPressed);

Check warning on line 42 in src/main/java/seedu/address/ui/CommandBox.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/ui/CommandBox.java#L42

Added line #L42 was not covered by tests
}

/**
Expand Down Expand Up @@ -70,16 +81,29 @@
}

/**
* Represents a function that can execute commands.
* Handle the event when a key is pressed.
*
* @param event the KeyEvent when a key is pressed.
*/
@FunctionalInterface
public interface CommandExecutor {
/**
* Executes the command and returns the result.
*
* @see seedu.address.logic.Logic#execute(String)
*/
String execute(String commandText) throws CommandException, ParseException, StorageException;
private void handleKeyPressed(KeyEvent event) {
String commandText = null;

Check warning on line 89 in src/main/java/seedu/address/ui/CommandBox.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/ui/CommandBox.java#L89

Added line #L89 was not covered by tests
if (event.getCode() == KeyCode.UP) {
// go up the command history
logger.info("UP key pressed");
commandText = commandExecutor.getPreviousCommandText();

Check warning on line 93 in src/main/java/seedu/address/ui/CommandBox.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/ui/CommandBox.java#L92-L93

Added lines #L92 - L93 were not covered by tests
} else if (event.getCode() == KeyCode.DOWN) {
// go down the command history
logger.info("DOWN key pressed");
commandText = commandExecutor.getNextCommandText();

Check warning on line 97 in src/main/java/seedu/address/ui/CommandBox.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/ui/CommandBox.java#L96-L97

Added lines #L96 - L97 were not covered by tests
}

if (commandText != null) {
commandTextField.setText(commandText);
commandTextField.positionCaret(commandTextField.getLength());
logger.info("commandTextField set to \"" + commandText + "\"");

Check warning on line 103 in src/main/java/seedu/address/ui/CommandBox.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/ui/CommandBox.java#L101-L103

Added lines #L101 - L103 were not covered by tests
}

event.consume();

Check warning on line 106 in src/main/java/seedu/address/ui/CommandBox.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/ui/CommandBox.java#L106

Added line #L106 was not covered by tests
}

}
33 changes: 33 additions & 0 deletions src/main/java/seedu/address/ui/CommandExecutor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package seedu.address.ui;

import seedu.address.logic.commands.exceptions.CommandException;
import seedu.address.logic.util.exceptions.ParseException;
import seedu.address.storage.exceptions.StorageException;

/**
* Represents an API which supports command execution and history tracking.
*/
public interface CommandExecutor {

/**
* Executes the command and returns the result.
*
* @see seedu.address.logic.Logic#execute(String)
*/
String execute(String commandText) throws CommandException, ParseException, StorageException;

/**
* Gets the previous command's text.
*
* @return the previous command's text.
*/
String getPreviousCommandText();

/**
* Gets the next command's text.
*
* @return the next command's text.
*/
String getNextCommandText();

}
69 changes: 40 additions & 29 deletions src/main/java/seedu/address/ui/MainWindow.java
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@
StatusBarFooter statusBarFooter = new StatusBarFooter(logic.getAddressBookFilePath());
statusbarPlaceholder.getChildren().add(statusBarFooter.getRoot());

CommandBox commandBox = new CommandBox(this::executeCommand);
CommandBox commandBox = new CommandBox(new CommandExecutorInator());

Check warning on line 126 in src/main/java/seedu/address/ui/MainWindow.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/ui/MainWindow.java#L126

Added line #L126 was not covered by tests
commandBoxPlaceholder.getChildren().add(commandBox.getRoot());
}

Expand Down Expand Up @@ -174,39 +174,50 @@
clipboard.setContent(content);
}

/**
* Executes the command and returns the result.
*
* @see seedu.address.logic.Logic#execute(String)
*/
private String executeCommand(String commandText) throws CommandException, ParseException, StorageException {
String commandResult;
try {
commandResult = logic.execute(commandText);
logger.info("Result: " + commandResult);
showMessage(commandResult);
} catch (CommandException | ParseException | StorageException e) {
logger.info("An error occurred while executing command: " + commandText);
showMessage(e.getMessage());
throw e;
}
public void showMessage(String msg) {
resultDisplay.setFeedbackToUser(msg);
}

Check warning on line 179 in src/main/java/seedu/address/ui/MainWindow.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/ui/MainWindow.java#L178-L179

Added lines #L178 - L179 were not covered by tests

private class CommandExecutorInator implements CommandExecutor {

Check warning on line 181 in src/main/java/seedu/address/ui/MainWindow.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/ui/MainWindow.java#L181

Added line #L181 was not covered by tests

@Override
public String execute(String commandText) throws CommandException, ParseException, StorageException {
String commandResult;
try {
commandResult = logic.execute(commandText);
logger.info("Result: " + commandResult);
showMessage(commandResult);
} catch (CommandException | ParseException | StorageException e) {
logger.info("An error occurred while executing command: " + commandText);
showMessage(e.getMessage());
throw e;
}

Check warning on line 194 in src/main/java/seedu/address/ui/MainWindow.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/ui/MainWindow.java#L187-L194

Added lines #L187 - L194 were not covered by tests

// == used to prevent an edge case where a command may somehow return this exact string,
// but is not actually a help or exit command.
if (commandResult == Messages.MESSAGE_SHOWING_HELP) {
handleHelp();
// == used to prevent an edge case where a command may somehow return this exact string,
// but is not actually a help or exit command.
if (commandResult == Messages.MESSAGE_SHOWING_HELP) {
handleHelp();

Check warning on line 199 in src/main/java/seedu/address/ui/MainWindow.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/ui/MainWindow.java#L199

Added line #L199 was not covered by tests
}
if (commandResult == Messages.MESSAGE_EXITING) {
handleExit();

Check warning on line 202 in src/main/java/seedu/address/ui/MainWindow.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/ui/MainWindow.java#L202

Added line #L202 was not covered by tests
}
if (commandResult.startsWith(Messages.MESSAGE_COPIED.substring(0, Messages.MESSAGE_COPIED_LEN + 1))) {
handleCopy(commandResult.substring(Messages.MESSAGE_COPIED_LEN).trim());

Check warning on line 205 in src/main/java/seedu/address/ui/MainWindow.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/ui/MainWindow.java#L205

Added line #L205 was not covered by tests
}

return commandResult;

Check warning on line 208 in src/main/java/seedu/address/ui/MainWindow.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/ui/MainWindow.java#L208

Added line #L208 was not covered by tests
}
if (commandResult == Messages.MESSAGE_EXITING) {
handleExit();

@Override
public String getPreviousCommandText() {
return logic.getPreviousCommandText();

Check warning on line 213 in src/main/java/seedu/address/ui/MainWindow.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/ui/MainWindow.java#L213

Added line #L213 was not covered by tests
}
if (commandResult.startsWith(Messages.MESSAGE_COPIED.substring(0, Messages.MESSAGE_COPIED_LEN + 1))) {
handleCopy(commandResult.substring(Messages.MESSAGE_COPIED_LEN).trim());

@Override
public String getNextCommandText() {
return logic.getNextCommandText();

Check warning on line 218 in src/main/java/seedu/address/ui/MainWindow.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/seedu/address/ui/MainWindow.java#L218

Added line #L218 was not covered by tests
}
return commandResult;
}

public void showMessage(String msg) {
resultDisplay.setFeedbackToUser(msg);
}

}
1 change: 0 additions & 1 deletion src/main/java/seedu/address/ui/UiManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ public void start(Stage primaryStage) {

try {
mainWindow = new MainWindow(primaryStage, logic);

} catch (Throwable e) {
logger.severe(StringUtil.getDetails(e));
showFatalErrorDialogAndShutdown("Fatal error during initializing", e);
Expand Down
Loading