From c18cada8c702b8f908b5022a607a75def5812747 Mon Sep 17 00:00:00 2001 From: Taz03 Date: Sat, 1 Oct 2022 10:54:01 +0530 Subject: [PATCH 01/28] added auto-complete for tag command --- .../tjbot/commands/tags/TagCommand.java | 31 +++++++++++++++---- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/tags/TagCommand.java b/application/src/main/java/org/togetherjava/tjbot/commands/tags/TagCommand.java index 2e00048a18..39ea709d15 100644 --- a/application/src/main/java/org/togetherjava/tjbot/commands/tags/TagCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/commands/tags/TagCommand.java @@ -1,9 +1,12 @@ package org.togetherjava.tjbot.commands.tags; import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.events.interaction.command.CommandAutoCompleteInteractionEvent; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; +import net.dv8tion.jda.api.interactions.commands.Command; import net.dv8tion.jda.api.interactions.commands.OptionMapping; import net.dv8tion.jda.api.interactions.commands.OptionType; +import net.dv8tion.jda.api.interactions.commands.build.OptionData; import net.dv8tion.jda.api.requests.restaction.interactions.ReplyCallbackAction; import org.togetherjava.tjbot.commands.CommandVisibility; import org.togetherjava.tjbot.commands.SlashCommandAdapter; @@ -21,8 +24,10 @@ public final class TagCommand extends SlashCommandAdapter { private final TagSystem tagSystem; - static final String ID_OPTION = "id"; - static final String REPLY_TO_USER_OPTION = "reply-to"; + private static final String COMMAND_NAME = "tag"; + private static final String ID_OPTION = "id"; + private static final String REPLY_TO_USER_OPTION = "reply-to"; + private static final int MAX_OPTIONS = 25; /** * Creates a new instance, using the given tag system as base. @@ -30,15 +35,16 @@ public final class TagCommand extends SlashCommandAdapter { * @param tagSystem the system providing the actual tag data */ public TagCommand(TagSystem tagSystem) { - super("tag", "Display a tags content", CommandVisibility.GUILD); + super(COMMAND_NAME, "Display a tags content", CommandVisibility.GUILD); this.tagSystem = tagSystem; // TODO Think about adding an ephemeral selection menu with pagination support // if the user calls this without id or similar - getData().addOption(OptionType.STRING, ID_OPTION, "The id of the tag to display", true) - .addOption(OptionType.USER, REPLY_TO_USER_OPTION, - "Optionally, the user who you want to reply to", false); + getData().addOptions( + new OptionData(OptionType.STRING, ID_OPTION, "The id of the tag to display", true, true), + new OptionData(OptionType.USER, REPLY_TO_USER_OPTION, "Optionally, the user who you want to reply to", false) + ); } @Override @@ -62,4 +68,17 @@ public void onSlashCommand(SlashCommandInteractionEvent event) { } message.queue(); } + + @Override + public void onAutoComplete(CommandAutoCompleteInteractionEvent event) { + if (event.getName().equals(COMMAND_NAME) && event.getFocusedOption().getName().equals(ID_OPTION)) { + event.replyChoices( + tagSystem.getAllIds().stream() + .filter(id -> id.startsWith(event.getFocusedOption().getValue())) + .map(id -> new Command.Choice(id, id)) + .limit(MAX_OPTIONS) + .toList() + ).queue(); + } + } } From cb279c6931b64eb5a94416f148ee30677ed56b43 Mon Sep 17 00:00:00 2001 From: Taz03 Date: Sat, 1 Oct 2022 11:01:09 +0530 Subject: [PATCH 02/28] spotless hates me --- .../tjbot/commands/tags/TagCommand.java | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/tags/TagCommand.java b/application/src/main/java/org/togetherjava/tjbot/commands/tags/TagCommand.java index 39ea709d15..6002022f8a 100644 --- a/application/src/main/java/org/togetherjava/tjbot/commands/tags/TagCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/commands/tags/TagCommand.java @@ -12,6 +12,7 @@ import org.togetherjava.tjbot.commands.SlashCommandAdapter; import java.time.Instant; +import java.util.List; import java.util.Objects; /** @@ -42,9 +43,10 @@ public TagCommand(TagSystem tagSystem) { // TODO Think about adding an ephemeral selection menu with pagination support // if the user calls this without id or similar getData().addOptions( - new OptionData(OptionType.STRING, ID_OPTION, "The id of the tag to display", true, true), - new OptionData(OptionType.USER, REPLY_TO_USER_OPTION, "Optionally, the user who you want to reply to", false) - ); + new OptionData(OptionType.STRING, ID_OPTION, "The id of the tag to display", true, + true), + new OptionData(OptionType.USER, REPLY_TO_USER_OPTION, + "Optionally, the user who you want to reply to", false)); } @Override @@ -71,14 +73,16 @@ public void onSlashCommand(SlashCommandInteractionEvent event) { @Override public void onAutoComplete(CommandAutoCompleteInteractionEvent event) { - if (event.getName().equals(COMMAND_NAME) && event.getFocusedOption().getName().equals(ID_OPTION)) { - event.replyChoices( - tagSystem.getAllIds().stream() - .filter(id -> id.startsWith(event.getFocusedOption().getValue())) - .map(id -> new Command.Choice(id, id)) - .limit(MAX_OPTIONS) - .toList() - ).queue(); + if (event.getName().equals(COMMAND_NAME) + && event.getFocusedOption().getName().equals(ID_OPTION)) { + List choices = tagSystem.getAllIds() + .stream() + .filter(id -> id.startsWith(event.getFocusedOption().getValue())) + .map(id -> new Command.Choice(id, id)) + .limit(MAX_OPTIONS) + .toList(); + + event.replyChoices(choices).queue(); } } } From c71ebf4d3ca550cc9b6df3aaeeb906ebdfb62461 Mon Sep 17 00:00:00 2001 From: Taz03 Date: Sat, 1 Oct 2022 11:12:27 +0530 Subject: [PATCH 03/28] removed private from fields --- .../java/org/togetherjava/tjbot/commands/tags/TagCommand.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/tags/TagCommand.java b/application/src/main/java/org/togetherjava/tjbot/commands/tags/TagCommand.java index 6002022f8a..7d11917eb5 100644 --- a/application/src/main/java/org/togetherjava/tjbot/commands/tags/TagCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/commands/tags/TagCommand.java @@ -26,8 +26,8 @@ public final class TagCommand extends SlashCommandAdapter { private final TagSystem tagSystem; private static final String COMMAND_NAME = "tag"; - private static final String ID_OPTION = "id"; - private static final String REPLY_TO_USER_OPTION = "reply-to"; + static final String ID_OPTION = "id"; + static final String REPLY_TO_USER_OPTION = "reply-to"; private static final int MAX_OPTIONS = 25; /** From 1e28c879209b30c49e8f16426c1efbde2264caed Mon Sep 17 00:00:00 2001 From: Taz03 Date: Sat, 1 Oct 2022 11:38:39 +0530 Subject: [PATCH 04/28] CR --- .../org/togetherjava/tjbot/commands/tags/TagCommand.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/tags/TagCommand.java b/application/src/main/java/org/togetherjava/tjbot/commands/tags/TagCommand.java index 7d11917eb5..8760d146ee 100644 --- a/application/src/main/java/org/togetherjava/tjbot/commands/tags/TagCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/commands/tags/TagCommand.java @@ -28,7 +28,6 @@ public final class TagCommand extends SlashCommandAdapter { private static final String COMMAND_NAME = "tag"; static final String ID_OPTION = "id"; static final String REPLY_TO_USER_OPTION = "reply-to"; - private static final int MAX_OPTIONS = 25; /** * Creates a new instance, using the given tag system as base. @@ -73,13 +72,12 @@ public void onSlashCommand(SlashCommandInteractionEvent event) { @Override public void onAutoComplete(CommandAutoCompleteInteractionEvent event) { - if (event.getName().equals(COMMAND_NAME) - && event.getFocusedOption().getName().equals(ID_OPTION)) { + if (event.getFocusedOption().getName().equals(ID_OPTION)) { List choices = tagSystem.getAllIds() .stream() .filter(id -> id.startsWith(event.getFocusedOption().getValue())) .map(id -> new Command.Choice(id, id)) - .limit(MAX_OPTIONS) + .limit(OptionData.MAX_CHOICES) .toList(); event.replyChoices(choices).queue(); From afa3133cc69dd4934f7b3d8167c05ab556cf5096 Mon Sep 17 00:00:00 2001 From: Taz03 Date: Sat, 1 Oct 2022 14:22:38 +0530 Subject: [PATCH 05/28] removed a var --- .../java/org/togetherjava/tjbot/commands/tags/TagCommand.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/tags/TagCommand.java b/application/src/main/java/org/togetherjava/tjbot/commands/tags/TagCommand.java index 8760d146ee..f9bb51a597 100644 --- a/application/src/main/java/org/togetherjava/tjbot/commands/tags/TagCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/commands/tags/TagCommand.java @@ -25,7 +25,6 @@ public final class TagCommand extends SlashCommandAdapter { private final TagSystem tagSystem; - private static final String COMMAND_NAME = "tag"; static final String ID_OPTION = "id"; static final String REPLY_TO_USER_OPTION = "reply-to"; @@ -35,7 +34,7 @@ public final class TagCommand extends SlashCommandAdapter { * @param tagSystem the system providing the actual tag data */ public TagCommand(TagSystem tagSystem) { - super(COMMAND_NAME, "Display a tags content", CommandVisibility.GUILD); + super("tag", "Display a tags content", CommandVisibility.GUILD); this.tagSystem = tagSystem; From 7e61818bf1eb459b3dc53ff2424fb53093bf2574 Mon Sep 17 00:00:00 2001 From: Taz03 Date: Sat, 1 Oct 2022 19:21:23 +0530 Subject: [PATCH 06/28] more complex --- .../tjbot/commands/tags/TagCommand.java | 29 +++++++++++++------ 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/tags/TagCommand.java b/application/src/main/java/org/togetherjava/tjbot/commands/tags/TagCommand.java index f9bb51a597..77966dc8c3 100644 --- a/application/src/main/java/org/togetherjava/tjbot/commands/tags/TagCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/commands/tags/TagCommand.java @@ -3,6 +3,7 @@ import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.events.interaction.command.CommandAutoCompleteInteractionEvent; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; +import net.dv8tion.jda.api.interactions.AutoCompleteQuery; import net.dv8tion.jda.api.interactions.commands.Command; import net.dv8tion.jda.api.interactions.commands.OptionMapping; import net.dv8tion.jda.api.interactions.commands.OptionType; @@ -12,8 +13,11 @@ import org.togetherjava.tjbot.commands.SlashCommandAdapter; import java.time.Instant; +import java.util.Collection; +import java.util.Comparator; import java.util.List; -import java.util.Objects; + +import static org.togetherjava.tjbot.commands.utils.StringDistances.prefixEditDistance; /** * Implements the {@code /tag} command which lets the bot respond content of a tag that has been @@ -49,7 +53,7 @@ public TagCommand(TagSystem tagSystem) { @Override public void onSlashCommand(SlashCommandInteractionEvent event) { - String id = Objects.requireNonNull(event.getOption(ID_OPTION)).getAsString(); + String id = event.getOption(ID_OPTION).getAsString(); OptionMapping replyToUserOption = event.getOption(REPLY_TO_USER_OPTION); if (tagSystem.handleIsUnknownTag(id, event)) { @@ -71,15 +75,22 @@ public void onSlashCommand(SlashCommandInteractionEvent event) { @Override public void onAutoComplete(CommandAutoCompleteInteractionEvent event) { - if (event.getFocusedOption().getName().equals(ID_OPTION)) { - List choices = tagSystem.getAllIds() - .stream() - .filter(id -> id.startsWith(event.getFocusedOption().getValue())) - .map(id -> new Command.Choice(id, id)) - .limit(OptionData.MAX_CHOICES) - .toList(); + AutoCompleteQuery focusedOption = event.getFocusedOption(); + + if (focusedOption.getName().equals(ID_OPTION)) { + List choices = + matches(focusedOption.getValue(), tagSystem.getAllIds()).stream() + .map(id -> new Command.Choice(id, id)) + .limit(OptionData.MAX_CHOICES) + .toList(); event.replyChoices(choices).queue(); } } + + private static List matches(String prefix, Collection candidates) { + return candidates.stream() + .sorted(Comparator.comparingInt(candidate -> prefixEditDistance(prefix, candidate))) + .toList(); + } } From dbc186fda6938e55c9e8a73117db9628314e3c19 Mon Sep 17 00:00:00 2001 From: Taz03 Date: Sun, 2 Oct 2022 23:52:23 +0530 Subject: [PATCH 07/28] now its a search engine instead of autocomplete --- .../tjbot/commands/tags/TagCommand.java | 22 +++++----------- .../tjbot/commands/utils/StringDistances.java | 26 +++++++++++++++++++ 2 files changed, 33 insertions(+), 15 deletions(-) diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/tags/TagCommand.java b/application/src/main/java/org/togetherjava/tjbot/commands/tags/TagCommand.java index 77966dc8c3..a9abc0e59b 100644 --- a/application/src/main/java/org/togetherjava/tjbot/commands/tags/TagCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/commands/tags/TagCommand.java @@ -11,13 +11,10 @@ import net.dv8tion.jda.api.requests.restaction.interactions.ReplyCallbackAction; import org.togetherjava.tjbot.commands.CommandVisibility; import org.togetherjava.tjbot.commands.SlashCommandAdapter; +import org.togetherjava.tjbot.commands.utils.StringDistances; import java.time.Instant; import java.util.Collection; -import java.util.Comparator; -import java.util.List; - -import static org.togetherjava.tjbot.commands.utils.StringDistances.prefixEditDistance; /** * Implements the {@code /tag} command which lets the bot respond content of a tag that has been @@ -78,19 +75,14 @@ public void onAutoComplete(CommandAutoCompleteInteractionEvent event) { AutoCompleteQuery focusedOption = event.getFocusedOption(); if (focusedOption.getName().equals(ID_OPTION)) { - List choices = - matches(focusedOption.getValue(), tagSystem.getAllIds()).stream() - .map(id -> new Command.Choice(id, id)) - .limit(OptionData.MAX_CHOICES) - .toList(); + Collection choices = StringDistances + .autocompleteSuggestions(focusedOption.getValue(), tagSystem.getAllIds(), 0.5) + .stream() + .map(id -> new Command.Choice(id, id)) + .limit(OptionData.MAX_CHOICES) + .toList(); event.replyChoices(choices).queue(); } } - - private static List matches(String prefix, Collection candidates) { - return candidates.stream() - .sorted(Comparator.comparingInt(candidate -> prefixEditDistance(prefix, candidate))) - .toList(); - } } diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/utils/StringDistances.java b/application/src/main/java/org/togetherjava/tjbot/commands/utils/StringDistances.java index c839a46681..25519ec588 100644 --- a/application/src/main/java/org/togetherjava/tjbot/commands/utils/StringDistances.java +++ b/application/src/main/java/org/togetherjava/tjbot/commands/utils/StringDistances.java @@ -4,8 +4,17 @@ import java.util.Collection; import java.util.Comparator; import java.util.Optional; +import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.IntStream; +record CandidateScore(String candidate, int score) implements Comparable { + @Override + public int compareTo(CandidateScore otherCandidateScore) { + return Integer.compare(this.score, otherCandidateScore.score); + } +} + + /** * Utility class for computing string distances, for example the edit distance between two words. */ @@ -51,6 +60,23 @@ public static Optional autocomplete(CharSequence pre .min(Comparator.comparingInt(candidate -> prefixEditDistance(prefix, candidate))); } + public static Collection autocompleteSuggestions(CharSequence prefix, + Collection candidates, double errorMargin) { + AtomicInteger firstCandidateScore = new AtomicInteger(-1); + return candidates.stream() + .map(candidate -> new CandidateScore(candidate, prefixEditDistance(prefix, candidate))) + .sorted() + .peek(candidate -> { + if (firstCandidateScore.get() == -1) { + firstCandidateScore.set(candidate.score()); + } + }) + .takeWhile(candidateScore -> candidateScore.score() <= firstCandidateScore.get() + + firstCandidateScore.get() * errorMargin) + .map(CandidateScore::candidate) + .toList(); + } + /** * Distance to receive {@code destination} from {@code source} by editing. *

From 9bd96dfd8d06398d0c5780e8fe5f90d4c47772d5 Mon Sep 17 00:00:00 2001 From: Taz03 Date: Mon, 3 Oct 2022 00:44:52 +0530 Subject: [PATCH 08/28] no peeking --- .../tjbot/commands/utils/CandidateScore.java | 8 +++++++ .../tjbot/commands/utils/StringDistances.java | 23 +++++-------------- 2 files changed, 14 insertions(+), 17 deletions(-) create mode 100644 application/src/main/java/org/togetherjava/tjbot/commands/utils/CandidateScore.java diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/utils/CandidateScore.java b/application/src/main/java/org/togetherjava/tjbot/commands/utils/CandidateScore.java new file mode 100644 index 0000000000..9ff99349fe --- /dev/null +++ b/application/src/main/java/org/togetherjava/tjbot/commands/utils/CandidateScore.java @@ -0,0 +1,8 @@ +package org.togetherjava.tjbot.commands.utils; + +record CandidateScore(String candidate, int score) implements Comparable { + @Override + public int compareTo(CandidateScore otherCandidateScore) { + return Integer.compare(this.score, otherCandidateScore.score); + } +} diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/utils/StringDistances.java b/application/src/main/java/org/togetherjava/tjbot/commands/utils/StringDistances.java index 25519ec588..e8e02797b2 100644 --- a/application/src/main/java/org/togetherjava/tjbot/commands/utils/StringDistances.java +++ b/application/src/main/java/org/togetherjava/tjbot/commands/utils/StringDistances.java @@ -4,17 +4,8 @@ import java.util.Collection; import java.util.Comparator; import java.util.Optional; -import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.IntStream; -record CandidateScore(String candidate, int score) implements Comparable { - @Override - public int compareTo(CandidateScore otherCandidateScore) { - return Integer.compare(this.score, otherCandidateScore.score); - } -} - - /** * Utility class for computing string distances, for example the edit distance between two words. */ @@ -62,17 +53,15 @@ public static Optional autocomplete(CharSequence pre public static Collection autocompleteSuggestions(CharSequence prefix, Collection candidates, double errorMargin) { - AtomicInteger firstCandidateScore = new AtomicInteger(-1); + int firstCandidateScore = candidates.stream() + .mapToInt(candidate -> prefixEditDistance(prefix, candidate)) + .min() + .orElse(0); return candidates.stream() .map(candidate -> new CandidateScore(candidate, prefixEditDistance(prefix, candidate))) .sorted() - .peek(candidate -> { - if (firstCandidateScore.get() == -1) { - firstCandidateScore.set(candidate.score()); - } - }) - .takeWhile(candidateScore -> candidateScore.score() <= firstCandidateScore.get() - + firstCandidateScore.get() * errorMargin) + .takeWhile(candidateScore -> candidateScore.score() <= firstCandidateScore + + firstCandidateScore * errorMargin) .map(CandidateScore::candidate) .toList(); } From f98dc0ebb751a801d0b75876f6e7f9743ed185ca Mon Sep 17 00:00:00 2001 From: Taz03 Date: Mon, 3 Oct 2022 00:52:55 +0530 Subject: [PATCH 09/28] documented --- .../tjbot/commands/utils/StringDistances.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/utils/StringDistances.java b/application/src/main/java/org/togetherjava/tjbot/commands/utils/StringDistances.java index e8e02797b2..547ecda4d2 100644 --- a/application/src/main/java/org/togetherjava/tjbot/commands/utils/StringDistances.java +++ b/application/src/main/java/org/togetherjava/tjbot/commands/utils/StringDistances.java @@ -51,6 +51,14 @@ public static Optional autocomplete(CharSequence pre .min(Comparator.comparingInt(candidate -> prefixEditDistance(prefix, candidate))); } + /** + * Gives sorted suggestion to autocomplete a prefix string from the given options + * + * @param prefix the prefix to give suggestion for + * @param candidates all the possible suggestions + * @param errorMargin error margin of suggestions, error percentage / 100 + * @return collection of autocomplete suggestions + */ public static Collection autocompleteSuggestions(CharSequence prefix, Collection candidates, double errorMargin) { int firstCandidateScore = candidates.stream() From 25be64e549293fc4cf8d110d9ef0d6292c780be8 Mon Sep 17 00:00:00 2001 From: Taz03 Date: Tue, 4 Oct 2022 16:59:36 +0530 Subject: [PATCH 10/28] early return --- .../tjbot/commands/tags/TagCommand.java | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/tags/TagCommand.java b/application/src/main/java/org/togetherjava/tjbot/commands/tags/TagCommand.java index a9abc0e59b..2ff96c5a33 100644 --- a/application/src/main/java/org/togetherjava/tjbot/commands/tags/TagCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/commands/tags/TagCommand.java @@ -75,14 +75,16 @@ public void onAutoComplete(CommandAutoCompleteInteractionEvent event) { AutoCompleteQuery focusedOption = event.getFocusedOption(); if (focusedOption.getName().equals(ID_OPTION)) { - Collection choices = StringDistances - .autocompleteSuggestions(focusedOption.getValue(), tagSystem.getAllIds(), 0.5) - .stream() - .map(id -> new Command.Choice(id, id)) - .limit(OptionData.MAX_CHOICES) - .toList(); - - event.replyChoices(choices).queue(); + return; } + + Collection choices = StringDistances + .autocompleteSuggestions(focusedOption.getValue(), tagSystem.getAllIds(), 0.5) + .stream() + .map(id -> new Command.Choice(id, id)) + .limit(OptionData.MAX_CHOICES) + .toList(); + + event.replyChoices(choices).queue(); } } From b738d2c3e4032a20d3ded6be905561337e5c610f Mon Sep 17 00:00:00 2001 From: Taz03 Date: Wed, 5 Oct 2022 01:22:33 +0530 Subject: [PATCH 11/28] fixed CandidateScore record --- .../tjbot/commands/utils/CandidateScore.java | 8 -------- .../tjbot/commands/utils/StringDistances.java | 13 ++++++++++--- 2 files changed, 10 insertions(+), 11 deletions(-) delete mode 100644 application/src/main/java/org/togetherjava/tjbot/commands/utils/CandidateScore.java diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/utils/CandidateScore.java b/application/src/main/java/org/togetherjava/tjbot/commands/utils/CandidateScore.java deleted file mode 100644 index 9ff99349fe..0000000000 --- a/application/src/main/java/org/togetherjava/tjbot/commands/utils/CandidateScore.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.togetherjava.tjbot.commands.utils; - -record CandidateScore(String candidate, int score) implements Comparable { - @Override - public int compareTo(CandidateScore otherCandidateScore) { - return Integer.compare(this.score, otherCandidateScore.score); - } -} diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/utils/StringDistances.java b/application/src/main/java/org/togetherjava/tjbot/commands/utils/StringDistances.java index 547ecda4d2..ac3c9fa00d 100644 --- a/application/src/main/java/org/togetherjava/tjbot/commands/utils/StringDistances.java +++ b/application/src/main/java/org/togetherjava/tjbot/commands/utils/StringDistances.java @@ -61,16 +61,23 @@ public static Optional autocomplete(CharSequence pre */ public static Collection autocompleteSuggestions(CharSequence prefix, Collection candidates, double errorMargin) { + record MatchScore(String candidate, int score) implements Comparable { + @Override + public int compareTo(MatchScore otherMatchScore) { + return Integer.compare(this.score, otherMatchScore.score); + } + } + int firstCandidateScore = candidates.stream() .mapToInt(candidate -> prefixEditDistance(prefix, candidate)) .min() .orElse(0); return candidates.stream() - .map(candidate -> new CandidateScore(candidate, prefixEditDistance(prefix, candidate))) + .map(candidate -> new MatchScore(candidate, prefixEditDistance(prefix, candidate))) .sorted() - .takeWhile(candidateScore -> candidateScore.score() <= firstCandidateScore + .takeWhile(matchScore -> matchScore.score() <= firstCandidateScore + firstCandidateScore * errorMargin) - .map(CandidateScore::candidate) + .map(MatchScore::candidate) .toList(); } From 9bdeb06f23730342c655d4d3b02f9b96ef54778c Mon Sep 17 00:00:00 2001 From: Taz03 Date: Wed, 5 Oct 2022 20:52:58 +0530 Subject: [PATCH 12/28] added tests --- .../commands/utils/StringDistancesTest.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/application/src/test/java/org/togetherjava/tjbot/commands/utils/StringDistancesTest.java b/application/src/test/java/org/togetherjava/tjbot/commands/utils/StringDistancesTest.java index 2b83957bed..54d5ce4f31 100644 --- a/application/src/test/java/org/togetherjava/tjbot/commands/utils/StringDistancesTest.java +++ b/application/src/test/java/org/togetherjava/tjbot/commands/utils/StringDistancesTest.java @@ -2,12 +2,29 @@ import org.junit.jupiter.api.Test; +import java.util.Collection; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; final class StringDistancesTest { + @Test + void autoCompleteSuggestions() { + record TestCase(String name, Collection expectedSuggestions, String prefix, Collection candidates, double errorMargin) { + } + + List tests = List.of( + new TestCase("empty_candidates", List.of(), "prefix", List.of(), 0), + new TestCase("empty_prefix", List.of("one", "two", "three"), "", List.of("one", "two", "three"), 0), + new TestCase("real_test", List.of("java", "one", "js", "j"), "jo", List.of("java", "xj", "bs", "one", "yes", "js", "a", "j"), 0.8) + ); + + for (TestCase test : tests) { + assertEquals(test.expectedSuggestions, StringDistances.autocompleteSuggestions(test.prefix, test.candidates, test.errorMargin), "Test '%s' failed".formatted(test.name)); + } + } + @Test void editDistance() { record TestCase(String name, int expectedDistance, String source, String destination) { From 9315a394d9abe7d7046cc81eff714dcf41b9d51e Mon Sep 17 00:00:00 2001 From: Taz03 Date: Wed, 5 Oct 2022 20:55:44 +0530 Subject: [PATCH 13/28] spotless --- .../commands/utils/StringDistancesTest.java | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/application/src/test/java/org/togetherjava/tjbot/commands/utils/StringDistancesTest.java b/application/src/test/java/org/togetherjava/tjbot/commands/utils/StringDistancesTest.java index 54d5ce4f31..57095b7b2c 100644 --- a/application/src/test/java/org/togetherjava/tjbot/commands/utils/StringDistancesTest.java +++ b/application/src/test/java/org/togetherjava/tjbot/commands/utils/StringDistancesTest.java @@ -11,17 +11,22 @@ final class StringDistancesTest { @Test void autoCompleteSuggestions() { - record TestCase(String name, Collection expectedSuggestions, String prefix, Collection candidates, double errorMargin) { + record TestCase(String name, Collection expectedSuggestions, String prefix, + Collection candidates, double errorMargin) { } - List tests = List.of( - new TestCase("empty_candidates", List.of(), "prefix", List.of(), 0), - new TestCase("empty_prefix", List.of("one", "two", "three"), "", List.of("one", "two", "three"), 0), - new TestCase("real_test", List.of("java", "one", "js", "j"), "jo", List.of("java", "xj", "bs", "one", "yes", "js", "a", "j"), 0.8) - ); + List tests = + List.of(new TestCase("empty_candidates", List.of(), "prefix", List.of(), 0), + new TestCase("empty_prefix", List.of("one", "two", "three"), "", + List.of("one", "two", "three"), 0), + new TestCase("real_test", List.of("java", "one", "js", "j"), "jo", + List.of("java", "xj", "bs", "one", "yes", "js", "a", "j"), 0.8)); for (TestCase test : tests) { - assertEquals(test.expectedSuggestions, StringDistances.autocompleteSuggestions(test.prefix, test.candidates, test.errorMargin), "Test '%s' failed".formatted(test.name)); + assertEquals( + test.expectedSuggestions, StringDistances.autocompleteSuggestions(test.prefix, + test.candidates, test.errorMargin), + "Test '%s' failed".formatted(test.name)); } } From 7cb0528ad34eb52164639ed7d6f1e6acadc9dbb3 Mon Sep 17 00:00:00 2001 From: Taz03 Date: Wed, 5 Oct 2022 22:47:50 +0530 Subject: [PATCH 14/28] better sorting --- .../tjbot/commands/utils/StringDistances.java | 8 +++++++- .../tjbot/commands/utils/StringDistancesTest.java | 8 +++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/utils/StringDistances.java b/application/src/main/java/org/togetherjava/tjbot/commands/utils/StringDistances.java index ac3c9fa00d..d55959a9bc 100644 --- a/application/src/main/java/org/togetherjava/tjbot/commands/utils/StringDistances.java +++ b/application/src/main/java/org/togetherjava/tjbot/commands/utils/StringDistances.java @@ -64,7 +64,13 @@ public static Collection autocompleteSuggestions(CharSequence prefix, record MatchScore(String candidate, int score) implements Comparable { @Override public int compareTo(MatchScore otherMatchScore) { - return Integer.compare(this.score, otherMatchScore.score); + int compare = Integer.compare(this.score, otherMatchScore.score); + + if (compare == 0) { + return this.candidate.compareTo(otherMatchScore.candidate); + } + + return compare; } } diff --git a/application/src/test/java/org/togetherjava/tjbot/commands/utils/StringDistancesTest.java b/application/src/test/java/org/togetherjava/tjbot/commands/utils/StringDistancesTest.java index 57095b7b2c..5d05056be8 100644 --- a/application/src/test/java/org/togetherjava/tjbot/commands/utils/StringDistancesTest.java +++ b/application/src/test/java/org/togetherjava/tjbot/commands/utils/StringDistancesTest.java @@ -17,10 +17,12 @@ record TestCase(String name, Collection expectedSuggestions, String pref List tests = List.of(new TestCase("empty_candidates", List.of(), "prefix", List.of(), 0), - new TestCase("empty_prefix", List.of("one", "two", "three"), "", + new TestCase("empty_prefix", List.of("one", "three", "two"), "", List.of("one", "two", "three"), 0), - new TestCase("real_test", List.of("java", "one", "js", "j"), "jo", - List.of("java", "xj", "bs", "one", "yes", "js", "a", "j"), 0.8)); + new TestCase("all_empty", List.of(), "", List.of(), 1), + new TestCase("max_error", List.of("aa"), "a", List.of("json", "one", "aa", "two", "++"), 1), + new TestCase("real_test", List.of("j", "java", "js", "one"), "jo", + List.of("java", "xj", "bs", "one", "yes", "js", "a", "j"), 0.5)); for (TestCase test : tests) { assertEquals( From 77b73f24c4973f8d736485f567df9422a6b95758 Mon Sep 17 00:00:00 2001 From: Taz03 Date: Wed, 5 Oct 2022 22:49:04 +0530 Subject: [PATCH 15/28] spotless --- .../togetherjava/tjbot/commands/utils/StringDistancesTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/application/src/test/java/org/togetherjava/tjbot/commands/utils/StringDistancesTest.java b/application/src/test/java/org/togetherjava/tjbot/commands/utils/StringDistancesTest.java index 5d05056be8..4088a69ed7 100644 --- a/application/src/test/java/org/togetherjava/tjbot/commands/utils/StringDistancesTest.java +++ b/application/src/test/java/org/togetherjava/tjbot/commands/utils/StringDistancesTest.java @@ -20,7 +20,8 @@ record TestCase(String name, Collection expectedSuggestions, String pref new TestCase("empty_prefix", List.of("one", "three", "two"), "", List.of("one", "two", "three"), 0), new TestCase("all_empty", List.of(), "", List.of(), 1), - new TestCase("max_error", List.of("aa"), "a", List.of("json", "one", "aa", "two", "++"), 1), + new TestCase("max_error", List.of("aa"), "a", + List.of("json", "one", "aa", "two", "++"), 1), new TestCase("real_test", List.of("j", "java", "js", "one"), "jo", List.of("java", "xj", "bs", "one", "yes", "js", "a", "j"), 0.5)); From b9d111d9c46ebc2b4236621fc9ab029a750969f7 Mon Sep 17 00:00:00 2001 From: Taz03 Date: Thu, 6 Oct 2022 18:05:42 +0530 Subject: [PATCH 16/28] comments --- .../java/org/togetherjava/tjbot/commands/tags/TagCommand.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/tags/TagCommand.java b/application/src/main/java/org/togetherjava/tjbot/commands/tags/TagCommand.java index 2ff96c5a33..84b858f3b7 100644 --- a/application/src/main/java/org/togetherjava/tjbot/commands/tags/TagCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/commands/tags/TagCommand.java @@ -39,8 +39,6 @@ public TagCommand(TagSystem tagSystem) { this.tagSystem = tagSystem; - // TODO Think about adding an ephemeral selection menu with pagination support - // if the user calls this without id or similar getData().addOptions( new OptionData(OptionType.STRING, ID_OPTION, "The id of the tag to display", true, true), From ea757b6562e016d4058a57d1fb3050253efa87d0 Mon Sep 17 00:00:00 2001 From: Taz03 Date: Thu, 6 Oct 2022 18:18:10 +0530 Subject: [PATCH 17/28] made sonarlint happy --- .../tjbot/commands/utils/StringDistances.java | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/utils/StringDistances.java b/application/src/main/java/org/togetherjava/tjbot/commands/utils/StringDistances.java index d55959a9bc..e688a2538f 100644 --- a/application/src/main/java/org/togetherjava/tjbot/commands/utils/StringDistances.java +++ b/application/src/main/java/org/togetherjava/tjbot/commands/utils/StringDistances.java @@ -61,19 +61,6 @@ public static Optional autocomplete(CharSequence pre */ public static Collection autocompleteSuggestions(CharSequence prefix, Collection candidates, double errorMargin) { - record MatchScore(String candidate, int score) implements Comparable { - @Override - public int compareTo(MatchScore otherMatchScore) { - int compare = Integer.compare(this.score, otherMatchScore.score); - - if (compare == 0) { - return this.candidate.compareTo(otherMatchScore.candidate); - } - - return compare; - } - } - int firstCandidateScore = candidates.stream() .mapToInt(candidate -> prefixEditDistance(prefix, candidate)) .min() @@ -177,4 +164,17 @@ private static int[][] computeLevenshteinDistanceTable(CharSequence source, return table; } + + private record MatchScore(String candidate, int score) implements Comparable { + @Override + public int compareTo(MatchScore otherMatchScore) { + int compare = Integer.compare(this.score, otherMatchScore.score); + + if (compare == 0) { + return this.candidate.compareTo(otherMatchScore.candidate); + } + + return compare; + } + } } From 75b51cc28ae21ad789747ad2d8ab965b8604b1e0 Mon Sep 17 00:00:00 2001 From: Taz03 Date: Thu, 6 Oct 2022 23:18:14 +0530 Subject: [PATCH 18/28] reability++ --- .../togetherjava/tjbot/commands/utils/StringDistances.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/utils/StringDistances.java b/application/src/main/java/org/togetherjava/tjbot/commands/utils/StringDistances.java index e688a2538f..637e3288f4 100644 --- a/application/src/main/java/org/togetherjava/tjbot/commands/utils/StringDistances.java +++ b/application/src/main/java/org/togetherjava/tjbot/commands/utils/StringDistances.java @@ -168,13 +168,13 @@ private static int[][] computeLevenshteinDistanceTable(CharSequence source, private record MatchScore(String candidate, int score) implements Comparable { @Override public int compareTo(MatchScore otherMatchScore) { - int compare = Integer.compare(this.score, otherMatchScore.score); + int compareResult = Integer.compare(this.score, otherMatchScore.score); - if (compare == 0) { + if (compareResult == 0) { return this.candidate.compareTo(otherMatchScore.candidate); } - return compare; + return compareResult; } } } From e195886fc41d1ea4d6f6aec74e3180cf04a643c7 Mon Sep 17 00:00:00 2001 From: Taz03 Date: Fri, 7 Oct 2022 16:34:55 +0530 Subject: [PATCH 19/28] CR --- .../tjbot/commands/tags/TagCommand.java | 11 +++--- .../tjbot/commands/utils/StringDistances.java | 38 +++++++++++-------- .../commands/utils/StringDistancesTest.java | 18 +++------ 3 files changed, 33 insertions(+), 34 deletions(-) diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/tags/TagCommand.java b/application/src/main/java/org/togetherjava/tjbot/commands/tags/TagCommand.java index 84b858f3b7..97b03c6fa6 100644 --- a/application/src/main/java/org/togetherjava/tjbot/commands/tags/TagCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/commands/tags/TagCommand.java @@ -76,12 +76,11 @@ public void onAutoComplete(CommandAutoCompleteInteractionEvent event) { return; } - Collection choices = StringDistances - .autocompleteSuggestions(focusedOption.getValue(), tagSystem.getAllIds(), 0.5) - .stream() - .map(id -> new Command.Choice(id, id)) - .limit(OptionData.MAX_CHOICES) - .toList(); + Collection choices = + StringDistances.closeMatches(focusedOption.getValue(), tagSystem.getAllIds(), OptionData.MAX_CHOICES) + .stream() + .map(id -> new Command.Choice(id, id)) + .toList(); event.replyChoices(choices).queue(); } diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/utils/StringDistances.java b/application/src/main/java/org/togetherjava/tjbot/commands/utils/StringDistances.java index 637e3288f4..15abb44bdf 100644 --- a/application/src/main/java/org/togetherjava/tjbot/commands/utils/StringDistances.java +++ b/application/src/main/java/org/togetherjava/tjbot/commands/utils/StringDistances.java @@ -1,15 +1,19 @@ package org.togetherjava.tjbot.commands.utils; -import java.util.Arrays; -import java.util.Collection; -import java.util.Comparator; -import java.util.Optional; +import java.util.*; import java.util.stream.IntStream; +import java.util.stream.Stream; /** * Utility class for computing string distances, for example the edit distance between two words. */ public class StringDistances { + /** + * Matches that are further off than this are not considered as match anymore. The value is + * between 0.0 (full match) and 1.0 (completely different). + */ + private static final double offByPercentageThreshold = 0.5; + private StringDistances() { throw new UnsupportedOperationException("Utility class, construction not supported"); } @@ -56,24 +60,28 @@ public static Optional autocomplete(CharSequence pre * * @param prefix the prefix to give suggestion for * @param candidates all the possible suggestions - * @param errorMargin error margin of suggestions, error percentage / 100 * @return collection of autocomplete suggestions */ - public static Collection autocompleteSuggestions(CharSequence prefix, - Collection candidates, double errorMargin) { - int firstCandidateScore = candidates.stream() - .mapToInt(candidate -> prefixEditDistance(prefix, candidate)) - .min() - .orElse(0); - return candidates.stream() + public static Collection closeMatches(CharSequence prefix, + Collection candidates, int limit) { + Collection scoredMatches = candidates.stream() .map(candidate -> new MatchScore(candidate, prefixEditDistance(prefix, candidate))) - .sorted() - .takeWhile(matchScore -> matchScore.score() <= firstCandidateScore - + firstCandidateScore * errorMargin) + .toList(); + + Queue bestMatches = new PriorityQueue<>(); + bestMatches.addAll(scoredMatches); + + return Stream.generate(bestMatches::poll) + .takeWhile(matchScore -> isCloseEnough(matchScore, prefix)) .map(MatchScore::candidate) + .limit(limit) .toList(); } + private static boolean isCloseEnough(MatchScore matchScore, CharSequence prefix) { + return matchScore.score / prefix.length() <= offByPercentageThreshold; + } + /** * Distance to receive {@code destination} from {@code source} by editing. *

diff --git a/application/src/test/java/org/togetherjava/tjbot/commands/utils/StringDistancesTest.java b/application/src/test/java/org/togetherjava/tjbot/commands/utils/StringDistancesTest.java index 4088a69ed7..39b84705e3 100644 --- a/application/src/test/java/org/togetherjava/tjbot/commands/utils/StringDistancesTest.java +++ b/application/src/test/java/org/togetherjava/tjbot/commands/utils/StringDistancesTest.java @@ -10,25 +10,17 @@ final class StringDistancesTest { @Test - void autoCompleteSuggestions() { + void closeMatches() { record TestCase(String name, Collection expectedSuggestions, String prefix, - Collection candidates, double errorMargin) { + Collection candidates, int limit) { } - List tests = - List.of(new TestCase("empty_candidates", List.of(), "prefix", List.of(), 0), - new TestCase("empty_prefix", List.of("one", "three", "two"), "", - List.of("one", "two", "three"), 0), - new TestCase("all_empty", List.of(), "", List.of(), 1), - new TestCase("max_error", List.of("aa"), "a", - List.of("json", "one", "aa", "two", "++"), 1), - new TestCase("real_test", List.of("j", "java", "js", "one"), "jo", - List.of("java", "xj", "bs", "one", "yes", "js", "a", "j"), 0.5)); + List tests = List.of(); for (TestCase test : tests) { assertEquals( - test.expectedSuggestions, StringDistances.autocompleteSuggestions(test.prefix, - test.candidates, test.errorMargin), + test.expectedSuggestions, StringDistances.closeMatches(test.prefix, + test.candidates, test.limit), "Test '%s' failed".formatted(test.name)); } } From 954ef2220e516c2a661e00c98c810e31901ccf25 Mon Sep 17 00:00:00 2001 From: Taz03 Date: Fri, 7 Oct 2022 16:46:11 +0530 Subject: [PATCH 20/28] CR --- .../tjbot/commands/utils/StringDistances.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/utils/StringDistances.java b/application/src/main/java/org/togetherjava/tjbot/commands/utils/StringDistances.java index 15abb44bdf..8fc3ca6596 100644 --- a/application/src/main/java/org/togetherjava/tjbot/commands/utils/StringDistances.java +++ b/application/src/main/java/org/togetherjava/tjbot/commands/utils/StringDistances.java @@ -12,7 +12,7 @@ public class StringDistances { * Matches that are further off than this are not considered as match anymore. The value is * between 0.0 (full match) and 1.0 (completely different). */ - private static final double offByPercentageThreshold = 0.5; + private static final double OFF_BY_PERCENTAGE_THRESHOLD = 0.5; private StringDistances() { throw new UnsupportedOperationException("Utility class, construction not supported"); @@ -56,11 +56,12 @@ public static Optional autocomplete(CharSequence pre } /** - * Gives sorted suggestion to autocomplete a prefix string from the given options + * Gives sorted suggestion to autocomplete a prefix string from the given options. * - * @param prefix the prefix to give suggestion for - * @param candidates all the possible suggestions - * @return collection of autocomplete suggestions + * @param prefix the prefix to give matches for + * @param candidates all the possible matches + * @param limit number of matches to generate at max + * @return the matches closest to the given prefix, limited to the given limit */ public static Collection closeMatches(CharSequence prefix, Collection candidates, int limit) { @@ -79,7 +80,7 @@ public static Collection closeMatches(CharSequence prefix, } private static boolean isCloseEnough(MatchScore matchScore, CharSequence prefix) { - return matchScore.score / prefix.length() <= offByPercentageThreshold; + return matchScore.score / prefix.length() <= OFF_BY_PERCENTAGE_THRESHOLD; } /** From e4fd045ec24e08eb454bb8227071a8c1b8a4d9ab Mon Sep 17 00:00:00 2001 From: Taz03 Date: Fri, 7 Oct 2022 16:48:32 +0530 Subject: [PATCH 21/28] spotless --- .../togetherjava/tjbot/commands/tags/TagCommand.java | 10 +++++----- .../tjbot/commands/utils/StringDistancesTest.java | 5 ++--- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/tags/TagCommand.java b/application/src/main/java/org/togetherjava/tjbot/commands/tags/TagCommand.java index 97b03c6fa6..9492313a9c 100644 --- a/application/src/main/java/org/togetherjava/tjbot/commands/tags/TagCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/commands/tags/TagCommand.java @@ -76,11 +76,11 @@ public void onAutoComplete(CommandAutoCompleteInteractionEvent event) { return; } - Collection choices = - StringDistances.closeMatches(focusedOption.getValue(), tagSystem.getAllIds(), OptionData.MAX_CHOICES) - .stream() - .map(id -> new Command.Choice(id, id)) - .toList(); + Collection choices = StringDistances + .closeMatches(focusedOption.getValue(), tagSystem.getAllIds(), OptionData.MAX_CHOICES) + .stream() + .map(id -> new Command.Choice(id, id)) + .toList(); event.replyChoices(choices).queue(); } diff --git a/application/src/test/java/org/togetherjava/tjbot/commands/utils/StringDistancesTest.java b/application/src/test/java/org/togetherjava/tjbot/commands/utils/StringDistancesTest.java index 39b84705e3..0151c938d2 100644 --- a/application/src/test/java/org/togetherjava/tjbot/commands/utils/StringDistancesTest.java +++ b/application/src/test/java/org/togetherjava/tjbot/commands/utils/StringDistancesTest.java @@ -18,9 +18,8 @@ record TestCase(String name, Collection expectedSuggestions, String pref List tests = List.of(); for (TestCase test : tests) { - assertEquals( - test.expectedSuggestions, StringDistances.closeMatches(test.prefix, - test.candidates, test.limit), + assertEquals(test.expectedSuggestions, + StringDistances.closeMatches(test.prefix, test.candidates, test.limit), "Test '%s' failed".formatted(test.name)); } } From 79c7fef4d709cc65418a9adfde874039c6c2d2fc Mon Sep 17 00:00:00 2001 From: Taz03 Date: Fri, 7 Oct 2022 20:48:22 +0530 Subject: [PATCH 22/28] CR --- .../tjbot/commands/tags/TagCommand.java | 14 +++++++------- .../tjbot/commands/utils/StringDistances.java | 12 ++++++++++-- .../tjbot/commands/utils/StringDistancesTest.java | 11 ++++++++++- 3 files changed, 27 insertions(+), 10 deletions(-) diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/tags/TagCommand.java b/application/src/main/java/org/togetherjava/tjbot/commands/tags/TagCommand.java index 9492313a9c..69288ab7e8 100644 --- a/application/src/main/java/org/togetherjava/tjbot/commands/tags/TagCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/commands/tags/TagCommand.java @@ -72,15 +72,15 @@ public void onSlashCommand(SlashCommandInteractionEvent event) { public void onAutoComplete(CommandAutoCompleteInteractionEvent event) { AutoCompleteQuery focusedOption = event.getFocusedOption(); - if (focusedOption.getName().equals(ID_OPTION)) { - return; + if (!focusedOption.getName().equals(ID_OPTION)) { + throw new IllegalArgumentException("Unexpected option"); } - Collection choices = StringDistances - .closeMatches(focusedOption.getValue(), tagSystem.getAllIds(), OptionData.MAX_CHOICES) - .stream() - .map(id -> new Command.Choice(id, id)) - .toList(); + Collection choices = + StringDistances.closeMatches(focusedOption.getValue(), tagSystem.getAllIds(), 5) + .stream() + .map(id -> new Command.Choice(id, id)) + .toList(); event.replyChoices(choices).queue(); } diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/utils/StringDistances.java b/application/src/main/java/org/togetherjava/tjbot/commands/utils/StringDistances.java index 8fc3ca6596..de598659ec 100644 --- a/application/src/main/java/org/togetherjava/tjbot/commands/utils/StringDistances.java +++ b/application/src/main/java/org/togetherjava/tjbot/commands/utils/StringDistances.java @@ -65,6 +65,14 @@ public static Optional autocomplete(CharSequence pre */ public static Collection closeMatches(CharSequence prefix, Collection candidates, int limit) { + if (prefix.isEmpty()) { + return candidates.stream().sorted().limit(limit).toList(); + } + + if (candidates.isEmpty()) { + return candidates; + } + Collection scoredMatches = candidates.stream() .map(candidate -> new MatchScore(candidate, prefixEditDistance(prefix, candidate))) .toList(); @@ -73,14 +81,14 @@ public static Collection closeMatches(CharSequence prefix, bestMatches.addAll(scoredMatches); return Stream.generate(bestMatches::poll) + .limit(limit) .takeWhile(matchScore -> isCloseEnough(matchScore, prefix)) .map(MatchScore::candidate) - .limit(limit) .toList(); } private static boolean isCloseEnough(MatchScore matchScore, CharSequence prefix) { - return matchScore.score / prefix.length() <= OFF_BY_PERCENTAGE_THRESHOLD; + return (double) matchScore.score / prefix.length() <= OFF_BY_PERCENTAGE_THRESHOLD; } /** diff --git a/application/src/test/java/org/togetherjava/tjbot/commands/utils/StringDistancesTest.java b/application/src/test/java/org/togetherjava/tjbot/commands/utils/StringDistancesTest.java index 0151c938d2..e710f14e38 100644 --- a/application/src/test/java/org/togetherjava/tjbot/commands/utils/StringDistancesTest.java +++ b/application/src/test/java/org/togetherjava/tjbot/commands/utils/StringDistancesTest.java @@ -15,7 +15,16 @@ record TestCase(String name, Collection expectedSuggestions, String pref Collection candidates, int limit) { } - List tests = List.of(); + List exampleTags = List.of("c", "c#", "c++", "emacs", "foo", "hello", "java", "js", + "key", "nvim", "py", "tag", "taz", "vi", "vim"); + + List tests = List.of(new TestCase("no_tags", List.of(), "foo", List.of(), 5), + new TestCase("no_prefix", List.of("c", "c#", "c++", "emacs", "foo"), "", + exampleTags, 5), + new TestCase("both_empty", List.of(), "", List.of(), 5), + new TestCase("test0", List.of("vi", "vim"), "v", exampleTags, 5), + new TestCase("test1", List.of("java", "js"), "j", exampleTags, 5), + new TestCase("test2", List.of("c", "c#", "c++"), "c", exampleTags, 5)); for (TestCase test : tests) { assertEquals(test.expectedSuggestions, From ac29be439444cf7d1d11c75521e764e9b70ac94d Mon Sep 17 00:00:00 2001 From: Taz03 Date: Sun, 9 Oct 2022 00:52:07 +0530 Subject: [PATCH 23/28] CR --- .../tjbot/commands/tags/TagCommand.java | 15 ++++++++------- .../tjbot/commands/utils/StringDistances.java | 2 +- .../commands/utils/StringDistancesTest.java | 17 ++++++++++------- 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/tags/TagCommand.java b/application/src/main/java/org/togetherjava/tjbot/commands/tags/TagCommand.java index 69288ab7e8..27cd586dc1 100644 --- a/application/src/main/java/org/togetherjava/tjbot/commands/tags/TagCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/commands/tags/TagCommand.java @@ -25,7 +25,7 @@ */ public final class TagCommand extends SlashCommandAdapter { private final TagSystem tagSystem; - + private static final int MAX_OPTIONS = 5; static final String ID_OPTION = "id"; static final String REPLY_TO_USER_OPTION = "reply-to"; @@ -73,14 +73,15 @@ public void onAutoComplete(CommandAutoCompleteInteractionEvent event) { AutoCompleteQuery focusedOption = event.getFocusedOption(); if (!focusedOption.getName().equals(ID_OPTION)) { - throw new IllegalArgumentException("Unexpected option"); + throw new IllegalArgumentException( + "Unexpected option, was: " + focusedOption.getName()); } - Collection choices = - StringDistances.closeMatches(focusedOption.getValue(), tagSystem.getAllIds(), 5) - .stream() - .map(id -> new Command.Choice(id, id)) - .toList(); + Collection choices = StringDistances + .closeMatches(focusedOption.getValue(), tagSystem.getAllIds(), MAX_OPTIONS) + .stream() + .map(id -> new Command.Choice(id, id)) + .toList(); event.replyChoices(choices).queue(); } diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/utils/StringDistances.java b/application/src/main/java/org/togetherjava/tjbot/commands/utils/StringDistances.java index de598659ec..3e9d714033 100644 --- a/application/src/main/java/org/togetherjava/tjbot/commands/utils/StringDistances.java +++ b/application/src/main/java/org/togetherjava/tjbot/commands/utils/StringDistances.java @@ -70,7 +70,7 @@ public static Collection closeMatches(CharSequence prefix, } if (candidates.isEmpty()) { - return candidates; + return List.of(); } Collection scoredMatches = candidates.stream() diff --git a/application/src/test/java/org/togetherjava/tjbot/commands/utils/StringDistancesTest.java b/application/src/test/java/org/togetherjava/tjbot/commands/utils/StringDistancesTest.java index e710f14e38..b667174901 100644 --- a/application/src/test/java/org/togetherjava/tjbot/commands/utils/StringDistancesTest.java +++ b/application/src/test/java/org/togetherjava/tjbot/commands/utils/StringDistancesTest.java @@ -15,16 +15,19 @@ record TestCase(String name, Collection expectedSuggestions, String pref Collection candidates, int limit) { } - List exampleTags = List.of("c", "c#", "c++", "emacs", "foo", "hello", "java", "js", + List existingTags = List.of("c", "c#", "c++", "emacs", "foo", "hello", "java", "js", "key", "nvim", "py", "tag", "taz", "vi", "vim"); + final int MAX_MATCHES = 5; - List tests = List.of(new TestCase("no_tags", List.of(), "foo", List.of(), 5), + List tests = List.of( + new TestCase("no_tags", List.of(), "foo", List.of(), MAX_MATCHES), new TestCase("no_prefix", List.of("c", "c#", "c++", "emacs", "foo"), "", - exampleTags, 5), - new TestCase("both_empty", List.of(), "", List.of(), 5), - new TestCase("test0", List.of("vi", "vim"), "v", exampleTags, 5), - new TestCase("test1", List.of("java", "js"), "j", exampleTags, 5), - new TestCase("test2", List.of("c", "c#", "c++"), "c", exampleTags, 5)); + existingTags, MAX_MATCHES), + new TestCase("both_empty", List.of(), "", List.of(), MAX_MATCHES), + new TestCase("withPrefix0", List.of("vi", "vim"), "v", existingTags, MAX_MATCHES), + new TestCase("withPrefix1", List.of("java", "js"), "j", existingTags, MAX_MATCHES), + new TestCase("withPrefix2", List.of("c", "c#", "c++"), "c", existingTags, + MAX_MATCHES)); for (TestCase test : tests) { assertEquals(test.expectedSuggestions, From 14795b0f2c4ce7634d6feaf3597b312691d9f347 Mon Sep 17 00:00:00 2001 From: Taz03 Date: Sun, 9 Oct 2022 01:08:28 +0530 Subject: [PATCH 24/28] changed var name --- .../tjbot/commands/utils/StringDistancesTest.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/application/src/test/java/org/togetherjava/tjbot/commands/utils/StringDistancesTest.java b/application/src/test/java/org/togetherjava/tjbot/commands/utils/StringDistancesTest.java index b667174901..4a439e1f7a 100644 --- a/application/src/test/java/org/togetherjava/tjbot/commands/utils/StringDistancesTest.java +++ b/application/src/test/java/org/togetherjava/tjbot/commands/utils/StringDistancesTest.java @@ -15,18 +15,18 @@ record TestCase(String name, Collection expectedSuggestions, String pref Collection candidates, int limit) { } - List existingTags = List.of("c", "c#", "c++", "emacs", "foo", "hello", "java", "js", + List candidates = List.of("c", "c#", "c++", "emacs", "foo", "hello", "java", "js", "key", "nvim", "py", "tag", "taz", "vi", "vim"); final int MAX_MATCHES = 5; List tests = List.of( new TestCase("no_tags", List.of(), "foo", List.of(), MAX_MATCHES), - new TestCase("no_prefix", List.of("c", "c#", "c++", "emacs", "foo"), "", - existingTags, MAX_MATCHES), + new TestCase("no_prefix", List.of("c", "c#", "c++", "emacs", "foo"), "", candidates, + MAX_MATCHES), new TestCase("both_empty", List.of(), "", List.of(), MAX_MATCHES), - new TestCase("withPrefix0", List.of("vi", "vim"), "v", existingTags, MAX_MATCHES), - new TestCase("withPrefix1", List.of("java", "js"), "j", existingTags, MAX_MATCHES), - new TestCase("withPrefix2", List.of("c", "c#", "c++"), "c", existingTags, + new TestCase("withPrefix0", List.of("vi", "vim"), "v", candidates, MAX_MATCHES), + new TestCase("withPrefix1", List.of("java", "js"), "j", candidates, MAX_MATCHES), + new TestCase("withPrefix2", List.of("c", "c#", "c++"), "c", candidates, MAX_MATCHES)); for (TestCase test : tests) { From 6cb93a31cc699c9acef4f823b9ff4d6e04f0b9a1 Mon Sep 17 00:00:00 2001 From: Taz03 Date: Sun, 9 Oct 2022 02:16:39 +0530 Subject: [PATCH 25/28] made score a double --- .../togetherjava/tjbot/commands/utils/StringDistances.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/utils/StringDistances.java b/application/src/main/java/org/togetherjava/tjbot/commands/utils/StringDistances.java index 3e9d714033..c51505d37e 100644 --- a/application/src/main/java/org/togetherjava/tjbot/commands/utils/StringDistances.java +++ b/application/src/main/java/org/togetherjava/tjbot/commands/utils/StringDistances.java @@ -88,7 +88,7 @@ public static Collection closeMatches(CharSequence prefix, } private static boolean isCloseEnough(MatchScore matchScore, CharSequence prefix) { - return (double) matchScore.score / prefix.length() <= OFF_BY_PERCENTAGE_THRESHOLD; + return matchScore.score / prefix.length() <= OFF_BY_PERCENTAGE_THRESHOLD; } /** @@ -182,10 +182,10 @@ private static int[][] computeLevenshteinDistanceTable(CharSequence source, return table; } - private record MatchScore(String candidate, int score) implements Comparable { + private record MatchScore(String candidate, double score) implements Comparable { @Override public int compareTo(MatchScore otherMatchScore) { - int compareResult = Integer.compare(this.score, otherMatchScore.score); + int compareResult = Double.compare(this.score, otherMatchScore.score); if (compareResult == 0) { return this.candidate.compareTo(otherMatchScore.candidate); From 0a00dcf29688fade3d5c519ab17e8cffac212bc8 Mon Sep 17 00:00:00 2001 From: Taz03 Date: Sun, 9 Oct 2022 02:18:29 +0530 Subject: [PATCH 26/28] changed var name --- .../java/org/togetherjava/tjbot/commands/tags/TagCommand.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/tags/TagCommand.java b/application/src/main/java/org/togetherjava/tjbot/commands/tags/TagCommand.java index 27cd586dc1..31e027485c 100644 --- a/application/src/main/java/org/togetherjava/tjbot/commands/tags/TagCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/commands/tags/TagCommand.java @@ -25,7 +25,7 @@ */ public final class TagCommand extends SlashCommandAdapter { private final TagSystem tagSystem; - private static final int MAX_OPTIONS = 5; + private static final int MAX_SUGGESTIONS = 5; static final String ID_OPTION = "id"; static final String REPLY_TO_USER_OPTION = "reply-to"; @@ -78,7 +78,7 @@ public void onAutoComplete(CommandAutoCompleteInteractionEvent event) { } Collection choices = StringDistances - .closeMatches(focusedOption.getValue(), tagSystem.getAllIds(), MAX_OPTIONS) + .closeMatches(focusedOption.getValue(), tagSystem.getAllIds(), MAX_SUGGESTIONS) .stream() .map(id -> new Command.Choice(id, id)) .toList(); From 80db9e1d4c46f4acda84ae0a56363e646aae073d Mon Sep 17 00:00:00 2001 From: Taz03 Date: Sun, 9 Oct 2022 02:35:54 +0530 Subject: [PATCH 27/28] k-sort --- .../tjbot/commands/utils/StringDistances.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/utils/StringDistances.java b/application/src/main/java/org/togetherjava/tjbot/commands/utils/StringDistances.java index c51505d37e..a84c4fc6d3 100644 --- a/application/src/main/java/org/togetherjava/tjbot/commands/utils/StringDistances.java +++ b/application/src/main/java/org/togetherjava/tjbot/commands/utils/StringDistances.java @@ -65,10 +65,6 @@ public static Optional autocomplete(CharSequence pre */ public static Collection closeMatches(CharSequence prefix, Collection candidates, int limit) { - if (prefix.isEmpty()) { - return candidates.stream().sorted().limit(limit).toList(); - } - if (candidates.isEmpty()) { return List.of(); } @@ -80,6 +76,13 @@ public static Collection closeMatches(CharSequence prefix, Queue bestMatches = new PriorityQueue<>(); bestMatches.addAll(scoredMatches); + if (prefix.isEmpty()) { + return Stream.generate(bestMatches::poll) + .limit(limit) + .map(MatchScore::candidate) + .toList(); + } + return Stream.generate(bestMatches::poll) .limit(limit) .takeWhile(matchScore -> isCloseEnough(matchScore, prefix)) From b8c206ee7e8acab58f598c67ca39cab3cd2a8167 Mon Sep 17 00:00:00 2001 From: Taz03 Date: Sun, 9 Oct 2022 11:52:17 +0530 Subject: [PATCH 28/28] reduced duplication --- .../tjbot/commands/utils/StringDistances.java | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/utils/StringDistances.java b/application/src/main/java/org/togetherjava/tjbot/commands/utils/StringDistances.java index a84c4fc6d3..b3f522bb22 100644 --- a/application/src/main/java/org/togetherjava/tjbot/commands/utils/StringDistances.java +++ b/application/src/main/java/org/togetherjava/tjbot/commands/utils/StringDistances.java @@ -76,13 +76,6 @@ public static Collection closeMatches(CharSequence prefix, Queue bestMatches = new PriorityQueue<>(); bestMatches.addAll(scoredMatches); - if (prefix.isEmpty()) { - return Stream.generate(bestMatches::poll) - .limit(limit) - .map(MatchScore::candidate) - .toList(); - } - return Stream.generate(bestMatches::poll) .limit(limit) .takeWhile(matchScore -> isCloseEnough(matchScore, prefix)) @@ -91,6 +84,10 @@ public static Collection closeMatches(CharSequence prefix, } private static boolean isCloseEnough(MatchScore matchScore, CharSequence prefix) { + if (prefix.isEmpty()) { + return true; + } + return matchScore.score / prefix.length() <= OFF_BY_PERCENTAGE_THRESHOLD; }