Skip to content

Commit

Permalink
Improve autocompletion
Browse files Browse the repository at this point in the history
* AND / OR can now be autocompleted
* Spaces are inserted before and after where necessary
* Newlines are properly preserved
* The lambda that glues together some of the autocompletion info is now a general class that takes any filter
  • Loading branch information
Bios-Marcel committed Jul 20, 2024
1 parent f5d3a5a commit 0b31e61
Show file tree
Hide file tree
Showing 6 changed files with 64 additions and 40 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
package link.biosmarcel.baka.filter;

public enum BinaryExpressionType {
AND,
OR,
AND("AND"),
OR("OR"),
;

public final String text;

BinaryExpressionType(String text) {
this.text = text;
}
}
39 changes: 39 additions & 0 deletions src/main/java/link/biosmarcel/baka/view/AutocompleteGenerator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package link.biosmarcel.baka.view;

import link.biosmarcel.baka.filter.BinaryExpressionType;
import link.biosmarcel.baka.filter.Filter;
import link.biosmarcel.baka.filter.IncompleteQueryException;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class AutocompleteGenerator {
private final Filter<?> filter;

public AutocompleteGenerator(final Filter<?> filter) {
this.filter = filter;
}

public List<String> generate(final String value) {
try {
filter.setQuery(value);
if (value.endsWith(")") || value.endsWith(" ") || value.endsWith("\n")) {
// If the query is valid so far, we can always attach another via binary operators.
return Arrays.stream(BinaryExpressionType.values()).map(bo -> bo.text).toList();
}
return Collections.EMPTY_LIST;
} catch (final IncompleteQueryException exception) {
if ((value.endsWith("(") && exception.token.isBlank()) ||
(value.endsWith(exception.token) &&
(!exception.token.isEmpty() || value.isEmpty() || !value.stripTrailing().equalsIgnoreCase(value)))
) {
return exception.options;
}
return Collections.EMPTY_LIST;
} catch (final RuntimeException exception) {
System.out.println(exception.getMessage());
return Collections.EMPTY_LIST;
}
}
}
20 changes: 14 additions & 6 deletions src/main/java/link/biosmarcel/baka/view/AutocompleteInput.java
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ private boolean canShowPopup() {
return input.isFocused() && !input.isDisabled();
}

private static final char[] autocompleteAfterChars = new char[]{')', '(', ' ', '\n'};

private void complete() {
final String selectedItem = completionList.getSelectionModel().getSelectedItem();
// selection is always nullable
Expand All @@ -108,14 +110,20 @@ private void complete() {

// FIXME Properly preserve newlines?
// FIXME Autocomplete over selection?
var lastSpace = textBeforeCaret.lastIndexOf(' ');
if (lastSpace == 1) {
lastSpace = textBeforeCaret.lastIndexOf('\n');

int autocompleteTo = -1;
for (final char c : autocompleteAfterChars) {
autocompleteTo = Integer.max(textBeforeCaret.lastIndexOf(c), autocompleteTo);
}
final var preCompletionText = textBeforeCaret.substring(0, autocompleteTo + 1);

// Make sure that we have a space after either open or closed parenthesis.
String textToInsert = selectedItem + " ";
if (autocompleteTo != -1 && !Character.isWhitespace(textBeforeCaret.charAt(autocompleteTo))) {
textToInsert = " " + textToInsert;
}
final var preCompletionText = textBeforeCaret.substring(0, lastSpace + 1);

// FIXME Use insert?
input.setText(preCompletionText + selectedItem + " " + input.getText().substring(input.getCaretPosition()));
input.setText(preCompletionText + textToInsert + input.getText().substring(input.getCaretPosition()));
// We add a space at the end, so we can start writing / completing the next token type right away.
input.positionCaret(preCompletionText.length() + selectedItem.length() + 1);
}
Expand Down
20 changes: 1 addition & 19 deletions src/main/java/link/biosmarcel/baka/view/ClassificationsView.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,10 @@
import javafx.scene.layout.HBox;
import link.biosmarcel.baka.ApplicationState;
import link.biosmarcel.baka.data.ClassificationRule;
import link.biosmarcel.baka.filter.IncompleteQueryException;
import org.jspecify.annotations.Nullable;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

public class ClassificationsView extends BakaTab {
Expand Down Expand Up @@ -48,23 +46,7 @@ protected void updateItem(@Nullable ClassificationRuleFX item, boolean empty) {
selectedRuleProperty = listView.getSelectionModel().selectedItemProperty();
nameField = new TextField();
tagField = new TextField();
final var autocompleteFilter = new PaymentFilter();
queryField = new AutocompleteTextArea((value) -> {
try {
autocompleteFilter.setQuery(value);
return Collections.EMPTY_LIST;
} catch (final IncompleteQueryException exception) {
if (value.endsWith(exception.token) &&
(!exception.token.isEmpty() || value.isEmpty() || !value.stripTrailing().equalsIgnoreCase(value))
) {
return exception.options;
}
return Collections.EMPTY_LIST;
} catch (final RuntimeException exception) {
System.out.println(exception.getMessage());
return Collections.EMPTY_LIST;
}
});
queryField = new AutocompleteTextArea(new AutocompleteGenerator(new PaymentFilter())::generate);

final BooleanBinding disableInputs = Bindings.createBooleanBinding(() -> selectedRuleProperty.getValue() == null, selectedRuleProperty);
selectedRuleProperty.addListener((_, oldValue, newValue) -> {
Expand Down
14 changes: 1 addition & 13 deletions src/main/java/link/biosmarcel/baka/view/PaymentsView.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.function.BiFunction;

Expand Down Expand Up @@ -81,18 +80,7 @@ public PaymentsView(ApplicationState state) {

details = new PaymentDetails(state);

// FIXME Can we make parts reusable?
final var autocompleteFilter = new PaymentFilter();
final var filterField = new AutocompleteField((value) -> {
try {
autocompleteFilter.setQuery(value);
return Collections.EMPTY_LIST;
} catch (final IncompleteQueryException exception) {
return exception.options;
} catch (final RuntimeException exception) {
return Collections.EMPTY_LIST;
}
});
final var filterField = new AutocompleteField(new AutocompleteGenerator(new PaymentFilter())::generate);
final var filter = new PaymentFilter();
filterField.textProperty().addListener((_, _, newText) -> {
try {
Expand Down
1 change: 1 addition & 0 deletions src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@
exports link.biosmarcel.baka;
exports link.biosmarcel.baka.bankimport;
exports link.biosmarcel.baka.data;
exports link.biosmarcel.baka.filter;
exports link.biosmarcel.baka.view;
}

0 comments on commit 0b31e61

Please sign in to comment.