├── .editorconfig
├── .gitattributes
├── .github
└── workflows
│ └── build.yml
├── .gitignore
├── LICENSE
├── README.md
├── art
└── icon.psd
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── src
└── main
├── java
└── com
│ └── oott123
│ └── rechars
│ ├── ReChars.java
│ ├── helpers
│ └── MatchHelper.java
│ ├── mixins
│ ├── AppliedEnergeticsInterfaceTerminal.java
│ ├── AppliedEnergeticsSearchPredicates.java
│ ├── ControllingKeyBindsScreen.java
│ ├── EmiSearchContains.java
│ ├── EmiSearchRegex.java
│ ├── MixinJeiSearch.java
│ ├── MixinJeiSearchLowMemory.java
│ ├── ModMenuModSearch.java
│ ├── REITextSearch.java
│ ├── TomSimpleStorage.java
│ └── VanillaTextSearch.java
│ └── stubs
│ ├── FakeTree.java
│ └── RecSuffixArray.java
└── resources
├── assets
└── roughly-enough-characters
│ └── icon.png
├── fabric.mod.json
└── roughly-enough-characters.mixins.json
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | end_of_line = lf
6 | indent_size = 4
7 | tab_width = 4
8 | indent_style = space
9 | insert_final_newline = true
10 | max_line_length = 120
11 |
12 | [*.yml]
13 | indent_size = 2
14 | tab_width = 2
15 |
16 | [*.bat]
17 | end_of_line = crlf
18 |
19 | [*.json]
20 | indent_size = 2
21 | tab_width = 2
22 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto eol=lf
2 | *.bat text eol=crlf
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: build
2 | on: [pull_request, push]
3 |
4 | jobs:
5 | build:
6 | strategy:
7 | matrix:
8 | # Use these Java versions
9 | java: [ 17 ]
10 | os: [ ubuntu-20.04 ]
11 | runs-on: ${{ matrix.os }}
12 | steps:
13 | - name: checkout repository
14 | uses: actions/checkout@v2
15 | - name: validate gradle wrapper
16 | uses: gradle/wrapper-validation-action@v1
17 | - name: setup jdk ${{ matrix.java }}
18 | uses: actions/setup-java@v1
19 | with:
20 | java-version: ${{ matrix.java }}
21 | - name: make gradle wrapper executable
22 | if: ${{ runner.os != 'Windows' }}
23 | run: chmod +x ./gradlew
24 | - name: build
25 | run: ./gradlew build
26 | - name: capture build artifacts
27 | if: ${{ runner.os == 'Linux' && matrix.java == '17' }}
28 | uses: actions/upload-artifact@v2
29 | with:
30 | name: Artifacts
31 | path: build/libs/
32 | release:
33 | name: Release
34 | runs-on: ubuntu-latest
35 | needs: [build]
36 | if: startsWith(github.ref, 'refs/tags/')
37 | steps:
38 | - name: Download artifacts
39 | uses: actions/download-artifact@v1
40 | with:
41 | name: Artifacts
42 | - name: Release
43 | uses: fnkr/github-action-ghr@v1
44 | env:
45 | GHR_PATH: Artifacts/
46 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
47 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # gradle
2 |
3 | .gradle/
4 | build/
5 | out/
6 | classes/
7 |
8 | # eclipse
9 |
10 | *.launch
11 |
12 | # idea
13 |
14 | .idea/
15 | *.iml
16 | *.ipr
17 | *.iws
18 |
19 | # vscode
20 |
21 | .settings/
22 | .vscode/
23 | bin/
24 | .classpath
25 | .project
26 |
27 | # macos
28 |
29 | *.DS_Store
30 |
31 | # fabric
32 |
33 | run/
34 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU LESSER GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 |
9 | This version of the GNU Lesser General Public License incorporates
10 | the terms and conditions of version 3 of the GNU General Public
11 | License, supplemented by the additional permissions listed below.
12 |
13 | 0. Additional Definitions.
14 |
15 | As used herein, "this License" refers to version 3 of the GNU Lesser
16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU
17 | General Public License.
18 |
19 | "The Library" refers to a covered work governed by this License,
20 | other than an Application or a Combined Work as defined below.
21 |
22 | An "Application" is any work that makes use of an interface provided
23 | by the Library, but which is not otherwise based on the Library.
24 | Defining a subclass of a class defined by the Library is deemed a mode
25 | of using an interface provided by the Library.
26 |
27 | A "Combined Work" is a work produced by combining or linking an
28 | Application with the Library. The particular version of the Library
29 | with which the Combined Work was made is also called the "Linked
30 | Version".
31 |
32 | The "Minimal Corresponding Source" for a Combined Work means the
33 | Corresponding Source for the Combined Work, excluding any source code
34 | for portions of the Combined Work that, considered in isolation, are
35 | based on the Application, and not on the Linked Version.
36 |
37 | The "Corresponding Application Code" for a Combined Work means the
38 | object code and/or source code for the Application, including any data
39 | and utility programs needed for reproducing the Combined Work from the
40 | Application, but excluding the System Libraries of the Combined Work.
41 |
42 | 1. Exception to Section 3 of the GNU GPL.
43 |
44 | You may convey a covered work under sections 3 and 4 of this License
45 | without being bound by section 3 of the GNU GPL.
46 |
47 | 2. Conveying Modified Versions.
48 |
49 | If you modify a copy of the Library, and, in your modifications, a
50 | facility refers to a function or data to be supplied by an Application
51 | that uses the facility (other than as an argument passed when the
52 | facility is invoked), then you may convey a copy of the modified
53 | version:
54 |
55 | a) under this License, provided that you make a good faith effort to
56 | ensure that, in the event an Application does not supply the
57 | function or data, the facility still operates, and performs
58 | whatever part of its purpose remains meaningful, or
59 |
60 | b) under the GNU GPL, with none of the additional permissions of
61 | this License applicable to that copy.
62 |
63 | 3. Object Code Incorporating Material from Library Header Files.
64 |
65 | The object code form of an Application may incorporate material from
66 | a header file that is part of the Library. You may convey such object
67 | code under terms of your choice, provided that, if the incorporated
68 | material is not limited to numerical parameters, data structure
69 | layouts and accessors, or small macros, inline functions and templates
70 | (ten or fewer lines in length), you do both of the following:
71 |
72 | a) Give prominent notice with each copy of the object code that the
73 | Library is used in it and that the Library and its use are
74 | covered by this License.
75 |
76 | b) Accompany the object code with a copy of the GNU GPL and this license
77 | document.
78 |
79 | 4. Combined Works.
80 |
81 | You may convey a Combined Work under terms of your choice that,
82 | taken together, effectively do not restrict modification of the
83 | portions of the Library contained in the Combined Work and reverse
84 | engineering for debugging such modifications, if you also do each of
85 | the following:
86 |
87 | a) Give prominent notice with each copy of the Combined Work that
88 | the Library is used in it and that the Library and its use are
89 | covered by this License.
90 |
91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license
92 | document.
93 |
94 | c) For a Combined Work that displays copyright notices during
95 | execution, include the copyright notice for the Library among
96 | these notices, as well as a reference directing the user to the
97 | copies of the GNU GPL and this license document.
98 |
99 | d) Do one of the following:
100 |
101 | 0) Convey the Minimal Corresponding Source under the terms of this
102 | License, and the Corresponding Application Code in a form
103 | suitable for, and under terms that permit, the user to
104 | recombine or relink the Application with a modified version of
105 | the Linked Version to produce a modified Combined Work, in the
106 | manner specified by section 6 of the GNU GPL for conveying
107 | Corresponding Source.
108 |
109 | 1) Use a suitable shared library mechanism for linking with the
110 | Library. A suitable mechanism is one that (a) uses at run time
111 | a copy of the Library already present on the user's computer
112 | system, and (b) will operate properly with a modified version
113 | of the Library that is interface-compatible with the Linked
114 | Version.
115 |
116 | e) Provide Installation Information, but only if you would otherwise
117 | be required to provide such information under section 6 of the
118 | GNU GPL, and only to the extent that such information is
119 | necessary to install and execute a modified version of the
120 | Combined Work produced by recombining or relinking the
121 | Application with a modified version of the Linked Version. (If
122 | you use option 4d0, the Installation Information must accompany
123 | the Minimal Corresponding Source and Corresponding Application
124 | Code. If you use option 4d1, you must provide the Installation
125 | Information in the manner specified by section 6 of the GNU GPL
126 | for conveying Corresponding Source.)
127 |
128 | 5. Combined Libraries.
129 |
130 | You may place library facilities that are a work based on the
131 | Library side by side in a single library together with other library
132 | facilities that are not Applications and are not covered by this
133 | License, and convey such a combined library under terms of your
134 | choice, if you do both of the following:
135 |
136 | a) Accompany the combined library with a copy of the same work based
137 | on the Library, uncombined with any other library facilities,
138 | conveyed under the terms of this License.
139 |
140 | b) Give prominent notice with the combined library that part of it
141 | is a work based on the Library, and explaining where to find the
142 | accompanying uncombined form of the same work.
143 |
144 | 6. Revised Versions of the GNU Lesser General Public License.
145 |
146 | The Free Software Foundation may publish revised and/or new versions
147 | of the GNU Lesser General Public License from time to time. Such new
148 | versions will be similar in spirit to the present version, but may
149 | differ in detail to address new problems or concerns.
150 |
151 | Each version is given a distinguishing version number. If the
152 | Library as you received it specifies that a certain numbered version
153 | of the GNU Lesser General Public License "or any later version"
154 | applies to it, you have the option of following the terms and
155 | conditions either of that published version or of any later version
156 | published by the Free Software Foundation. If the Library as you
157 | received it does not specify a version number of the GNU Lesser
158 | General Public License, you may choose any version of the GNU Lesser
159 | General Public License ever published by the Free Software Foundation.
160 |
161 | If the Library as you received it specifies that a proxy can decide
162 | whether future versions of the GNU Lesser General Public License shall
163 | apply, that proxy's public statement of acceptance of any version is
164 | permanent authorization for you to choose that version for the
165 | Library.
166 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # Roughly Enough Characters
4 |
5 | 为 Minecraft 增加拼音搜索。[JustEnoughCharacters](https://github.com/Towdium/JustEnoughCharacters) 的 Fabric 版本。
6 |
7 | 大部分代码参考自 JECH,并使用和 JECH 同样的 [PinIn](https://github.com/Towdium/PinIn) 库实现匹配逻辑。
8 |
9 | ## 使用
10 |
11 | 在 [Modrinth](https://modrinth.com/mod/roughly-enough-characters) 上下载后,放入 mods 文件夹即可。
12 |
13 | ## Mod 兼容性
14 |
15 | 每个不同 Mod 的每个搜索框都需要单独适配。
16 |
17 | 目前以下搜索框已适配:
18 |
19 | * 创造模式物品栏
20 | * REI
21 | * AE2 终端和接口终端
22 | * Controlling
23 | * ModMenu
24 | * JEI (by @vfyjxf)
25 | * Tom's Simple Storage Mod (by @vfyjxf)
26 |
27 | 目前我没有精力接受适配需求,主要适配自己玩的 Mod,但不反对任何人在 Issue 中提出适配需求。
28 |
29 | ## 开发
30 |
31 | 如果你想为某个 Mod 增加适配,请参考 [mixins](./src/main/java/com/oott123/rechars/mixins) 文件夹实现对应逻辑,然后在 [roughly-enough-characters.mixins.json](./src/main/resources/roughly-enough-characters.mixins.json) 中添加对应的 Mixin Class 定义即可。
32 |
33 | 我会定期接受适配的 Pull Request。
34 |
--------------------------------------------------------------------------------
/art/icon.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oott123/RoughlyEnoughCharacters/3d7ef4cadbfbe816f8d1c62a76140720ebcdd7d9/art/icon.psd
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'fabric-loom' version '0.10-SNAPSHOT'
3 | id 'maven-publish'
4 | id "com.modrinth.minotaur" version "2.+"
5 | }
6 |
7 | sourceCompatibility = JavaVersion.VERSION_17
8 | targetCompatibility = JavaVersion.VERSION_17
9 |
10 | archivesBaseName = "${project.archives_base_name}"
11 | version = "${project.mod_version}+${project.minecraft_version}-fabric${project.loader_version}"
12 | group = project.maven_group
13 |
14 | repositories {
15 | maven { url 'https://jitpack.io' }
16 | maven { url 'https://dvs1.progwml6.com/files/maven/' }
17 | }
18 |
19 | dependencies {
20 | // To change the versions see the gradle.properties file
21 | minecraft "com.mojang:minecraft:${project.minecraft_version}"
22 | mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2"
23 | modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
24 |
25 | // Fabric API. This is not required to run this mod, but useful when debugging.
26 | modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
27 | modCompileOnly "mezz.jei:jei-${minecraft_version}-fabric:${jei_version}"
28 | modImplementation 'com.github.Towdium:PinIn:1.5.0'
29 | include 'com.github.Towdium:PinIn:1.5.0'
30 | }
31 |
32 | processResources {
33 | inputs.property "version", project.version
34 |
35 | filesMatching("fabric.mod.json") {
36 | expand "version": project.version
37 | }
38 | }
39 |
40 | tasks.withType(JavaCompile).configureEach {
41 | // Minecraft 1.18 (1.18-pre2) upwards uses Java 17.
42 | it.options.release = 17
43 | }
44 |
45 | java {
46 | // Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task
47 | // if it is present.
48 | // If you remove this line, sources will not be generated.
49 | withSourcesJar()
50 | }
51 |
52 | jar {
53 | from("LICENSE") {
54 | rename { "${it}_${project.archivesBaseName}"}
55 | }
56 | }
57 |
58 | // configure the maven publication
59 | publishing {
60 | publications {
61 | mavenJava(MavenPublication) {
62 | from components.java
63 | }
64 | }
65 |
66 | // See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing.
67 | repositories {
68 | // Add repositories to publish to here.
69 | // Notice: This block does NOT have the same function as the block in the top level.
70 | // The repositories here will be used for publishing your artifact, not for
71 | // retrieving dependencies.
72 | }
73 | }
74 |
75 | modrinth {
76 | projectId = "y6h3fkEO"
77 | versionType = "alpha"
78 | uploadFile = remapJar
79 | loaders = ["fabric"]
80 | versionName = "${project.mod_version} (${project.minecraft_version})"
81 | versionNumber = "${project.mod_version}+${project.minecraft_version}-fabric${project.loader_version}"
82 | gameVersions = ["${project.minecraft_version}"]
83 | }
84 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Done to increase the memory available to gradle.
2 | org.gradle.jvmargs=-Xmx1G
3 |
4 | # Fabric Properties
5 | # check these on https://fabricmc.net/develop
6 | minecraft_version=1.18.2
7 | yarn_mappings=1.18.2+build.2
8 | loader_version=0.13.3
9 |
10 | #Fabric api
11 | fabric_version=0.48.0+1.18.2
12 |
13 | # Mod Deps
14 | jei_version=10.1.0.228
15 |
16 | # Mod Properties
17 | mod_version = 0.0.7
18 | maven_group = com.oott123.rechars
19 | archives_base_name = roughly-enough-characters
20 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oott123/RoughlyEnoughCharacters/3d7ef4cadbfbe816f8d1c62a76140720ebcdd7d9/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.1-bin.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oott123/RoughlyEnoughCharacters/3d7ef4cadbfbe816f8d1c62a76140720ebcdd7d9/gradlew
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | repositories {
3 | maven {
4 | name = 'Fabric'
5 | url = 'https://maven.fabricmc.net/'
6 | }
7 | mavenCentral()
8 | gradlePluginPortal()
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/main/java/com/oott123/rechars/ReChars.java:
--------------------------------------------------------------------------------
1 | package com.oott123.rechars;
2 |
3 | import net.fabricmc.api.ModInitializer;
4 | import org.apache.logging.log4j.LogManager;
5 | import org.apache.logging.log4j.Logger;
6 |
7 | public class ReChars implements ModInitializer {
8 | public static final Logger LOGGER = LogManager.getLogger("RoughlyEnoughCharacters");
9 |
10 | @Override
11 | public void onInitialize() {
12 | // This code runs as soon as Minecraft is in a mod-load-ready state.
13 | // However, some things (like resources) may still be uninitialized.
14 | // Proceed with mild caution.
15 |
16 | LOGGER.info("Thanks for using Roughly Enough Characters!");
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/main/java/com/oott123/rechars/helpers/MatchHelper.java:
--------------------------------------------------------------------------------
1 | // https://github.com/Towdium/JustEnoughCharacters/blob/1.18/src/main/java/me/towdium/jecharacters/utils/Match.java
2 |
3 | package com.oott123.rechars.helpers;
4 |
5 | import me.towdium.pinin.DictLoader;
6 | import me.towdium.pinin.PinIn;
7 | import me.towdium.pinin.searchers.TreeSearcher;
8 |
9 | import java.util.Collections;
10 | import java.util.Set;
11 | import java.util.WeakHashMap;
12 | import java.util.function.BiConsumer;
13 | import java.util.regex.Matcher;
14 | import java.util.regex.Pattern;
15 |
16 | import static me.towdium.pinin.searchers.Searcher.Logic.CONTAIN;
17 |
18 | public class MatchHelper {
19 | public static final PinIn context = new PinIn(new Loader()).config().accelerate(true).commit();
20 | static final Pattern p = Pattern.compile("a");
21 | static Set> searchers = Collections.newSetFromMap(new WeakHashMap<>());
22 |
23 | private static TreeSearcher searcher() {
24 | TreeSearcher ret = new TreeSearcher<>(CONTAIN, context);
25 | searchers.add(ret);
26 | return ret;
27 | }
28 |
29 | // Psi
30 | public static int rank(Object o, String s1, String s2) {
31 | return contains(s1, s2) ? 1 : 0;
32 | }
33 |
34 | public static boolean contains(String haystack, CharSequence needle) {
35 | boolean b = context.contains(haystack, needle.toString());
36 | // ReChars.LOGGER.debug(String.format("Searching %s in %s, matched: %b", needle, haystack, b));
37 | return b;
38 | }
39 |
40 | public static boolean contains(CharSequence haystack, CharSequence needle, boolean caseInsensitive) {
41 | if (caseInsensitive) {
42 | var result = contains(haystack.toString().toLowerCase(), needle.toString().toLowerCase());
43 | // ReChars.LOGGER.debug(String.format("Searching %s in %s case insensitive, matched: %b", needle, haystack, result));
44 | return result;
45 | } else {
46 | var result = contains(haystack, needle);
47 | // ReChars.LOGGER.debug(String.format("Searching %s in %s case sensitive, matched: %b", needle, haystack, result));
48 | return result;
49 | }
50 | }
51 |
52 | public static boolean equals(String s, Object o) {
53 | boolean b = o instanceof String && context.matches(s, (String) o);
54 | // ReChars.LOGGER.debug(String.format("Checking if %s is %s, equals: %b", s, o));
55 | return b;
56 | }
57 |
58 | public static boolean contains(CharSequence a, CharSequence b) {
59 | return contains(a.toString(), b);
60 | }
61 |
62 | public static Matcher matcher(Pattern test, CharSequence needle) {
63 | boolean result;
64 | if ((test.flags() & Pattern.CASE_INSENSITIVE) != 0 || (test.flags() & Pattern.UNICODE_CASE) != 0) {
65 | result = matches(needle.toString().toLowerCase(), test.toString().toLowerCase());
66 | } else {
67 | result = matches(needle.toString(), test.toString());
68 | }
69 | // TODO: do regexp matches
70 | // ReChars.LOGGER.debug(String.format("Searching %s in %s (regexp), matched: %b", needle, test, result));
71 | return result ? p.matcher("a") : p.matcher("");
72 | }
73 |
74 | public static boolean matches(String s1, String s2) {
75 | boolean start = s2.startsWith(".*");
76 | boolean end = s2.endsWith(".*");
77 | if (start && end && s2.length() < 4) end = false;
78 | if (start || end) s2 = s2.substring(start ? 2 : 0, s2.length() - (end ? 2 : 0));
79 | return contains(s1, s2);
80 | }
81 |
82 | static class Loader extends DictLoader.Default {
83 | @Override
84 | public void load(BiConsumer feed) {
85 | super.load(feed);
86 | feed.accept('\u9FCF', new String[]{"mai4"}); // 钅麦
87 | feed.accept('\u9FD4', new String[]{"ge1"}); // 钅哥
88 | feed.accept('\u9FED', new String[]{"ni3"}); // 钅尔
89 | feed.accept('\u9FEC', new String[]{"tian2"}); // 石田
90 | feed.accept('\u9FEB', new String[]{"ao4"}); // 奥气
91 | feed.accept('\uE900', new String[]{"lu2"}); // 钅卢
92 | feed.accept('\uE901', new String[]{"du4"}); // 钅杜
93 | feed.accept('\uE902', new String[]{"xi3"}); // 钅喜
94 | feed.accept('\uE903', new String[]{"bo1"}); // 钅波
95 | feed.accept('\uE904', new String[]{"hei1"}); // 钅黑
96 | feed.accept('\uE906', new String[]{"da2"}); // 钅达
97 | feed.accept('\uE907', new String[]{"lun2"}); // 钅仑
98 | feed.accept('\uE910', new String[]{"fu1"}); // 钅夫
99 | feed.accept('\uE912', new String[]{"li4"}); // 钅立
100 | }
101 | }
102 | }
--------------------------------------------------------------------------------
/src/main/java/com/oott123/rechars/mixins/AppliedEnergeticsInterfaceTerminal.java:
--------------------------------------------------------------------------------
1 | package com.oott123.rechars.mixins;
2 |
3 | import com.oott123.rechars.helpers.MatchHelper;
4 | import org.spongepowered.asm.mixin.Mixin;
5 | import org.spongepowered.asm.mixin.Pseudo;
6 | import org.spongepowered.asm.mixin.injection.At;
7 | import org.spongepowered.asm.mixin.injection.Redirect;
8 |
9 | @SuppressWarnings("UnresolvedMixinReference")
10 | @Pseudo
11 | @Mixin(targets = {"appeng.client.gui.me.interfaceterminal.InterfaceTerminalScreen"})
12 | public class AppliedEnergeticsInterfaceTerminal {
13 | @Redirect(method = {"refreshList", "itemStackMatchesSearchTerm"}, at = @At(value = "INVOKE", target = "Ljava/lang/String;contains(Ljava/lang/CharSequence;)Z"), remap = false)
14 | private boolean proxyStringContains(String haystack, CharSequence needle) {
15 | return MatchHelper.contains(haystack, needle);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/main/java/com/oott123/rechars/mixins/AppliedEnergeticsSearchPredicates.java:
--------------------------------------------------------------------------------
1 | package com.oott123.rechars.mixins;
2 |
3 | import com.oott123.rechars.helpers.MatchHelper;
4 | import org.spongepowered.asm.mixin.Mixin;
5 | import org.spongepowered.asm.mixin.Pseudo;
6 | import org.spongepowered.asm.mixin.injection.At;
7 | import org.spongepowered.asm.mixin.injection.Redirect;
8 |
9 | import java.util.regex.Matcher;
10 | import java.util.regex.Pattern;
11 |
12 | @SuppressWarnings("UnresolvedMixinReference")
13 | @Pseudo
14 | @Mixin(targets = {"appeng.client.gui.me.search.SearchPredicates"})
15 | public class AppliedEnergeticsSearchPredicates {
16 | @Redirect(
17 | method = {
18 | "/lambda\\$createModIdPredicate/",
19 | "/lambda\\$createNamePredicate/",
20 | "/lambda\\$createTooltipPredicate/"
21 | },
22 | at = @At(
23 | value = "INVOKE",
24 | target = "Ljava/util/regex/Pattern;matcher(Ljava/lang/CharSequence;)Ljava/util/regex/Matcher;"
25 | ),
26 | remap = false
27 | )
28 | private static Matcher proxyPatternMatcher(Pattern pattern, CharSequence sequence) {
29 | return MatchHelper.matcher(pattern, sequence);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/com/oott123/rechars/mixins/ControllingKeyBindsScreen.java:
--------------------------------------------------------------------------------
1 | package com.oott123.rechars.mixins;
2 |
3 | import com.oott123.rechars.helpers.MatchHelper;
4 | import org.spongepowered.asm.mixin.Mixin;
5 | import org.spongepowered.asm.mixin.Pseudo;
6 | import org.spongepowered.asm.mixin.injection.At;
7 | import org.spongepowered.asm.mixin.injection.Redirect;
8 |
9 | @SuppressWarnings("UnresolvedMixinReference")
10 | @Pseudo
11 | @Mixin(targets = {"com.blamejared.controlling.client.NewKeyBindsScreen"})
12 | public abstract class ControllingKeyBindsScreen {
13 | @Redirect(method = {"/^lambda\\$filterKeys/"}, at = @At(value = "INVOKE", target = "Ljava/lang/String;contains(Ljava/lang/CharSequence;)Z"), remap = false)
14 | private boolean proxyStringContains(String haystack, CharSequence needle) {
15 | return MatchHelper.contains(haystack, needle);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/main/java/com/oott123/rechars/mixins/EmiSearchContains.java:
--------------------------------------------------------------------------------
1 | package com.oott123.rechars.mixins;
2 |
3 | import com.oott123.rechars.helpers.MatchHelper;
4 | import org.spongepowered.asm.mixin.Mixin;
5 | import org.spongepowered.asm.mixin.Pseudo;
6 | import org.spongepowered.asm.mixin.injection.At;
7 | import org.spongepowered.asm.mixin.injection.Redirect;
8 |
9 | @SuppressWarnings("UnresolvedMixinReference")
10 | @Pseudo
11 | @Mixin(
12 | targets = {
13 | "dev.emi.emi.search.NameQuery",
14 | "dev.emi.emi.search.TooltipQuery"
15 | },
16 | remap = false
17 | )
18 | public class EmiSearchContains {
19 |
20 | @Redirect(method = "matches", at = @At(value = "INVOKE", target = "Ljava/lang/String;contains(Ljava/lang/CharSequence;)Z"))
21 | private boolean redirectContains(String haystack, CharSequence needle) {
22 | return MatchHelper.contains(haystack, needle);
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/com/oott123/rechars/mixins/EmiSearchRegex.java:
--------------------------------------------------------------------------------
1 | package com.oott123.rechars.mixins;
2 |
3 | import com.oott123.rechars.helpers.MatchHelper;
4 | import org.spongepowered.asm.mixin.Mixin;
5 | import org.spongepowered.asm.mixin.Pseudo;
6 | import org.spongepowered.asm.mixin.injection.At;
7 | import org.spongepowered.asm.mixin.injection.Redirect;
8 |
9 | import java.util.regex.Matcher;
10 | import java.util.regex.Pattern;
11 |
12 | @SuppressWarnings("UnresolvedMixinReference")
13 | @Pseudo
14 | @Mixin(
15 | targets = {
16 | "dev.emi.emi.search.RegexNameQuery",
17 | "dev.emi.emi.search.RegexTooltipQuery"
18 | },
19 | remap = false
20 | )
21 | public class EmiSearchRegex {
22 |
23 | @Redirect(method = "matches", at = @At(value = "INVOKE", target = "Ljava/util/regex/Pattern;matcher(Ljava/lang/CharSequence;)Ljava/util/regex/Matcher;"))
24 | private Matcher redirectMatcher(Pattern pattern, CharSequence sequence) {
25 | return MatchHelper.matcher(pattern, sequence);
26 | }
27 |
28 | }
29 |
30 |
--------------------------------------------------------------------------------
/src/main/java/com/oott123/rechars/mixins/MixinJeiSearch.java:
--------------------------------------------------------------------------------
1 | package com.oott123.rechars.mixins;
2 |
3 | import com.oott123.rechars.stubs.FakeTree;
4 | import mezz.jei.core.search.suffixtree.GeneralizedSuffixTree;
5 | import org.spongepowered.asm.mixin.Mixin;
6 | import org.spongepowered.asm.mixin.Pseudo;
7 | import org.spongepowered.asm.mixin.injection.At;
8 | import org.spongepowered.asm.mixin.injection.ModifyArg;
9 |
10 | import java.util.function.Supplier;
11 |
12 | @Pseudo
13 | @Mixin(targets = {"mezz.jei.common.search.ElementPrefixParser"})
14 | public abstract class MixinJeiSearch {
15 |
16 | @ModifyArg(method = "", at = @At(value = "INVOKE", target = "Lmezz/jei/core/search/PrefixInfo;(CLmezz/jei/core/search/PrefixInfo$IModeGetter;Lmezz/jei/core/search/PrefixInfo$IStringsGetter;Ljava/util/function/Supplier;)V"), remap = false)
17 | private static Supplier> modifyConstructorClInit(Supplier> supplier) {
18 | return FakeTree::new;
19 | }
20 |
21 | @ModifyArg(method = "", at = @At(value = "INVOKE", target = "Lmezz/jei/core/search/PrefixInfo;(CLmezz/jei/core/search/PrefixInfo$IModeGetter;Lmezz/jei/core/search/PrefixInfo$IStringsGetter;Ljava/util/function/Supplier;)V"), remap = false)
22 | private Supplier> modifyConstructorInit(Supplier> supplier) {
23 | if (supplier.get() instanceof GeneralizedSuffixTree) {
24 | return FakeTree::new;
25 | }
26 | return supplier;
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/java/com/oott123/rechars/mixins/MixinJeiSearchLowMemory.java:
--------------------------------------------------------------------------------
1 | package com.oott123.rechars.mixins;
2 |
3 | import com.oott123.rechars.helpers.MatchHelper;
4 | import org.spongepowered.asm.mixin.Mixin;
5 | import org.spongepowered.asm.mixin.Pseudo;
6 | import org.spongepowered.asm.mixin.injection.At;
7 | import org.spongepowered.asm.mixin.injection.Redirect;
8 |
9 | @Pseudo
10 | @Mixin(targets = {"mezz.jei.common.search.ElementSearchLowMem"})
11 | public abstract class MixinJeiSearchLowMemory {
12 |
13 | @Redirect(method = "matches", at = @At(value = "INVOKE", target = "Ljava/lang/String;contains(Ljava/lang/CharSequence;)Z"),remap = false)
14 | private static boolean redirectContains(String instance, CharSequence s) {
15 | return MatchHelper.contains(instance, s);
16 | }
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/src/main/java/com/oott123/rechars/mixins/ModMenuModSearch.java:
--------------------------------------------------------------------------------
1 | package com.oott123.rechars.mixins;
2 |
3 | import com.oott123.rechars.helpers.MatchHelper;
4 | import org.spongepowered.asm.mixin.Mixin;
5 | import org.spongepowered.asm.mixin.Pseudo;
6 | import org.spongepowered.asm.mixin.injection.At;
7 | import org.spongepowered.asm.mixin.injection.Redirect;
8 |
9 | @SuppressWarnings("UnresolvedMixinReference")
10 | @Pseudo
11 | @Mixin(targets = {"com.terraformersmc.modmenu.util.mod.ModSearch"})
12 | public abstract class ModMenuModSearch {
13 | @Redirect(method = {"passesFilters"}, at = @At(value = "INVOKE", target = "Ljava/lang/String;contains(Ljava/lang/CharSequence;)Z"), remap = false)
14 | private static boolean proxyStringContains(String haystack, CharSequence needle) {
15 | return MatchHelper.contains(haystack, needle);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/main/java/com/oott123/rechars/mixins/REITextSearch.java:
--------------------------------------------------------------------------------
1 | package com.oott123.rechars.mixins;
2 |
3 | import com.oott123.rechars.helpers.MatchHelper;
4 | import org.spongepowered.asm.mixin.Mixin;
5 | import org.spongepowered.asm.mixin.Pseudo;
6 | import org.spongepowered.asm.mixin.injection.At;
7 | import org.spongepowered.asm.mixin.injection.Redirect;
8 |
9 | @SuppressWarnings("UnresolvedMixinReference")
10 | @Pseudo
11 | @Mixin(targets = {
12 | "me.shedaniel.rei.impl.client.search.argument.type.IdentifierArgumentType",
13 | "me.shedaniel.rei.impl.client.search.argument.type.ModArgumentType",
14 | "me.shedaniel.rei.impl.client.search.argument.type.TagArgumentType",
15 | "me.shedaniel.rei.impl.client.search.argument.type.TextArgumentType",
16 | "me.shedaniel.rei.impl.client.search.argument.type.TooltipArgumentType"
17 | })
18 | public class REITextSearch {
19 | @Redirect(method = {"matches"}, at = @At(value = "INVOKE", target = "Ljava/lang/String;contains(Ljava/lang/CharSequence;)Z"), remap = false)
20 | private boolean proxyStringContains(String haystack, CharSequence needle) {
21 | return MatchHelper.contains(haystack, needle);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/main/java/com/oott123/rechars/mixins/TomSimpleStorage.java:
--------------------------------------------------------------------------------
1 | package com.oott123.rechars.mixins;
2 |
3 | import com.oott123.rechars.helpers.MatchHelper;
4 | import org.spongepowered.asm.mixin.Mixin;
5 | import org.spongepowered.asm.mixin.Pseudo;
6 | import org.spongepowered.asm.mixin.injection.At;
7 | import org.spongepowered.asm.mixin.injection.Redirect;
8 |
9 | import java.util.regex.Matcher;
10 | import java.util.regex.Pattern;
11 |
12 | @SuppressWarnings("UnresolvedMixinReference")
13 | @Pseudo
14 | @Mixin(targets = "com.tom.storagemod.gui.GuiStorageTerminalBase")
15 | public class TomSimpleStorage {
16 |
17 | @Redirect(method = {"updateSearch"}, at =@At(value = "INVOKE", target = "Ljava/util/regex/Pattern;matcher(Ljava/lang/CharSequence;)Ljava/util/regex/Matcher;"),remap = false)
18 | private Matcher redirectRegex(Pattern pattern, CharSequence sequence) {
19 | return MatchHelper.matcher(pattern, sequence);
20 | }
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/src/main/java/com/oott123/rechars/mixins/VanillaTextSearch.java:
--------------------------------------------------------------------------------
1 | package com.oott123.rechars.mixins;
2 |
3 | import com.oott123.rechars.stubs.RecSuffixArray;
4 | import net.minecraft.client.search.IdentifierSearchableContainer;
5 | import net.minecraft.client.search.SuffixArray;
6 | import net.minecraft.client.search.TextSearchableContainer;
7 | import net.minecraft.util.Identifier;
8 | import org.spongepowered.asm.mixin.Mixin;
9 | import org.spongepowered.asm.mixin.Shadow;
10 | import org.spongepowered.asm.mixin.injection.At;
11 | import org.spongepowered.asm.mixin.injection.Inject;
12 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
13 |
14 | import java.util.function.Function;
15 | import java.util.stream.Stream;
16 |
17 | @Mixin({TextSearchableContainer.class})
18 | public abstract class VanillaTextSearch extends IdentifierSearchableContainer {
19 | @Shadow
20 | private SuffixArray byText;
21 |
22 | public VanillaTextSearch(Function> identifierFinder) {
23 | super(identifierFinder);
24 | }
25 |
26 | @Inject(method = {""}, at = {@At("TAIL")})
27 | private final void onInit(Function textFinder, Function identifierFinder, CallbackInfo info) {
28 | this.reloadByTextWithRec();
29 | }
30 |
31 | @Inject(method = {"reload"}, at = {@At(target = "Lnet/minecraft/client/search/IdentifierSearchableContainer;reload()V", value = "INVOKE")})
32 | private final void onReload(CallbackInfo info) {
33 | this.reloadByTextWithRec();
34 | }
35 |
36 | private final void reloadByTextWithRec() {
37 | this.byText = new RecSuffixArray();
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/main/java/com/oott123/rechars/stubs/FakeTree.java:
--------------------------------------------------------------------------------
1 | package com.oott123.rechars.stubs;
2 |
3 | import com.oott123.rechars.helpers.MatchHelper;
4 | import me.towdium.pinin.searchers.Searcher;
5 | import me.towdium.pinin.searchers.TreeSearcher;
6 | import mezz.jei.core.search.suffixtree.GeneralizedSuffixTree;
7 |
8 | import java.util.Set;
9 |
10 | @SuppressWarnings("unused")
11 | public class FakeTree extends GeneralizedSuffixTree {
12 |
13 | TreeSearcher tree = new TreeSearcher<>(Searcher.Logic.CONTAIN, MatchHelper.context);
14 |
15 | @Override
16 | public void getSearchResults(String word, Set results) {
17 | results.addAll(tree.search(word));
18 | }
19 |
20 | @Override
21 | public void put(String key, T value) {
22 | tree.put(key, value);
23 | }
24 |
25 | @Override
26 | public void getAllElements(Set results) {
27 | results.addAll(tree.search(""));
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/src/main/java/com/oott123/rechars/stubs/RecSuffixArray.java:
--------------------------------------------------------------------------------
1 | package com.oott123.rechars.stubs;
2 |
3 | import com.oott123.rechars.ReChars;
4 | import com.oott123.rechars.helpers.MatchHelper;
5 | import me.towdium.pinin.searchers.Searcher;
6 | import me.towdium.pinin.searchers.TreeSearcher;
7 | import net.minecraft.client.search.SuffixArray;
8 |
9 | import java.util.List;
10 |
11 | public class RecSuffixArray extends SuffixArray {
12 | private TreeSearcher searcher = new TreeSearcher<>(Searcher.Logic.CONTAIN, MatchHelper.context);
13 |
14 | @Override
15 | public void add(T object, String text) {
16 | searcher.put(text, object);
17 | }
18 |
19 | @Override
20 | public void build() {
21 | // do nothing
22 | }
23 |
24 | @Override
25 | public List findAll(String text) {
26 | var result = searcher.search(text);
27 | ReChars.LOGGER.debug(String.format("Searching %s using SuffixArray, matched: %d", text, result.size()));
28 | return result;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/main/resources/assets/roughly-enough-characters/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oott123/RoughlyEnoughCharacters/3d7ef4cadbfbe816f8d1c62a76140720ebcdd7d9/src/main/resources/assets/roughly-enough-characters/icon.png
--------------------------------------------------------------------------------
/src/main/resources/fabric.mod.json:
--------------------------------------------------------------------------------
1 | {
2 | "schemaVersion": 1,
3 | "id": "roughly-enough-characters",
4 | "version": "${version}",
5 | "name": "Roughly Enough Characters",
6 | "description": "为 Minecraft 增加拼音搜索,Just Enough Charcaters 的 Fabric 版本",
7 | "authors": [
8 | "oott123"
9 | ],
10 | "contact": {
11 | "homepage": "https://github.com/oott123/RoughlyEnoughCharacters",
12 | "issues": "https://github.com/oott123/RoughlyEnoughCharacters/issues",
13 | "sources": "https://github.com/oott123/RoughlyEnoughCharacters"
14 | },
15 | "license": "GNU Lesser General Public License version 3 (LGPLv3)",
16 | "icon": "assets/roughly-enough-characters/icon.png",
17 | "environment": "client",
18 | "entrypoints": {
19 | "main": [
20 | "com.oott123.rechars.ReChars"
21 | ]
22 | },
23 | "mixins": [
24 | "roughly-enough-characters.mixins.json"
25 | ],
26 | "depends": {
27 | "fabricloader": ">=0.11.3",
28 | "minecraft": "1.18.x",
29 | "java": ">=17"
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/resources/roughly-enough-characters.mixins.json:
--------------------------------------------------------------------------------
1 | {
2 | "required": false,
3 | "minVersion": "0.8",
4 | "package": "com.oott123.rechars.mixins",
5 | "compatibilityLevel": "JAVA_16",
6 | "client": [
7 | "REITextSearch",
8 | "VanillaTextSearch",
9 | "AppliedEnergeticsSearchPredicates",
10 | "AppliedEnergeticsInterfaceTerminal",
11 | "ControllingKeyBindsScreen",
12 | "ModMenuModSearch",
13 | "TomSimpleStorage",
14 | "MixinJeiSearch",
15 | "MixinJeiSearchLowMemory",
16 | "EmiSearchContains",
17 | "EmiSearchRegex"
18 | ],
19 | "injectors": {
20 | "defaultRequire": 0
21 | }
22 | }
23 |
--------------------------------------------------------------------------------