From b49f0d63e62ab265f30b69ec39a536a59a72d469 Mon Sep 17 00:00:00 2001
From: AratakiLeo <83653555+aratakileo@users.noreply.github.com>
Date: Tue, 12 Sep 2023 17:03:17 +0300
Subject: [PATCH] Refactor code

---
 README.md                                     |  2 +-
 gradle.properties                             |  2 +-
 .../suggestionsapi/SuggestionsAPI.java        |  1 +
 .../core/SuggestionsProcessor.java            | 69 ++++++++++++-------
 .../injector/AsyncInjector.java               | 12 ++++
 .../{suggestion => injector}/Injector.java    | 53 +++-----------
 .../SuggestionsInjector.java                  |  3 +-
 .../suggestion/AsyncInjector.java             | 38 ----------
 8 files changed, 72 insertions(+), 108 deletions(-)
 create mode 100644 src/main/java/io/github/aratakileo/suggestionsapi/injector/AsyncInjector.java
 rename src/main/java/io/github/aratakileo/suggestionsapi/{suggestion => injector}/Injector.java (66%)
 rename src/main/java/io/github/aratakileo/suggestionsapi/{suggestion => injector}/SuggestionsInjector.java (67%)
 delete mode 100644 src/main/java/io/github/aratakileo/suggestionsapi/suggestion/AsyncInjector.java

