diff --git a/src/main/java/org/nwapw/abacus/function/DocumentationType.java b/src/main/java/org/nwapw/abacus/function/DocumentationType.java new file mode 100644 index 0000000..0084657 --- /dev/null +++ b/src/main/java/org/nwapw/abacus/function/DocumentationType.java @@ -0,0 +1,7 @@ +package org.nwapw.abacus.function; + +public enum DocumentationType { + + FUNCTION + +} diff --git a/src/main/java/org/nwapw/abacus/fx/AbacusController.java b/src/main/java/org/nwapw/abacus/fx/AbacusController.java index 2a0ebd5..2e913c9 100644 --- a/src/main/java/org/nwapw/abacus/fx/AbacusController.java +++ b/src/main/java/org/nwapw/abacus/fx/AbacusController.java @@ -7,6 +7,7 @@ import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.collections.transformation.FilteredList; import javafx.fxml.FXML; +import javafx.scene.Node; import javafx.scene.control.*; import javafx.scene.control.cell.CheckBoxListCell; import javafx.scene.text.Text; @@ -14,6 +15,8 @@ import javafx.util.Callback; import javafx.util.StringConverter; import org.nwapw.abacus.Abacus; import org.nwapw.abacus.config.Configuration; +import org.nwapw.abacus.function.Documentation; +import org.nwapw.abacus.function.DocumentationType; import org.nwapw.abacus.number.ComputationInterruptedException; import org.nwapw.abacus.number.NumberInterface; import org.nwapw.abacus.plugin.ClassFinder; @@ -24,7 +27,10 @@ import org.nwapw.abacus.tree.TreeNode; import java.io.File; import java.io.IOException; +import java.util.ArrayList; +import java.util.Comparator; import java.util.Set; +import java.util.stream.Collectors; /** @@ -97,7 +103,7 @@ public class AbacusController implements PluginListener { @FXML private TextField computationLimitField; @FXML - private ListView functionListView; + private ListView functionListView; @FXML private TextField functionListSearchField; @@ -120,11 +126,11 @@ public class AbacusController implements PluginListener { /** * The list of functions that are registered in the calculator. */ - private ObservableList functionList; + private ObservableList functionList; /** * The filtered list displayed to the user. */ - private FilteredList functionFilter; + private FilteredList functionFilter; /** * The abacus instance used for changing the plugin configuration. @@ -227,12 +233,12 @@ public class AbacusController implements PluginListener { return new ToggleablePlugin(string, true); } }); - functionList = FXCollections.observableArrayList(); functionFilter = new FilteredList<>(functionList, (s) -> true); functionListView.setItems(functionFilter); functionListSearchField.textProperty().addListener((observable, oldValue, newValue) -> - functionFilter.setPredicate((newValue.length() == 0) ? ((s) -> true) : ((s) -> s.contains(newValue)))); + functionFilter.setPredicate((newValue.length() == 0) ? ((s) -> true) : ((s) -> s.matches(newValue)))); + functionListView.setCellFactory(param -> new DocumentationCell()); historyData = FXCollections.observableArrayList(); historyTable.setItems(historyData); numberImplementationOptions = FXCollections.observableArrayList(); @@ -354,8 +360,10 @@ public class AbacusController implements PluginListener { plugin.getEnabledProperty().addListener(e -> changesMade = true); enabledPlugins.add(plugin); } - functionList.addAll(manager.getAllFunctions()); - functionList.sort(String::compareTo); + PluginManager pluginManager = abacus.getPluginManager(); + functionList.addAll(manager.getAllFunctions().stream().map(name -> pluginManager.documentationFor(name, DocumentationType.FUNCTION)) + .collect(Collectors.toCollection(ArrayList::new))); + functionList.sort(Comparator.comparing(Documentation::getCodeName)); } @Override diff --git a/src/main/java/org/nwapw/abacus/fx/DocumentationCell.java b/src/main/java/org/nwapw/abacus/fx/DocumentationCell.java new file mode 100644 index 0000000..fbb5c0f --- /dev/null +++ b/src/main/java/org/nwapw/abacus/fx/DocumentationCell.java @@ -0,0 +1,58 @@ +package org.nwapw.abacus.fx; + +import javafx.scene.control.Label; +import javafx.scene.control.ListCell; +import javafx.scene.control.TitledPane; +import javafx.scene.layout.VBox; +import org.nwapw.abacus.function.Documentation; + +public class DocumentationCell extends ListCell { + + private Label codeNameLabel; + private Label nameLabel; + private Label description; + private Label longDescription; + private TitledPane titledPane; + + public DocumentationCell(){ + VBox vbox = new VBox(); + vbox.setSpacing(10); + titledPane = new TitledPane(); + codeNameLabel = new Label(); + nameLabel = new Label(); + description = new Label(); + longDescription = new Label(); + codeNameLabel.setWrapText(true); + nameLabel.setWrapText(true); + description.setWrapText(true); + longDescription.setWrapText(true); + vbox.getChildren().add(codeNameLabel); + vbox.getChildren().add(nameLabel); + vbox.getChildren().add(description); + vbox.getChildren().add(longDescription); + titledPane.textProperty().bindBidirectional(codeNameLabel.textProperty()); + titledPane.setContent(vbox); + titledPane.setExpanded(false); + titledPane.prefWidthProperty().bind(widthProperty()); + + visibleProperty().addListener((a, b, c) -> titledPane.setExpanded(false)); + } + + @Override + protected void updateItem(Documentation item, boolean empty) { + super.updateItem(item, empty); + if(empty){ + codeNameLabel.setText(""); + nameLabel.setText(""); + description.setText(""); + longDescription.setText(""); + setGraphic(null); + } else { + codeNameLabel.setText(item.getCodeName()); + nameLabel.setText(item.getName()); + description.setText(item.getDescription()); + longDescription.setText(item.getLongDescription()); + setGraphic(titledPane); + } + } +} diff --git a/src/main/java/org/nwapw/abacus/plugin/Plugin.java b/src/main/java/org/nwapw/abacus/plugin/Plugin.java index 7a78764..b92a9f4 100644 --- a/src/main/java/org/nwapw/abacus/plugin/Plugin.java +++ b/src/main/java/org/nwapw/abacus/plugin/Plugin.java @@ -1,5 +1,7 @@ package org.nwapw.abacus.plugin; +import org.nwapw.abacus.function.Documentation; +import org.nwapw.abacus.function.DocumentationType; import org.nwapw.abacus.function.Function; import org.nwapw.abacus.function.Operator; import org.nwapw.abacus.number.NumberInterface; @@ -94,6 +96,15 @@ public abstract class Plugin { manager.registerNumberImplementation(name, implementation); } + /** + * To be used in load(). Registers a documentation instance + * used to explain some element of the plugin to the user. + * @param documentation the documentation instance. + */ + protected final void registerDocumentation(Documentation documentation){ + manager.registerDocumentation(documentation); + } + /** * Searches the PluginManager for the given function name. * This can be used by the plugins internally in order to call functions @@ -130,6 +141,17 @@ public abstract class Plugin { return manager.numberImplementationFor(name); } + /** + * Searches the PluginManager for the given documentation name and type. + * + * @param name the name for which to search. + * @param type the type of documentation to search for. + * @return the found documentation, or null if none was found. + */ + protected final Documentation documentationFor(String name, DocumentationType type){ + return manager.documentationFor(name, type); + } + /** * Searches the plugin manager for a Pi value for the given number implementation. * This is done so that number implementations with various degrees of precision diff --git a/src/main/java/org/nwapw/abacus/plugin/PluginManager.java b/src/main/java/org/nwapw/abacus/plugin/PluginManager.java index 46c9517..78d4cb9 100644 --- a/src/main/java/org/nwapw/abacus/plugin/PluginManager.java +++ b/src/main/java/org/nwapw/abacus/plugin/PluginManager.java @@ -1,6 +1,8 @@ package org.nwapw.abacus.plugin; import org.nwapw.abacus.Abacus; +import org.nwapw.abacus.function.Documentation; +import org.nwapw.abacus.function.DocumentationType; import org.nwapw.abacus.function.Function; import org.nwapw.abacus.function.Operator; import org.nwapw.abacus.number.NumberInterface; @@ -34,6 +36,10 @@ public class PluginManager { * The map of number implementations registered by the plugins. */ private Map registeredNumberImplementations; + /** + * The map of documentation for functions registered by the plugins. + */ + private Set registeredDocumentation; /** * The list of number implementations that have been * found by their implementation class. @@ -65,6 +71,7 @@ public class PluginManager { registeredFunctions = new HashMap<>(); registeredOperators = new HashMap<>(); registeredNumberImplementations = new HashMap<>(); + registeredDocumentation = new HashSet<>(); cachedInterfaceImplementations = new HashMap<>(); cachedPi = new HashMap<>(); listeners = new HashSet<>(); @@ -97,6 +104,15 @@ public class PluginManager { registeredNumberImplementations.put(name, implementation); } + /** + * Registers the given documentation with the plugin manager, + * making it accessible to the plugin manager etc. + * @param documentation the documentation to register. + */ + public void registerDocumentation(Documentation documentation){ + registeredDocumentation.add(documentation); + } + /** * Gets the function registered under the given name. * @param name the name of the function. @@ -124,6 +140,27 @@ public class PluginManager { return registeredNumberImplementations.get(name); } + /** + * Gets the documentation for the given entity of the given type. + * @param name the name of the entity to search for. + * @param type the type that this entity is, to filter out similarly named documentation. + * @return the documentation object. + */ + public Documentation documentationFor(String name, DocumentationType type){ + Documentation toReturn = null; + for(Documentation entry : registeredDocumentation){ + if(entry.getCodeName().equals(name) && entry.getType() == type) { + toReturn = entry; + break; + } + } + if(toReturn == null){ + toReturn = new Documentation(name, "", "", "", type); + registerDocumentation(toReturn); + } + return toReturn; + } + /** * Gets the number implementation for the given implementation class. * @@ -230,6 +267,7 @@ public class PluginManager { registeredFunctions.clear(); registeredOperators.clear(); registeredNumberImplementations.clear(); + registeredDocumentation.clear(); cachedInterfaceImplementations.clear(); cachedPi.clear(); listeners.forEach(e -> e.onUnload(this)); diff --git a/src/main/java/org/nwapw/abacus/plugin/StandardPlugin.java b/src/main/java/org/nwapw/abacus/plugin/StandardPlugin.java index d30c265..4f3f370 100755 --- a/src/main/java/org/nwapw/abacus/plugin/StandardPlugin.java +++ b/src/main/java/org/nwapw/abacus/plugin/StandardPlugin.java @@ -1,15 +1,18 @@ package org.nwapw.abacus.plugin; -import org.nwapw.abacus.function.Function; -import org.nwapw.abacus.function.Operator; -import org.nwapw.abacus.function.OperatorAssociativity; -import org.nwapw.abacus.function.OperatorType; +import org.nwapw.abacus.function.*; +import org.nwapw.abacus.lexing.pattern.Match; import org.nwapw.abacus.number.NaiveNumber; import org.nwapw.abacus.number.NumberInterface; import org.nwapw.abacus.number.PreciseNumber; +import org.nwapw.abacus.parsing.Parser; +import org.nwapw.abacus.parsing.ShuntingYardParser; +import org.nwapw.abacus.tree.TokenType; +import org.nwapw.abacus.tree.TreeNode; import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.function.BiFunction; /** @@ -568,6 +571,35 @@ public class StandardPlugin extends Plugin { registerFunction("sec", functionSec); registerFunction("csc", functionCsc); registerFunction("cot", functionCot); + + registerDocumentation(new Documentation("abs", "Absolute Value", "Finds the distance " + + "from zero of a number.", "Given a number, this function finds the distance form " + + "zero of a number, effectively turning negative numbers into positive ones.\n\n" + + "Example: abs(-2) -> 2", DocumentationType.FUNCTION)); + registerDocumentation(new Documentation("exp", "Exponentiate", "Brings e to the given power.", + "This function evaluates e to the power of the given value, and is the inverse " + + "of the natural logarithm.\n\n" + + "Example: exp(1) -> 2.718...", DocumentationType.FUNCTION)); + registerDocumentation(new Documentation("ln", "Natural Logarithm", "Gets the natural " + + "logarithm of the given value.", "The natural logarithm of a number is " + + "the power that e has to be brought to to be equal to the number.\n\n" + + "Example: ln(2.718) -> 1", DocumentationType.FUNCTION)); + registerDocumentation(new Documentation("sqrt", "Square Root", "Finds the square root " + + "of the number.", "A square root a of a number is defined such that a times a is equal " + + "to that number.\n\n" + + "Example: sqrt(4) -> 2", DocumentationType.FUNCTION)); + registerDocumentation(new Documentation("sin", "Sine", "Computes the sine of the given angle, " + + "in radians.", "", DocumentationType.FUNCTION)); + registerDocumentation(new Documentation("cos", "Cosine", "Computes the cosine of the given angle, " + + "in radians.", "", DocumentationType.FUNCTION)); + registerDocumentation(new Documentation("tan", "Tangent", "Computes the tangent of the given angle, " + + "in radians.", "", DocumentationType.FUNCTION)); + registerDocumentation(new Documentation("sec", "Secant", "Computes the secant of the given angle, " + + "in radians.", "", DocumentationType.FUNCTION)); + registerDocumentation(new Documentation("csc", "Cosecant", "Computes the cosecant of the given angle, " + + "in radians.", "", DocumentationType.FUNCTION)); + registerDocumentation(new Documentation("cot", "Cotangent", "Computes the cotangent of the given angle, " + + "in radians.", "", DocumentationType.FUNCTION)); } @Override diff --git a/src/main/kotlin/org/nwapw/abacus/function/Documentation.kt b/src/main/kotlin/org/nwapw/abacus/function/Documentation.kt new file mode 100644 index 0000000..3b94845 --- /dev/null +++ b/src/main/kotlin/org/nwapw/abacus/function/Documentation.kt @@ -0,0 +1,26 @@ +package org.nwapw.abacus.function + +/** + * A data class used for storing information about a function. + * + * The Documentation class holds the information necessary to display the information + * about a function to the user. + * + * @param codeName the name of the function as it occurs in code. + * @param name the name of the function in English. + * @param description the short description of this function. + * @param longDescription the full description of this function. + * @param type the things this documentation maps to. + */ +data class Documentation(val codeName: String, val name: String, + val description: String, val longDescription: String, + val type: DocumentationType) { + + fun matches(other: String): Boolean { + return codeName.toLowerCase().contains(other.toLowerCase()) || + name.toLowerCase().contains(other.toLowerCase()) || + description.toLowerCase().contains(other.toLowerCase()) || + longDescription.toLowerCase().contains(other.toLowerCase()) + } + +} \ No newline at end of file