From 05069aa62b09f02a8cd6e526ec58a30347a56500 Mon Sep 17 00:00:00 2001 From: shedaniel Date: Wed, 27 Jul 2022 23:25:27 +0800 Subject: WIP Module --- .../search/.gradle/loom-cache/launch.cfg | 14 + runtime-engine/search/.gradle/loom-cache/log4j.xml | 63 ++++ .../search/.gradle/loom-cache/remapClasspath.txt | 1 + .../libs/search-transformProductionFabric.jar | Bin 0 -> 166 bytes .../build/libs/search-transformProductionForge.jar | Bin 0 -> 166 bytes .../shedaniel/rei/impl/client/search/IntRange.java | 27 ++ .../rei/impl/client/search/SearchProviderImpl.java | 129 ++++++++ .../search/argument/AlternativeArgument.java | 74 +++++ .../rei/impl/client/search/argument/Argument.java | 360 +++++++++++++++++++++ .../client/search/argument/CompoundArgument.java | 88 +++++ .../rei/impl/client/search/argument/IndexSet.java | 173 ++++++++++ .../client/search/argument/InputMethodMatcher.java | 129 ++++++++ .../argument/type/AlwaysMatchingArgumentType.java | 60 ++++ .../client/search/argument/type/ArgumentType.java | 101 ++++++ .../argument/type/ArgumentTypesRegistry.java | 52 +++ .../argument/type/IdentifierArgumentType.java | 88 +++++ .../client/search/argument/type/MatchType.java | 41 +++ .../search/argument/type/ModArgumentType.java | 103 ++++++ .../search/argument/type/RegexArgumentType.java | 99 ++++++ .../search/argument/type/TagArgumentType.java | 92 ++++++ .../search/argument/type/TextArgumentType.java | 75 +++++ .../search/argument/type/TooltipArgumentType.java | 123 +++++++ .../client/search/method/DefaultInputMethod.java | 76 +++++ .../search/method/InputMethodRegistryImpl.java | 110 +++++++ .../search/result/ArgumentApplicableResult.java | 122 +++++++ .../client/runtime/SearchFilterPrepareWatcher.java | 91 ++++++ ....shedaniel.rei.api.client.search.SearchProvider | 1 + ...hedaniel.rei.impl.client.gui.hints.HintProvider | 1 + 28 files changed, 2293 insertions(+) create mode 100644 runtime-engine/search/.gradle/loom-cache/launch.cfg create mode 100644 runtime-engine/search/.gradle/loom-cache/log4j.xml create mode 100644 runtime-engine/search/.gradle/loom-cache/remapClasspath.txt create mode 100644 runtime-engine/search/build/libs/search-transformProductionFabric.jar create mode 100644 runtime-engine/search/build/libs/search-transformProductionForge.jar create mode 100644 runtime-engine/search/src/main/java/me/shedaniel/rei/impl/client/search/IntRange.java create mode 100644 runtime-engine/search/src/main/java/me/shedaniel/rei/impl/client/search/SearchProviderImpl.java create mode 100644 runtime-engine/search/src/main/java/me/shedaniel/rei/impl/client/search/argument/AlternativeArgument.java create mode 100644 runtime-engine/search/src/main/java/me/shedaniel/rei/impl/client/search/argument/Argument.java create mode 100644 runtime-engine/search/src/main/java/me/shedaniel/rei/impl/client/search/argument/CompoundArgument.java create mode 100644 runtime-engine/search/src/main/java/me/shedaniel/rei/impl/client/search/argument/IndexSet.java create mode 100644 runtime-engine/search/src/main/java/me/shedaniel/rei/impl/client/search/argument/InputMethodMatcher.java create mode 100644 runtime-engine/search/src/main/java/me/shedaniel/rei/impl/client/search/argument/type/AlwaysMatchingArgumentType.java create mode 100644 runtime-engine/search/src/main/java/me/shedaniel/rei/impl/client/search/argument/type/ArgumentType.java create mode 100644 runtime-engine/search/src/main/java/me/shedaniel/rei/impl/client/search/argument/type/ArgumentTypesRegistry.java create mode 100644 runtime-engine/search/src/main/java/me/shedaniel/rei/impl/client/search/argument/type/IdentifierArgumentType.java create mode 100644 runtime-engine/search/src/main/java/me/shedaniel/rei/impl/client/search/argument/type/MatchType.java create mode 100644 runtime-engine/search/src/main/java/me/shedaniel/rei/impl/client/search/argument/type/ModArgumentType.java create mode 100644 runtime-engine/search/src/main/java/me/shedaniel/rei/impl/client/search/argument/type/RegexArgumentType.java create mode 100644 runtime-engine/search/src/main/java/me/shedaniel/rei/impl/client/search/argument/type/TagArgumentType.java create mode 100644 runtime-engine/search/src/main/java/me/shedaniel/rei/impl/client/search/argument/type/TextArgumentType.java create mode 100644 runtime-engine/search/src/main/java/me/shedaniel/rei/impl/client/search/argument/type/TooltipArgumentType.java create mode 100644 runtime-engine/search/src/main/java/me/shedaniel/rei/impl/client/search/method/DefaultInputMethod.java create mode 100644 runtime-engine/search/src/main/java/me/shedaniel/rei/impl/client/search/method/InputMethodRegistryImpl.java create mode 100644 runtime-engine/search/src/main/java/me/shedaniel/rei/impl/client/search/result/ArgumentApplicableResult.java create mode 100644 runtime-engine/search/src/main/java/me/shedaniel/rei/plugin/client/runtime/SearchFilterPrepareWatcher.java create mode 100644 runtime-engine/search/src/main/resources/META-INF/services/me.shedaniel.rei.api.client.search.SearchProvider create mode 100644 runtime-engine/search/src/main/resources/META-INF/services/me.shedaniel.rei.impl.client.gui.hints.HintProvider (limited to 'runtime-engine/search') diff --git a/runtime-engine/search/.gradle/loom-cache/launch.cfg b/runtime-engine/search/.gradle/loom-cache/launch.cfg new file mode 100644 index 000000000..30b5dde0a --- /dev/null +++ b/runtime-engine/search/.gradle/loom-cache/launch.cfg @@ -0,0 +1,14 @@ +commonProperties + fabric.development=true + fabric.remapClasspathFile=/home/shedaniel/Documents/JavaCoding/RoughlyEnoughItems-117/runtime-engine/search/.gradle/loom-cache/remapClasspath.txt + log4j.configurationFile=/home/shedaniel/Documents/JavaCoding/RoughlyEnoughItems-117/runtime-engine/search/.gradle/loom-cache/log4j.xml + log4j2.formatMsgNoLookups=true + fabric.log.disableAnsi=false +clientArgs + --assetIndex + 1.18.2-1.18 + --assetsDir + /home/shedaniel/.gradle/caches/fabric-loom/assets +clientProperties + java.library.path=/home/shedaniel/Documents/JavaCoding/RoughlyEnoughItems-117/.gradle/loom-cache/natives/1.18.2 + org.lwjgl.librarypath=/home/shedaniel/Documents/JavaCoding/RoughlyEnoughItems-117/.gradle/loom-cache/natives/1.18.2 \ No newline at end of file diff --git a/runtime-engine/search/.gradle/loom-cache/log4j.xml b/runtime-engine/search/.gradle/loom-cache/log4j.xml new file mode 100644 index 000000000..c009f9fb4 --- /dev/null +++ b/runtime-engine/search/.gradle/loom-cache/log4j.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/runtime-engine/search/.gradle/loom-cache/remapClasspath.txt b/runtime-engine/search/.gradle/loom-cache/remapClasspath.txt new file mode 100644 index 000000000..01f75adc1 --- /dev/null +++ b/runtime-engine/search/.gradle/loom-cache/remapClasspath.txt @@ -0,0 +1 @@ +/home/shedaniel/.gradle/caches/modules-2/files-2.1/net.fabricmc/tiny-mappings-parser/0.3.0+build.17/2f10540a290e382a7cd35c16ec3900046a4e252/tiny-mappings-parser-0.3.0+build.17.jar:/home/shedaniel/.gradle/caches/modules-2/files-2.1/net.fabricmc/sponge-mixin/0.11.2+mixin.0.8.5/f4ae569d559b5e3244b67321945261d83ff7ad8e/sponge-mixin-0.11.2+mixin.0.8.5.jar:/home/shedaniel/.gradle/caches/modules-2/files-2.1/net.fabricmc/tiny-remapper/0.8.1/d220c092cd1446d5f2668b53e71abe23a12cb8cf/tiny-remapper-0.8.1.jar:/home/shedaniel/.gradle/caches/modules-2/files-2.1/net.fabricmc/access-widener/2.1.0/f62a27adbfd8ab4d4fa5681793039f2c0b177155/access-widener-2.1.0.jar:/home/shedaniel/.gradle/caches/modules-2/files-2.1/org.ow2.asm/asm/9.2/81a03f76019c67362299c40e0ba13405f5467bff/asm-9.2.jar:/home/shedaniel/.gradle/caches/modules-2/files-2.1/org.ow2.asm/asm-analysis/9.2/7487dd756daf96cab9986e44b9d7bcb796a61c10/asm-analysis-9.2.jar:/home/shedaniel/.gradle/caches/modules-2/files-2.1/org.ow2.asm/asm-commons/9.2/f4d7f0fc9054386f2893b602454d48e07d4fbead/asm-commons-9.2.jar:/home/shedaniel/.gradle/caches/modules-2/files-2.1/org.ow2.asm/asm-tree/9.2/d96c99a30f5e1a19b0e609dbb19a44d8518ac01e/asm-tree-9.2.jar:/home/shedaniel/.gradle/caches/modules-2/files-2.1/org.ow2.asm/asm-util/9.2/fbc178fc5ba3dab50fd7e8a5317b8b647c8e8946/asm-util-9.2.jar:/home/shedaniel/.gradle/caches/modules-2/files-2.1/com.mojang/blocklist/1.0.10/5c685c5ffa94c4cd39496c7184c1d122e515ecef/blocklist-1.0.10.jar:/home/shedaniel/.gradle/caches/modules-2/files-2.1/com.mojang/patchy/2.2.10/da05971b07cbb379d002cf7eaec6a2048211fefc/patchy-2.2.10.jar:/home/shedaniel/.gradle/caches/modules-2/files-2.1/com.ibm.icu/icu4j/70.1/dfa3a1fbc55bf5db8c6e79fc0935ac7ab1202950/icu4j-70.1.jar:/home/shedaniel/.gradle/caches/modules-2/files-2.1/commons-codec/commons-codec/1.15/49d94806b6e3dc933dacbd8acb0fdbab8ebd1e5d/commons-codec-1.15.jar:/home/shedaniel/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-compress/1.21/4ec95b60d4e86b5c95a0e919cb172a0af98011ef/commons-compress-1.21.jar:/home/shedaniel/.gradle/caches/modules-2/files-2.1/org.apache.httpcomponents/httpclient/4.5.13/e5f6cae5ca7ecaac1ec2827a9e2d65ae2869cada/httpclient-4.5.13.jar:/home/shedaniel/.gradle/caches/modules-2/files-2.1/commons-logging/commons-logging/1.2/4bfc12adfe4842bf07b657f0369c4cb522955686/commons-logging-1.2.jar:/home/shedaniel/.gradle/caches/modules-2/files-2.1/org.apache.httpcomponents/httpcore/4.4.14/9dd1a631c082d92ecd4bd8fd4cf55026c720a8c1/httpcore-4.4.14.jar:/home/shedaniel/.gradle/caches/modules-2/files-2.1/org.lwjgl/lwjgl/3.2.2/8ad6294407e15780b43e84929c40e4c5e997972e/lwjgl-3.2.2.jar:/home/shedaniel/.gradle/caches/modules-2/files-2.1/org.lwjgl/lwjgl-jemalloc/3.2.2/ee8e57a79300f78294576d87c4a587f8c99402e2/lwjgl-jemalloc-3.2.2.jar:/home/shedaniel/.gradle/caches/modules-2/files-2.1/org.lwjgl/lwjgl-openal/3.2.2/2b772a102b0a11ee5f2109a5b136f4dc7c630827/lwjgl-openal-3.2.2.jar:/home/shedaniel/.gradle/caches/modules-2/files-2.1/org.lwjgl/lwjgl-opengl/3.2.2/6ac5bb88b44c43ea195a570aab059f63da004cd8/lwjgl-opengl-3.2.2.jar:/home/shedaniel/.gradle/caches/modules-2/files-2.1/org.lwjgl/lwjgl-glfw/3.2.2/d3ad4df38e400b8afba1de63f84338809399df5b/lwjgl-glfw-3.2.2.jar:/home/shedaniel/.gradle/caches/modules-2/files-2.1/org.lwjgl/lwjgl-stb/3.2.2/3b8e6ebc5851dd3d17e37e5cadce2eff2a429f0f/lwjgl-stb-3.2.2.jar:/home/shedaniel/.gradle/caches/modules-2/files-2.1/org.lwjgl/lwjgl-tinyfd/3.2.2/fcbe606c8f8da6f8f9a05e2c540eb1ee8632b0e9/lwjgl-tinyfd-3.2.2.jar:/home/shedaniel/.gradle/caches/modules-2/files-2.1/com.mojang/text2speech/1.12.4/1f618f522dbdd93218c270bcfd8f8dd84be31717/text2speech-1.12.4.jar:/home/shedaniel/.gradle/caches/modules-2/files-2.1/com.mojang/logging/1.0.0/f6ca3b2eee0b80b384e8ed93d368faecb82dfb9b/logging-1.0.0.jar:/home/shedaniel/.gradle/caches/modules-2/files-2.1/com.github.oshi/oshi-core/5.8.5/1d0ec654d820741327f5a9229d513732a4b7ce50/oshi-core-5.8.5.jar:/home/shedaniel/.gradle/caches/modules-2/files-2.1/net.java.dev.jna/jna/5.10.0/7cf4c87dd802db50721db66947aa237d7ad09418/jna-5.10.0.jar:/home/shedaniel/.gradle/caches/modules-2/files-2.1/net.java.dev.jna/jna-platform/5.10.0/fbed7d9669dba47714ad0d4f4454290a997aee69/jna-platform-5.10.0.jar:/home/shedaniel/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.8.0-beta4/83b0359d847ee053d745be7ec0d8e9e8a44304b4/slf4j-api-1.8.0-beta4.jar:/home/shedaniel/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-slf4j18-impl/2.17.0/bd7f6c0b9224dd214afb4e684957e2349b529a8d/log4j-slf4j18-impl-2.17.0.jar:/home/shedaniel/.gradle/caches/modules-2/files-2.1/com.mojang/javabridge/1.2.24/c876796229b2ef5120f186eab5acc870699d3b9/javabridge-1.2.24.jar:/home/shedaniel/.gradle/caches/modules-2/files-2.1/net.sf.jopt-simple/jopt-simple/5.0.4/4fdac2fbe92dfad86aa6e9301736f6b4342a3f5c/jopt-simple-5.0.4.jar:/home/shedaniel/.gradle/caches/modules-2/files-2.1/io.netty/netty-all/4.1.68.Final/b8266a3c93c1c051109f71d3449e5dcd5d60b333/netty-all-4.1.68.Final.jar:/home/shedaniel/.gradle/caches/modules-2/files-2.1/com.google.guava/failureaccess/1.0.1/1dcf1de382a0bf95a3d8b0849546c88bac1292c9/failureaccess-1.0.1.jar:/home/shedaniel/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/31.0.1-jre/119ea2b2bc205b138974d351777b20f02b92704b/guava-31.0.1-jre.jar:/home/shedaniel/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.12.0/c6842c86792ff03b9f1d1fe2aab8dc23aa6c6f0e/commons-lang3-3.12.0.jar:/home/shedaniel/.gradle/caches/modules-2/files-2.1/commons-io/commons-io/2.11.0/a2503f302b11ebde7ebc3df41daebe0e4eea3689/commons-io-2.11.0.jar:/home/shedaniel/.gradle/caches/modules-2/files-2.1/com.mojang/brigadier/1.0.18/c1ef1234282716483c92183f49bef47b1a89bfa9/brigadier-1.0.18.jar:/home/shedaniel/.gradle/caches/modules-2/files-2.1/com.mojang/datafixerupper/4.1.27/a02c43824ce57c3f7a7d7e744f0d99a040398b5/datafixerupper-4.1.27.jar:/home/shedaniel/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.9/8a432c1d6825781e21a02db2e2c33c5fde2833b9/gson-2.8.9.jar:/home/shedaniel/.gradle/caches/modules-2/files-2.1/com.mojang/authlib/3.3.39/289405e70c0917eaeac017f7fba9adb4427baa36/authlib-3.3.39.jar:/home/shedaniel/.gradle/caches/modules-2/files-2.1/it.unimi.dsi/fastutil/8.5.6/76f95700418a68fbc4ac050525261f05dc681ca1/fastutil-8.5.6.jar:/home/shedaniel/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.17.0/bbd791e9c8c9421e45337c4fe0a10851c086e36c/log4j-api-2.17.0.jar:/home/shedaniel/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.17.0/fe6e7a32c1228884b9691a744f953a55d0dd8ead/log4j-core-2.17.0.jar:/home/shedaniel/.gradle/caches/modules-2/files-2.1/me.shedaniel.cloth/cloth-config/6.2.57/51ec014dad95d65d070f9ef8a715966561998d3b/cloth-config-6.2.57.jar:/home/shedaniel/.gradle/caches/modules-2/files-2.1/dev.architectury/architectury/4.5.75/aec16eaf48959ed6de2c446609dfa70ed8a8b4dd/architectury-4.5.75.jar:/home/shedaniel/.gradle/caches/modules-2/files-2.1/me.shedaniel.cloth/basic-math/0.6.0/bcf71444d5a11d1ff0b70ded97c533ece0cea489/basic-math-0.6.0.jar:/home/shedaniel/.gradle/caches/modules-2/files-2.1/net.fabricmc/fabric-loader/0.13.3/a91fc6ea7a5e8c3140dfaf34aeadb43ce2f38fe2/fabric-loader-0.13.3.jar:/home/shedaniel/.gradle/caches/modules-2/files-2.1/net.fabricmc/fabric-loader/0.13.3/a91fc6ea7a5e8c3140dfaf34aeadb43ce2f38fe2/fabric-loader-0.13.3.jar:/home/shedaniel/.gradle/caches/fabric-loom/1.18.2/minecraft-merged-intermediary.jar \ No newline at end of file diff --git a/runtime-engine/search/build/libs/search-transformProductionFabric.jar b/runtime-engine/search/build/libs/search-transformProductionFabric.jar new file mode 100644 index 000000000..298b693b1 Binary files /dev/null and b/runtime-engine/search/build/libs/search-transformProductionFabric.jar differ diff --git a/runtime-engine/search/build/libs/search-transformProductionForge.jar b/runtime-engine/search/build/libs/search-transformProductionForge.jar new file mode 100644 index 000000000..298b693b1 Binary files /dev/null and b/runtime-engine/search/build/libs/search-transformProductionForge.jar differ diff --git a/runtime-engine/search/src/main/java/me/shedaniel/rei/impl/client/search/IntRange.java b/runtime-engine/search/src/main/java/me/shedaniel/rei/impl/client/search/IntRange.java new file mode 100644 index 000000000..94c259b8c --- /dev/null +++ b/runtime-engine/search/src/main/java/me/shedaniel/rei/impl/client/search/IntRange.java @@ -0,0 +1,27 @@ +/* + * This file is licensed under the MIT License, part of Roughly Enough Items. + * Copyright (c) 2018, 2019, 2020, 2021, 2022 shedaniel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.shedaniel.rei.impl.client.search; + +public record IntRange(int min, int max) { +} diff --git a/runtime-engine/search/src/main/java/me/shedaniel/rei/impl/client/search/SearchProviderImpl.java b/runtime-engine/search/src/main/java/me/shedaniel/rei/impl/client/search/SearchProviderImpl.java new file mode 100644 index 000000000..894153ddc --- /dev/null +++ b/runtime-engine/search/src/main/java/me/shedaniel/rei/impl/client/search/SearchProviderImpl.java @@ -0,0 +1,129 @@ +/* + * This file is licensed under the MIT License, part of Roughly Enough Items. + * Copyright (c) 2018, 2019, 2020, 2021, 2022 shedaniel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.shedaniel.rei.impl.client.search; + +import com.google.common.base.Suppliers; +import me.shedaniel.rei.api.client.search.SearchFilter; +import me.shedaniel.rei.api.client.search.SearchProvider; +import me.shedaniel.rei.api.client.search.method.InputMethod; +import me.shedaniel.rei.api.common.entry.EntryStack; +import me.shedaniel.rei.impl.client.search.argument.AlternativeArgument; +import me.shedaniel.rei.impl.client.search.argument.Argument; +import me.shedaniel.rei.impl.client.search.argument.CompoundArgument; +import me.shedaniel.rei.impl.client.search.argument.type.ArgumentType; +import me.shedaniel.rei.impl.client.util.CrashReportUtils; +import me.shedaniel.rei.impl.common.InternalLogger; +import net.minecraft.CrashReport; +import net.minecraft.CrashReportCategory; + +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +public class SearchProviderImpl implements SearchProvider { + @Override + public void startReload() { + } + + @Override + public SearchFilter createFilter(String filter, InputMethod inputMethod) { + return new SearchFilterImpl(filter, inputMethod); + } + + @Override + public void clearCache() { + Argument.SEARCH_CACHE.clear(); + } + + @Override + public boolean hasCache() { + return !Argument.SEARCH_CACHE.isEmpty(); + } + + public static class SearchFilterImpl implements SearchFilter { + private final String filter; + private final InputMethod inputMethod; + private final Supplier> arguments; + private final Supplier>> argumentTypes; + + public SearchFilterImpl(String filter, InputMethod inputMethod) { + this.filter = filter; + this.inputMethod = inputMethod; + this.arguments = Suppliers.memoize(() -> Argument.bakeArguments(filter)); + this.argumentTypes = Suppliers.memoize(() -> this.arguments.get().stream() + .flatMap(CompoundArgument::stream) + .flatMap(AlternativeArgument::stream) + .map(Argument::getArgument) + .distinct() + .collect(Collectors.toList())); + InternalLogger.getInstance().debug("Created search filter with %s using %s", filter, inputMethod.getName().getString()); + } + + @Override + public boolean test(EntryStack stack) { + try { + return Argument.matches(stack, arguments.get(), inputMethod); + } catch (Throwable throwable) { + CrashReport report = CrashReportUtils.essential(throwable, "Testing entry with search filter"); + CrashReportCategory category = report.addCategory("Search entry details"); + try { + stack.fillCrashReport(report, category); + } catch (Throwable throwable1) { + category.setDetailError("Filling Report", throwable1); + } + throw CrashReportUtils.throwReport(report); + } + } + + @Override + public void prepareFilter(Collection> stacks) { + Argument.prepareFilter(stacks, argumentTypes.get()); + } + + @Override + public void processDecoration(ParseDecorationSink sink) { + Argument.bakeArguments(filter, sink); + } + + @Override + public String getFilter() { + return filter; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + SearchFilterImpl that = (SearchFilterImpl) o; + return Objects.equals(filter, that.filter); + } + + @Override + public int hashCode() { + return Objects.hash(filter); + } + } +} diff --git a/runtime-engine/search/src/main/java/me/shedaniel/rei/impl/client/search/argument/AlternativeArgument.java b/runtime-engine/search/src/main/java/me/shedaniel/rei/impl/client/search/argument/AlternativeArgument.java new file mode 100644 index 000000000..01b169f6b --- /dev/null +++ b/runtime-engine/search/src/main/java/me/shedaniel/rei/impl/client/search/argument/AlternativeArgument.java @@ -0,0 +1,74 @@ +/* + * This file is licensed under the MIT License, part of Roughly Enough Items. + * Copyright (c) 2018, 2019, 2020, 2021, 2022 shedaniel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.shedaniel.rei.impl.client.search.argument; + +import com.google.common.collect.ForwardingList; +import org.jetbrains.annotations.ApiStatus; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +@ApiStatus.Internal +public class AlternativeArgument extends ForwardingList> { + static final AlternativeArgument EMPTY = new AlternativeArgument(Collections.emptyList()); + + private final List> arguments; + + public AlternativeArgument(List> arguments) { + this.arguments = arguments; + } + + public static Builder builder() { + return new Builder(); + } + + @Override + protected List> delegate() { + return arguments; + } + + public static class Builder { + private List> arguments; + + public Builder add(Argument argument) { + if (arguments == null) { + this.arguments = new ArrayList<>(); + } + + arguments.add(argument); + return this; + } + + public boolean isEmpty() { + return arguments == null; + } + + public AlternativeArgument build() { + if (arguments == null) return AlternativeArgument.EMPTY; + if (arguments.size() == 1) return new AlternativeArgument(Collections.singletonList(arguments.get(0))); + return new AlternativeArgument(arguments); + } + } +} diff --git a/runtime-engine/search/src/main/java/me/shedaniel/rei/impl/client/search/argument/Argument.java b/runtime-engine/search/src/main/java/me/shedaniel/rei/impl/client/search/argument/Argument.java new file mode 100644 index 000000000..8724af7d1 --- /dev/null +++ b/runtime-engine/search/src/main/java/me/shedaniel/rei/impl/client/search/argument/Argument.java @@ -0,0 +1,360 @@ +/* + * This file is licensed under the MIT License, part of Roughly Enough Items. + * Copyright (c) 2018, 2019, 2020, 2021, 2022 shedaniel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.shedaniel.rei.impl.client.search.argument; + +import com.google.common.base.MoreObjects; +import com.google.common.collect.Lists; +import it.unimi.dsi.fastutil.Pair; +import it.unimi.dsi.fastutil.ints.IntIntMutablePair; +import it.unimi.dsi.fastutil.ints.IntIntPair; +import it.unimi.dsi.fastutil.ints.IntList; +import it.unimi.dsi.fastutil.longs.Long2ObjectArrayMap; +import it.unimi.dsi.fastutil.longs.Long2ObjectMap; +import it.unimi.dsi.fastutil.longs.Long2ObjectMaps; +import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.shorts.Short2ObjectMap; +import it.unimi.dsi.fastutil.shorts.Short2ObjectMaps; +import it.unimi.dsi.fastutil.shorts.Short2ObjectOpenHashMap; +import me.shedaniel.rei.api.client.config.ConfigObject; +import me.shedaniel.rei.api.client.gui.config.SearchMode; +import me.shedaniel.rei.api.client.search.SearchFilter; +import me.shedaniel.rei.api.client.search.method.CharacterUnpackingInputMethod; +import me.shedaniel.rei.api.client.search.method.InputMethod; +import me.shedaniel.rei.api.common.entry.EntryStack; +import me.shedaniel.rei.api.common.util.CollectionUtils; +import me.shedaniel.rei.api.common.util.EntryStacks; +import me.shedaniel.rei.impl.client.search.argument.type.ArgumentType; +import me.shedaniel.rei.impl.client.search.argument.type.ArgumentTypesRegistry; +import me.shedaniel.rei.impl.client.search.result.ArgumentApplicableResult; +import me.shedaniel.rei.impl.common.util.HashedEntryStackWrapper; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.Util; +import net.minecraft.client.Minecraft; +import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; +import java.util.List; +import java.util.Locale; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicReference; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +@ApiStatus.Internal +@Environment(EnvType.CLIENT) +public class Argument { + public static final Short2ObjectMap> SEARCH_CACHE = Short2ObjectMaps.synchronize(new Short2ObjectOpenHashMap<>()); + private static final Object NO_CACHE = new Object(); + private static final AtomicReference lastLanguage = new AtomicReference<>(); + private ArgumentType argumentType; + private String text; + private T filterData; + private boolean regular; + private final int start; + private final int end; + private static final Pattern SPLIT_PATTERN = Pattern.compile("(?:\"([^\"]*)\")|([^\\s]+)"); + + public Argument(ArgumentType argumentType, String text, boolean regular, int start, int end, boolean lowercase) { + this.argumentType = argumentType; + this.text = lowercase ? text.toLowerCase(Locale.ROOT) : text; + this.regular = regular; + this.filterData = null; + this.start = start; + this.end = end; + } + + public int start() { + return start; + } + + public int end() { + return end; + } + + public static List bakeArguments(String filter) { + return bakeArguments(filter, null); + } + + public static List bakeArguments(String filter, @Nullable SearchFilter.ParseDecorationSink sink) { + List compoundArguments = Lists.newArrayList(); + int tokenStartIndex = 0; + + for (String token : StringUtils.splitByWholeSeparatorPreserveAllTokens(filter, "|")) { + Matcher terms = SPLIT_PATTERN.matcher(token); + CompoundArgument.Builder builder = CompoundArgument.builder(); + while (terms.find()) { + AlternativeArgument.Builder alternativeBuilder = AlternativeArgument.builder(); + + for (ArgumentType type : ArgumentTypesRegistry.ARGUMENT_TYPE_LIST) { + applyArgument(type, filter, terms, tokenStartIndex, alternativeBuilder, true, sink); + if (!alternativeBuilder.isEmpty()) { + break; + } + } + + if (alternativeBuilder.isEmpty()) { + for (ArgumentType type : ArgumentTypesRegistry.ARGUMENT_TYPE_LIST) { + applyArgument(type, filter, terms, tokenStartIndex, alternativeBuilder, false, sink); + } + } + + builder.add(alternativeBuilder); + } + compoundArguments.add(builder.build()); + tokenStartIndex += 1 + token.length(); + if (sink != null && tokenStartIndex - 1 < filter.length()) { + sink.addSplitter(tokenStartIndex - 1); + } + } + prepareSearchFilter(compoundArguments); + return compoundArguments; + } + + private static void prepareSearchFilter(List compoundArguments) { + for (CompoundArgument arguments : compoundArguments) { + for (AlternativeArgument alternativeArgument : arguments) { + for (Argument argument : alternativeArgument) { + //noinspection RedundantCast + ((Argument) argument).filterData = argument.argumentType.prepareSearchFilter(argument.getText()); + } + } + } + } + + private static void applyArgument(ArgumentType type, String filter, Matcher terms, int tokenStartIndex, AlternativeArgument.Builder alternativeBuilder, boolean forceGrammar, @Nullable SearchFilter.ParseDecorationSink sink) { + String term = MoreObjects.firstNonNull(terms.group(1), terms.group(2)); + if (type.getSearchMode() == SearchMode.NEVER) return; + ArgumentApplicableResult result = type.checkApplicable(term, forceGrammar); + + if (result.isApplicable()) { + int group = terms.group(1) != null ? 1 : 2; + Argument argument = new Argument<>(type, result.getText(), !result.isInverted(), + terms.start(group) + tokenStartIndex, terms.end(group) + tokenStartIndex, !result.shouldPreserveCasing()); + alternativeBuilder.add(argument); + if (sink != null) { + if (group == 1) { + sink.addQuote(terms.start() + tokenStartIndex); + if (terms.end() - 1 + tokenStartIndex < filter.length()) { + sink.addQuote(terms.end() - 1 + tokenStartIndex); + } + } + sink.addPart(IntIntPair.of(argument.start(), argument.end()), argument.argumentType.getHighlightedStyle(), result.isUsingGrammar(), + CollectionUtils.map(result.grammarRanges(), range -> IntIntPair.of(range.min(), range.max())), terms.start() + tokenStartIndex); + } + } + } + + public static boolean matches(EntryStack stack, List compoundArguments, InputMethod inputMethod) { + if (compoundArguments.isEmpty()) return true; + String newLanguage = Minecraft.getInstance().options.languageCode; + if (!Objects.equals(lastLanguage.getAndSet(newLanguage), newLanguage)) { + SEARCH_CACHE.clear(); + } + + a: + for (CompoundArgument arguments : compoundArguments) { + for (AlternativeArgument argument : arguments) { + if (!matches(stack, argument, inputMethod)) { + continue a; + } + } + + return true; + } + + return false; + } + + private static boolean matches(EntryStack stack, AlternativeArgument alternativeArgument, InputMethod inputMethod) { + if (alternativeArgument.isEmpty()) return true; + long hashExact = EntryStacks.hashExact(stack); + ResultSinkImpl sink = new ResultSinkImpl<>(inputMethod); + + for (Argument argument : alternativeArgument) { + sink.filters = inputMethod.expendFilter(argument.getText()); + if (matches(argument.getArgument(), stack, hashExact, argument.filterData, sink) == argument.isRegular()) { + return true; + } + } + + return false; + } + + private static Long2ObjectMap getSearchCache(ArgumentType argumentType) { + short argumentIndex = (short) argumentType.getIndex(); + Long2ObjectMap map = SEARCH_CACHE.get(argumentIndex); + if (map == null) { + SEARCH_CACHE.put(argumentIndex, map = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>())); + } + return map; + } + + private static boolean matches(ArgumentType argumentType, EntryStack stack, long hashExact, R filterData, ResultSinkImpl sink) { + Long2ObjectMap map = getSearchCache(argumentType); + Object value = map.get(hashExact); + if (value == null) { + value = argumentType.cacheData(stack); + map.put(hashExact, value == null ? NO_CACHE : value); + } + sink.matches = false; + argumentType.matches(value == NO_CACHE ? null : (B) value, stack, (T) filterData, sink); + return sink.matches; + } + + private static class ResultSinkImpl implements ArgumentType.ResultSink { + private final InputMethod inputMethod; + private boolean matches; + private Iterable filters; + + public ResultSinkImpl(InputMethod inputMethod) { + this.inputMethod = inputMethod; + } + + @Override + public boolean testTrue() { + return matches = true; + } + + @Override + public boolean testString(String text) { + if (matches) return true; + if (inputMethod instanceof CharacterUnpackingInputMethod im) { + for (T filter : filters) { + if (InputMethodMatcher.contains(im, IntList.of(text.codePoints().toArray()), (IntList) filter)) { + return matches = true; + } + } + } else { + for (T filter : filters) { + if (inputMethod.contains(text, filter)) { + return matches = true; + } + } + } + return false; + } + } + + public static Long prepareStart = null; + public static Collection> prepareStacks = null; + public static IntIntPair prepareStage = null; + public static IntIntPair[] currentStages = null; + + public static void prepareFilter(Collection> stacks, Collection> argumentTypes) { + if (prepareStage != null || currentStages != null) return; + try { + prepareStart = Util.getEpochMillis(); + prepareStacks = stacks; + prepareStage = new IntIntMutablePair(0, argumentTypes.size()); + currentStages = new IntIntPair[argumentTypes.size()]; + List hashedStacks = CollectionUtils.map(stacks, HashedEntryStackWrapper::new); + int searchPartitionSize = ConfigObject.getInstance().getAsyncSearchPartitionSize(); + boolean async = ConfigObject.getInstance().shouldAsyncSearch() && stacks.size() > searchPartitionSize * 4; + List>> futures = Lists.newArrayList(); + List, CompletableFuture>>> pairs = Lists.newArrayList(); + + for (ArgumentType argumentType : argumentTypes) { + prepareStage.first(prepareStage.firstInt() + 1); + Long2ObjectMap map = getSearchCache(argumentType); + IntIntPair currentStage = currentStages[prepareStage.firstInt() - 1] = new IntIntMutablePair(0, hashedStacks.size()); + + if (async) { + for (Collection partitionStacks : CollectionUtils.partition(hashedStacks, searchPartitionSize)) { + CompletableFuture> future = CompletableFuture.supplyAsync(() -> { + Long2ObjectMap out = new Long2ObjectArrayMap<>(searchPartitionSize + 1); + for (HashedEntryStackWrapper stack : partitionStacks) { + if (map.get(stack.hashExact()) == null) { + Object data = argumentType.cacheData(stack.unwrap()); + + if (data != null) { + out.put(stack.hashExact(), data); + } + } + } + return out; + }).whenComplete((objectLong2ObjectMap, throwable) -> { + currentStage.first(currentStage.firstInt() + partitionStacks.size()); + }); + futures.add(future); + pairs.add(Pair.of(argumentType, future)); + } + } else { + for (HashedEntryStackWrapper stack : hashedStacks) { + currentStage.first(currentStage.firstInt() + 1); + + if (map.get(stack.hashExact()) == null) { + Object data = argumentType.cacheData(stack.unwrap()); + + if (data != null) { + map.put(stack.hashExact(), data); + } + } + } + } + } + + if (async) { + try { + CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).get(30, TimeUnit.SECONDS); + } catch (InterruptedException | ExecutionException | TimeoutException e) { + e.printStackTrace(); + } + for (Pair, CompletableFuture>> pair : pairs) { + Long2ObjectMap now = pair.second().getNow(null); + if (now != null) getSearchCache(pair.left()).putAll(now); + } + } + } finally { + prepareStart = null; + prepareStacks = null; + prepareStage = null; + currentStages = null; + } + } + + public ArgumentType getArgument() { + return argumentType; + } + + public String getText() { + return text; + } + + public boolean isRegular() { + return regular; + } + + @Override + public String toString() { + return String.format("Argument[%s]: name = %s, regular = %b", argumentType.getName(), text, regular); + } + +} diff --git a/runtime-engine/search/src/main/java/me/shedaniel/rei/impl/client/search/argument/CompoundArgument.java b/runtime-engine/search/src/main/java/me/shedaniel/rei/impl/client/search/argument/CompoundArgument.java new file mode 100644 index 000000000..bcb3c0bf3 --- /dev/null +++ b/runtime-engine/search/src/main/java/me/shedaniel/rei/impl/client/search/argument/CompoundArgument.java @@ -0,0 +1,88 @@ +/* + * This file is licensed under the MIT License, part of Roughly Enough Items. + * Copyright (c) 2018, 2019, 2020, 2021, 2022 shedaniel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.shedaniel.rei.impl.client.search.argument; + +import com.google.common.collect.ForwardingList; +import org.jetbrains.annotations.ApiStatus; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +@ApiStatus.Internal +public class CompoundArgument extends ForwardingList { + public static final CompoundArgument ALWAYS = new CompoundArgument(AlternativeArgument.EMPTY); + private final AlternativeArgument[] arguments; + private final List argumentList; + + private CompoundArgument(AlternativeArgument... arguments) { + this.arguments = arguments; + this.argumentList = Arrays.asList(arguments); + } + + public static CompoundArgument of(AlternativeArgument... arguments) { + if (arguments.length == 0) return ALWAYS; + return new CompoundArgument(arguments); + } + + public static Builder builder() { + return new Builder(); + } + + public final boolean isAlways() { + return this == ALWAYS; + } + + @Override + protected List delegate() { + return argumentList; + } + + public static class Builder { + private List arguments; + + public Builder add(Argument argument) { + return add(new AlternativeArgument(Collections.singletonList(argument))); + } + + public Builder add(AlternativeArgument.Builder builder) { + return add(builder.build()); + } + + public Builder add(AlternativeArgument argument) { + if (arguments == null) { + this.arguments = new ArrayList<>(); + } + + arguments.add(argument); + return this; + } + + public CompoundArgument build() { + if (arguments == null) return CompoundArgument.ALWAYS; + return CompoundArgument.of(arguments.toArray(new AlternativeArgument[0])); + } + } +} diff --git a/runtime-engine/search/src/main/java/me/shedaniel/rei/impl/client/search/argument/IndexSet.java b/runtime-engine/search/src/main/java/me/shedaniel/rei/impl/client/search/argument/IndexSet.java new file mode 100644 index 000000000..025063d62 --- /dev/null +++ b/runtime-engine/search/src/main/java/me/shedaniel/rei/impl/client/search/argument/IndexSet.java @@ -0,0 +1,173 @@ +/* + * This file is licensed under the MIT License, part of Roughly Enough Items. + * Copyright (c) 2018, 2019, 2020, 2021, 2022 shedaniel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.shedaniel.rei.impl.client.search.argument; + +import java.util.function.IntConsumer; +import java.util.function.IntPredicate; + +/** + * MIT License + *

+ * Copyright (c) 2019 Juntong Liu + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +public class IndexSet { + public static final IndexSet ZERO = new IndexSet(0x1); + public static final IndexSet ONE = new IndexSet(0x2); + public static final IndexSet NONE = new IndexSet(0x0); + + int value = 0x0; + + public IndexSet() { + } + + public IndexSet(IndexSet set) { + value = set.value; + } + + public IndexSet(int value) { + this.value = value; + } + + public void set(int index) { + int i = 0x1 << index; + value |= i; + } + + public boolean get(int index) { + int i = 0x1 << index; + return (value & i) != 0; + } + + public void merge(IndexSet s) { + value = value == 0x1 ? s.value : (value |= s.value); + } + + public boolean traverse(IntPredicate p) { + int v = value; + for (int i = 0; i < 7; i++) { + if ((v & 0x1) == 0x1 && !p.test(i)) return false; + else if (v == 0) return true; + v >>= 1; + } + return true; + } + + public void foreach(IntConsumer c) { + int v = value; + for (int i = 0; i < 7; i++) { + if ((v & 0x1) == 0x1) c.accept(i); + else if (v == 0) return; + v >>= 1; + } + } + + public void offset(int i) { + value <<= i; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + traverse(i -> { + builder.append(i); + builder.append(", "); + return true; + }); + if (builder.length() != 0) { + builder.delete(builder.length() - 2, builder.length()); + return builder.toString(); + } else return "0"; + } + + public boolean isEmpty() { + return value == 0x0; + } + + public IndexSet copy() { + return new IndexSet(value); + } + + static class Immutable extends IndexSet { + @Override + public void set(int index) { + throw new UnsupportedOperationException("Immutable collection"); + } + + @Override + public void merge(IndexSet s) { + throw new UnsupportedOperationException("Immutable collection"); + } + + @Override + public void offset(int i) { + throw new UnsupportedOperationException("Immutable collection"); + } + } + + static class Storage { + IndexSet tmp = new Immutable(); + int[] data = new int[16]; + + public void set(IndexSet is, int index) { + if (index >= data.length) { + int size = index; + size |= size >> 1; + size |= size >> 2; + size |= size >> 4; + size |= size >> 8; + size |= size >> 16; + int[] replace = new int[size + 1]; + System.arraycopy(data, 0, replace, 0, data.length); + data = replace; + } + data[index] = is.value + 1; + } + + public IndexSet get(int index) { + if (index >= data.length) return null; + int ret = data[index]; + if (ret == 0) return null; + tmp.value = ret - 1; + return tmp; + } + } +} \ No newline at end of file diff --git a/runtime-engine/search/src/main/java/me/shedaniel/rei/impl/client/search/argument/InputMethodMatcher.java b/runtime-engine/search/src/main/java/me/shedaniel/rei/impl/client/search/argument/InputMethodMatcher.java new file mode 100644 index 000000000..58e5c46cd --- /dev/null +++ b/runtime-engine/search/src/main/java/me/shedaniel/rei/impl/client/search/argument/InputMethodMatcher.java @@ -0,0 +1,129 @@ +/* + * This file is licensed under the MIT License, part of Roughly Enough Items. + * Copyright (c) 2018, 2019, 2020, 2021, 2022 shedaniel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.shedaniel.rei.impl.client.search.argument; + +import it.unimi.dsi.fastutil.ints.IntList; +import me.shedaniel.rei.api.client.search.method.CharacterUnpackingInputMethod; +import me.shedaniel.rei.api.client.search.method.CharacterUnpackingInputMethod.ExpendedChar; + +import java.util.List; + +/** + * MIT License + *

+ * Copyright (c) 2019 Juntong Liu + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +public class InputMethodMatcher { + public static boolean contains(CharacterUnpackingInputMethod inputMethod, IntList s1, IntList s2) { + if (!s1.isEmpty()) { + for (int i = 0; i < s1.size(); i++) + if (check(inputMethod, s1, i, s2, 0, true)) return true; + } + return false; + } + + public static boolean matches(CharacterUnpackingInputMethod inputMethod, IntList s1, IntList s2) { + if (s1.isEmpty()) return s2.isEmpty(); + else return check(inputMethod, s1, 0, s2, 0, false); + } + + private static IndexSet match(CharacterUnpackingInputMethod inputMethod, int self, IntList str, int start, boolean partial) { + List expendedList = inputMethod.expendSourceChar(self); + IndexSet ret = (str.getInt(start) == self ? IndexSet.ONE : IndexSet.NONE).copy(); + for (ExpendedChar integers : expendedList) { + ret.merge(match(integers, str, start, partial)); + } + return ret; + } + + private static IndexSet match(ExpendedChar phonemes, IntList str, int start, boolean partial) { + IndexSet active = IndexSet.ZERO; + IndexSet ret = new IndexSet(); + for (IntList phoneme : phonemes.phonemes()) { + active = matchPhoneme(List.of(phoneme), str, active, start, partial); + if (active.isEmpty()) return ret; + ret.merge(active); + } + return ret; + } + + private static IndexSet matchPhoneme(List strs, IntList source, IndexSet idx, int start, boolean partial) { + if (strs.size() == 1 && strs.get(0).isEmpty()) return new IndexSet(idx); + IndexSet ret = new IndexSet(); + idx.foreach(i -> { + IndexSet is = matchM(strs, source, start + i, partial); + is.offset(i); + ret.merge(is); + }); + return ret; + } + + public static IndexSet matchM(List strs, IntList source, int start, boolean partial) { + IndexSet ret = new IndexSet(); + if (strs.size() == 1 && strs.get(0).isEmpty()) return ret; + for (IntList str : strs) { + int size = strCmp(source, str, start); + if (partial && start + size == source.size()) ret.set(size); // ending match + else if (size == str.size()) ret.set(size); // full match + } + return ret; + } + + private static int strCmp(IntList a, IntList b, int aStart) { + int len = Math.min(a.size() - aStart, b.size()); + for (int i = 0; i < len; i++) + if (a.getInt(i + aStart) != b.getInt(i)) return i; + return len; + } + + private static boolean check(CharacterUnpackingInputMethod inputMethod, IntList s1, int start1, IntList s2, int start2, boolean partial) { + if (start2 == s2.size()) return partial || start1 == s1.size(); + + int ch = s1.getInt(start1); + IndexSet s = match(inputMethod, ch, s2, start2, partial); + + if (start1 == s1.size() - 1) { + int i = s2.size() - start2; + return s.get(i); + } else return !s.traverse(i -> !check(inputMethod, s1, start1 + 1, s2, start2 + i, partial)); + } +} diff --git a/runtime-engine/search/src/main/java/me/shedaniel/rei/impl/client/search/argument/type/AlwaysMatchingArgumentType.java b/runtime-engine/search/src/main/java/me/shedaniel/rei/impl/client/search/argument/type/AlwaysMatchingArgumentType.java new file mode 100644 index 000000000..f69a1c0b8 --- /dev/null +++ b/runtime-engine/search/src/main/java/me/shedaniel/rei/impl/client/search/argument/type/AlwaysMatchingArgumentType.java @@ -0,0 +1,60 @@ +/* + * This file is licensed under the MIT License, part of Roughly Enough Items. + * Copyright (c) 2018, 2019, 2020, 2021, 2022 shedaniel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.shedaniel.rei.impl.client.search.argument.type; + +import me.shedaniel.rei.api.common.entry.EntryStack; +import me.shedaniel.rei.impl.client.search.result.ArgumentApplicableResult; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.util.Unit; +import org.jetbrains.annotations.ApiStatus; + +@ApiStatus.Internal +@Environment(EnvType.CLIENT) +public final class AlwaysMatchingArgumentType extends ArgumentType { + @Override + public String getName() { + return "always"; + } + + @Override + public Unit cacheData(EntryStack stack) { + return null; + } + + @Override + public void matches(Unit data, EntryStack stack, Unit filterData, ResultSink sink) { + sink.testTrue(); + } + + @Override + public Unit prepareSearchFilter(String searchText) { + return Unit.INSTANCE; + } + + @Override + public ArgumentApplicableResult checkApplicable(String text, boolean forceGrammar) { + return ArgumentApplicableResult.notApplicable();