diff --git a/README.md b/README.md
index 09e4571..2c5dd8b 100644
--- a/README.md
+++ b/README.md
@@ -91,7 +91,7 @@ SuggestionsAPI.registerResourceDependedInjector(
 ```
 
 ### How to dynamically inject suggestions?
-The `Injector` interface is located in the directory `io.github.aratakileo.suggestionsapi.suggestion`.
+The `Injector` interface is located in the directory `io.github.aratakileo.suggestionsapi.injector`.
 
 There are two types of injectors: simple and asynchronous. To initialize them, the library also provides functions. The first argument of which will be a regex pattern.
 
diff --git a/gradle.properties b/gradle.properties
index 68a989d..f2be7f8 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -8,7 +8,7 @@ loader_version=0.14.22
 
 # Mod Properties
 support_minecraft_version = 1.20.x
-mod_version = 1.0.0
+mod_version = 1.0.1
 prev_mod_version = 1.0.0
 maven_group = io.github.aratakileo
 archives_base_name = suggestions-api
diff --git a/src/main/java/io/github/aratakileo/suggestionsapi/SuggestionsAPI.java b/src/main/java/io/github/aratakileo/suggestionsapi/SuggestionsAPI.java
index ba5da34..c3665b9 100644
--- a/src/main/java/io/github/aratakileo/suggestionsapi/SuggestionsAPI.java
+++ b/src/main/java/io/github/aratakileo/suggestionsapi/SuggestionsAPI.java
@@ -1,5 +1,6 @@
 package io.github.aratakileo.suggestionsapi;
 
+import io.github.aratakileo.suggestionsapi.injector.Injector;
 import io.github.aratakileo.suggestionsapi.suggestion.*;
 import io.github.aratakileo.suggestionsapi.core.SuggestionsProcessor;
 import net.fabricmc.api.ClientModInitializer;
diff --git a/src/main/java/io/github/aratakileo/suggestionsapi/core/SuggestionsProcessor.java b/src/main/java/io/github/aratakileo/suggestionsapi/core/SuggestionsProcessor.java
index 58221e2..7866fbf 100644
--- a/src/main/java/io/github/aratakileo/suggestionsapi/core/SuggestionsProcessor.java
+++ b/src/main/java/io/github/aratakileo/suggestionsapi/core/SuggestionsProcessor.java
@@ -1,10 +1,10 @@
 package io.github.aratakileo.suggestionsapi.core;
 
 import com.mojang.brigadier.context.StringRange;
-import io.github.aratakileo.suggestionsapi.suggestion.AsyncInjector;
-import io.github.aratakileo.suggestionsapi.suggestion.Injector;
+import io.github.aratakileo.suggestionsapi.injector.AsyncInjector;
+import io.github.aratakileo.suggestionsapi.injector.Injector;
 import io.github.aratakileo.suggestionsapi.suggestion.Suggestion;
-import io.github.aratakileo.suggestionsapi.suggestion.SuggestionsInjector;
+import io.github.aratakileo.suggestionsapi.injector.SuggestionsInjector;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 import org.slf4j.Logger;
@@ -14,7 +14,7 @@
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
-import java.util.concurrent.Callable;
+import java.util.concurrent.CompletableFuture;
 import java.util.function.BiConsumer;
 import java.util.function.Consumer;
 import java.util.regex.Pattern;
@@ -22,6 +22,7 @@
 public class SuggestionsProcessor {
     private static final Pattern WHITESPACE_PATTERN = Pattern.compile("(\\s+)");
     private static final Logger LOGGER = LoggerFactory.getLogger(SuggestionsProcessor.class);
+    private static final HashMap<AsyncInjector, CompletableFuture<Void>> asyncProcessors = new HashMap<>();
 
     private final String textUptoCursor;
     private final int wordStart;
@@ -51,7 +52,7 @@ public boolean process() {
 
         final var currentExpression = textUptoCursor.substring(wordStart);
         final var suggestionsInjectorsBuffer = new HashMap<SuggestionsInjector, Collection<Suggestion>>();
-        final var asyncInjectorsBuffer = new ArrayList<AsyncInjector>();
+        final var asyncInjectorsBuffer = new HashMap<AsyncInjector, Runnable>();
 
         for (final var injector: injectors) {
             var isActiveInjector = false;
@@ -71,31 +72,38 @@ public boolean process() {
             if (injector instanceof AsyncInjector asyncInjector) {
                 isValidInjector = true;
 
-                final var willApplySuggestions = asyncInjector.initAsyncApplier(
-                        currentExpression,
-                        suggestionList -> {
-                            if (suggestionList == null || suggestionList.isEmpty()) return;
+                final var asyncApplier = asyncInjector.getAsyncApplier(currentExpression);
 
-                            final var mojangSuggestions = new ArrayList<com.mojang.brigadier.suggestion.Suggestion>();
-                            final var offset = injector.getStartOffset();
+                if (asyncApplier != null) {
+                    asyncInjectorsBuffer.put(asyncInjector, () -> {
+                        final var suggestionList = asyncApplier.get();
 
-                            suggestionList.forEach(suggestion -> {
-                                if (suggestion.shouldShowFor(currentExpression.substring(offset)))
-                                    mojangSuggestions.add(new com.mojang.brigadier.suggestion.Suggestion(
-                                            StringRange.between(wordStart + offset, textUptoCursor.length()),
-                                            suggestion.getSuggestionText()
-                                    ));
-                            });
+                        if (suggestionList == null || suggestionList.isEmpty()) {
+                            asyncProcessors.remove(asyncInjector);
+                            return;
+                        }
+
+                        final var mojangSuggestions = new ArrayList<com.mojang.brigadier.suggestion.Suggestion>();
+                        final var offset = injector.getStartOffset();
 
-                            if (mojangSuggestions.isEmpty()) return;
+                        suggestionList.forEach(suggestion -> {
+                            if (suggestion.shouldShowFor(currentExpression.substring(offset)))
+                                mojangSuggestions.add(new com.mojang.brigadier.suggestion.Suggestion(
+                                        StringRange.between(wordStart + offset, textUptoCursor.length()),
+                                        suggestion.getSuggestionText()
+                                ));
+                        });
 
-                            newSuggestionsApplier.accept(textUptoCursor, mojangSuggestions);
+                        if (mojangSuggestions.isEmpty()) {
+                            asyncProcessors.remove(asyncInjector);
+                            return;
                         }
-                );
 
-                if (willApplySuggestions) {
-                    asyncInjectorsBuffer.add(asyncInjector);
+                        newSuggestionsApplier.accept(textUptoCursor, mojangSuggestions);
+                    });
+
                     isActiveInjector = true;
+                    asyncProcessors.remove(asyncInjector);
                 }
             }
 
@@ -158,12 +166,23 @@ public boolean process() {
 
         var hasUsedAsyncInjector = false;
 
-        for (final var injector: asyncInjectorsBuffer) {
+        for (final var injectorEntry: asyncInjectorsBuffer.entrySet()) {
+            final var injector = injectorEntry.getKey();
+
             if (minOffset != -1 && injector.isIsolated() && injector.getStartOffset() > minOffset) continue;
 
             hasUsedAsyncInjector = true;
 
-            injector.runAsyncApplier();
+            CompletableFuture<Void> currentCompletableFuture;
+
+            if (
+                    asyncProcessors.containsKey(injector) && !(
+                            currentCompletableFuture = asyncProcessors.get(injector)
+                    ).isDone()
+            )
+                currentCompletableFuture.cancel(true);
+
+            asyncProcessors.put(injector, CompletableFuture.runAsync(injectorEntry.getValue()));
         }
 
         if (applicableMojangSuggestions.isEmpty() && !hasUsedAsyncInjector) return false;
diff --git a/src/main/java/io/github/aratakileo/suggestionsapi/injector/AsyncInjector.java b/src/main/java/io/github/aratakileo/suggestionsapi/injector/AsyncInjector.java
new file mode 100644
index 0000000..8e6b516
--- /dev/null
+++ b/src/main/java/io/github/aratakileo/suggestionsapi/injector/AsyncInjector.java
@@ -0,0 +1,12 @@
+package io.github.aratakileo.suggestionsapi.injector;
+
+import io.github.aratakileo.suggestionsapi.suggestion.Suggestion;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+import java.util.function.Supplier;
+
+public interface AsyncInjector extends Injector {
+    @Nullable Supplier<@Nullable List<Suggestion>> getAsyncApplier(@NotNull String currentExpression);
+}
diff --git a/src/main/java/io/github/aratakileo/suggestionsapi/suggestion/Injector.java b/src/main/java/io/github/aratakileo/suggestionsapi/injector/Injector.java
similarity index 66%
rename from src/main/java/io/github/aratakileo/suggestionsapi/suggestion/Injector.java
rename to src/main/java/io/github/aratakileo/suggestionsapi/injector/Injector.java
index 9aa72a4..a9adb4a 100644
--- a/src/main/java/io/github/aratakileo/suggestionsapi/suggestion/Injector.java
+++ b/src/main/java/io/github/aratakileo/suggestionsapi/injector/Injector.java
@@ -1,13 +1,12 @@
-package io.github.aratakileo.suggestionsapi.suggestion;
+package io.github.aratakileo.suggestionsapi.injector;
 
-import io.github.aratakileo.suggestionsapi.util.TripleFunction;
+import io.github.aratakileo.suggestionsapi.suggestion.Suggestion;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 import java.util.List;
-import java.util.concurrent.CompletableFuture;
 import java.util.function.BiFunction;
-import java.util.function.Consumer;
+import java.util.function.Supplier;
 import java.util.regex.Pattern;
 
 public interface Injector {
@@ -60,11 +59,10 @@ public boolean isIsolated() {
 
     static @NotNull AsyncInjector async(
             @NotNull Pattern pattern,
-            TripleFunction<
+            BiFunction<
                     @NotNull String,
                     @NotNull Integer,
-                    @NotNull Consumer<@Nullable List<Suggestion>>,
-                    @Nullable Runnable
+                    @Nullable List<Suggestion>
                     > uncheckedSupplierGetter
     ) {
         return async(pattern, uncheckedSupplierGetter, false);
@@ -72,58 +70,29 @@ public boolean isIsolated() {
 
     static @NotNull AsyncInjector async(
             @NotNull Pattern pattern,
-            TripleFunction<
+            BiFunction<
                     @NotNull String,
                     @NotNull Integer,
-                    @NotNull Consumer<@Nullable List<Suggestion>>,
-                    @Nullable Runnable
+                    @Nullable List<Suggestion>
                     > uncheckedSupplierGetter,
             boolean isIsolated
     ) {
         return new AsyncInjector() {
             private int startOffset = 0;
-            private CompletableFuture<Void> currentProcess = null;
-            private Runnable applier = null;
 
             @Override
             @Nullable
-            public CompletableFuture<Void> getCurrentProcess() {
-                return currentProcess;
-            }
-
-            @Override
-            public void setCurrentProcess(@Nullable CompletableFuture<Void> currentProcess) {
-                this.currentProcess = currentProcess;
-            }
-
-            @Override
-            public @Nullable Runnable getApplier() {
-                return applier;
-            }
-
-            @Override
-            public void setApplier(@Nullable Runnable applier) {
-                this.applier = applier;
-            }
-
-            @Override
-            public boolean initAsyncApplier(
-                    @NotNull String currentExpression,
-                    @NotNull Consumer<@Nullable List<Suggestion>> applier
+            public Supplier<@Nullable List<Suggestion>> getAsyncApplier(
+                    @NotNull String currentExpression
             ) {
                 final var lastMatchedStart = getLastMatchedStart(pattern, currentExpression);
 
                 if (lastMatchedStart == -1)
-                    return false;
-
-                AsyncInjector.setApplier(
-                        this,
-                        uncheckedSupplierGetter.apply(currentExpression, lastMatchedStart, applier)
-                );
+                    return null;
 
                 startOffset = lastMatchedStart;
 
-                return this.applier != null;
+                return () -> uncheckedSupplierGetter.apply(currentExpression, lastMatchedStart);
             }
 
             @Override
diff --git a/src/main/java/io/github/aratakileo/suggestionsapi/suggestion/SuggestionsInjector.java b/src/main/java/io/github/aratakileo/suggestionsapi/injector/SuggestionsInjector.java
similarity index 67%
rename from src/main/java/io/github/aratakileo/suggestionsapi/suggestion/SuggestionsInjector.java
rename to src/main/java/io/github/aratakileo/suggestionsapi/injector/SuggestionsInjector.java
index ca33ef8..00bb057 100644
--- a/src/main/java/io/github/aratakileo/suggestionsapi/suggestion/SuggestionsInjector.java
+++ b/src/main/java/io/github/aratakileo/suggestionsapi/injector/SuggestionsInjector.java
@@ -1,5 +1,6 @@
-package io.github.aratakileo.suggestionsapi.suggestion;
+package io.github.aratakileo.suggestionsapi.injector;
 
+import io.github.aratakileo.suggestionsapi.suggestion.Suggestion;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
diff --git a/src/main/java/io/github/aratakileo/suggestionsapi/suggestion/AsyncInjector.java b/src/main/java/io/github/aratakileo/suggestionsapi/suggestion/AsyncInjector.java
deleted file mode 100644
index dad2b8f..0000000
--- a/src/main/java/io/github/aratakileo/suggestionsapi/suggestion/AsyncInjector.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package io.github.aratakileo.suggestionsapi.suggestion;
-
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.util.List;
-import java.util.concurrent.CompletableFuture;
-import java.util.function.Consumer;
-
-public interface AsyncInjector extends Injector {
-    @Nullable CompletableFuture<Void> getCurrentProcess();
-
-    void setCurrentProcess(@Nullable CompletableFuture<Void> currentProcess);
-
-    @Nullable Runnable getApplier();
-
-    void setApplier(@Nullable Runnable applier);
-
-    default void runAsyncApplier() {
-        if (getApplier() == null) return;
-
-        setCurrentProcess(CompletableFuture.runAsync(getApplier()));
-    }
-
-    boolean initAsyncApplier(
-            @NotNull String currentExpression,
-            @NotNull Consumer<@Nullable List<Suggestion>> applier
-    );
-
-    static void setApplier(@NotNull AsyncInjector asyncInjector, @Nullable Runnable applier) {
-        final var currentProcess = asyncInjector.getCurrentProcess();
-
-        if (currentProcess != null && !currentProcess.isDone())
-            currentProcess.cancel(true);
-
-        asyncInjector.setApplier(applier);
-    }
-}