From 6392e728e1543530f6e0b950c27f415170875856 Mon Sep 17 00:00:00 2001 From: shedaniel Date: Sat, 10 Dec 2022 21:14:41 +0800 Subject: Add Fuzzy Pinyin --- .../search/method/unihan/BomopofoInputMethod.java | 10 +- .../method/unihan/DoublePinyinInputMethod.java | 64 ++----- .../search/method/unihan/PinyinInputMethod.java | 209 +++++++++++++++++++-- .../search/method/unihan/UniHanInputMethod.java | 6 +- 4 files changed, 213 insertions(+), 76 deletions(-) (limited to 'runtime/src/main/java') diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/search/method/unihan/BomopofoInputMethod.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/search/method/unihan/BomopofoInputMethod.java index 0054c2bc7..df69d71b7 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/search/method/unihan/BomopofoInputMethod.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/search/method/unihan/BomopofoInputMethod.java @@ -25,6 +25,7 @@ package me.shedaniel.rei.impl.client.search.method.unihan; import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntList; +import me.shedaniel.rei.api.client.favorites.FavoriteMenuEntry; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.TranslatableComponent; @@ -90,7 +91,12 @@ public class BomopofoInputMethod extends PinyinInputMethod { } @Override - protected ExpendedChar asExpendedChar(String string) { + public List getOptionsMenuEntries() { + return List.of(); + } + + @Override + protected List asExpendedChars(String string) { IntList codepoints = new IntArrayList(string.length() + 1); int[] tone = {-1}; string.codePoints().forEach(codepoint -> { @@ -110,7 +116,7 @@ public class BomopofoInputMethod extends PinyinInputMethod { codepoints.add(Character.forDigit(tone[0], 10)); } List phonemes = standard(codepoints).stream().map(str -> CONVERSION.getOrDefault(str, str)).toList(); - return new ExpendedChar(phonemes); + return List.of(new ExpendedChar(phonemes)); } private static List standard(IntList s) { diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/search/method/unihan/DoublePinyinInputMethod.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/search/method/unihan/DoublePinyinInputMethod.java index 7f6124379..370974851 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/search/method/unihan/DoublePinyinInputMethod.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/search/method/unihan/DoublePinyinInputMethod.java @@ -9,8 +9,6 @@ import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntMaps; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import me.shedaniel.rei.api.client.favorites.FavoriteMenuEntry; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.screens.Screen; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.TranslatableComponent; @@ -27,10 +25,10 @@ public class DoublePinyinInputMethod extends PinyinInputMethod { public DoublePinyinInputMethod(UniHanManager manager) { super(manager); - this.read(); } - private void read() { + @Override + protected void read() { Path path = Platform.getConfigFolder().resolve("roughlyenoughitems/pinyin_double.properties"); this.converter = Converters.SOUGOU; if (Files.exists(path)) { @@ -52,7 +50,8 @@ public class DoublePinyinInputMethod extends PinyinInputMethod { this.write(); } - private void write() { + @Override + protected void write() { Path path = Platform.getConfigFolder().resolve("roughlyenoughitems/pinyin_double.properties"); Properties properties = new Properties(); properties.put("Converter", Converters.CONVERTERS.inverse().get(this.converter)); @@ -98,54 +97,17 @@ public class DoublePinyinInputMethod extends PinyinInputMethod { } @Override - protected ExpendedChar asExpendedChar(String string) { - IntList[] codepoints = new IntList[3]; - int skip = 2; - int tone = -1; - char[] chars = string.toCharArray(); - if (chars[0] == 's' && chars[1] == 'h') { - codepoints[0] = this.converter.convert("sh"); - } else if (chars[0] == 'c' && chars[1] == 'h') { - codepoints[0] = this.converter.convert("ch"); - } else if (chars[0] == 'z' && chars[1] == 'h') { - codepoints[0] = this.converter.convert("zh"); - } else { - skip = 1; - ToneEntry toneEntry = toneMap.get(chars[0]); - if (toneEntry == null) { - codepoints[0] = this.converter.convert(chars[0] + ""); - } else { - codepoints[0] = this.converter.convert(((char) toneEntry.codepoint()) + ""); - tone = toneEntry.tone(); - } - } - StringBuilder builder = new StringBuilder(); - for (int i = skip; i < chars.length; i++) { - char c = chars[i]; - if (c == 'ü') { - builder.append('v'); - } else { - ToneEntry toneEntry = toneMap.get(c); - if (toneEntry == null) { - builder.append(c); - } else { - builder.append((char) toneEntry.codepoint()); - tone = toneEntry.tone(); - } - } - } - if (builder.isEmpty()) { - codepoints[1] = codepoints[0]; - if (this.converter == Converters.SOUGOU || this.converter == Converters.MICROSOFT) { - codepoints[0] = IntList.of('o'); - } + protected List expendSimple(String string) { + return List.of(this.converter.convert(string)); + } + + @Override + protected List[] expendSingles(List codepoint) { + if (this.converter == Converters.SOUGOU || this.converter == Converters.MICROSOFT) { + return new List[]{List.of(IntList.of('o')), codepoint}; } else { - codepoints[1] = this.converter.convert(builder.toString()); - } - if (tone != -1) { - codepoints[2] = IntList.of(Character.forDigit(tone, 10)); + return new List[]{codepoint, codepoint}; } - return new ExpendedChar(Arrays.asList(codepoints).subList(0, tone == -1 ? 2 : 3)); } public interface Converter { diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/search/method/unihan/PinyinInputMethod.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/search/method/unihan/PinyinInputMethod.java index 6c317e0dd..f6f9c486f 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/search/method/unihan/PinyinInputMethod.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/search/method/unihan/PinyinInputMethod.java @@ -23,21 +23,31 @@ package me.shedaniel.rei.impl.client.search.method.unihan; +import dev.architectury.platform.Platform; +import dev.architectury.utils.value.BooleanValue; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.IntList; +import me.shedaniel.rei.api.client.favorites.FavoriteMenuEntry; import me.shedaniel.rei.api.client.search.method.CharacterUnpackingInputMethod; import me.shedaniel.rei.api.client.search.method.InputMethod; import me.shedaniel.rei.api.common.util.CollectionUtils; import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TextComponent; import net.minecraft.network.chat.TranslatableComponent; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.util.*; public class PinyinInputMethod extends UniHanInputMethod implements CharacterUnpackingInputMethod { + protected final Map fuzzyMap = new LinkedHashMap<>(); protected final Int2ObjectMap toneMap; + protected final Set fuzzySet = new HashSet<>(); protected record ToneEntry(int codepoint, int tone) {} @@ -68,10 +78,67 @@ public class PinyinInputMethod extends UniHanInputMethod implements CharacterUnp addTone('ǘ', "v2"); addTone('ǚ', "v3"); addTone('ǜ', "v4"); + addFuzzy("z", "zh"); + addFuzzy("s", "sh"); + addFuzzy("c", "ch"); + addFuzzy("an", "ang"); + addFuzzy("en", "eng"); + addFuzzy("in", "ing"); + addFuzzy("ian", "iang"); + addFuzzy("uan", "uang"); + addFuzzy("n", "l"); + addFuzzy("r", "l"); + addFuzzy("h", "f"); + read(); + } + + private void addFuzzy(String original, String to) { + this.fuzzyMap.put(IntList.of(original.codePoints().toArray()), IntList.of(to.codePoints().toArray())); } private void addTone(char c, String s) { - toneMap.put(c, new ToneEntry(s.charAt(0), Character.digit(s.charAt(1), 10))); + this.toneMap.put(c, new ToneEntry(s.charAt(0), Character.digit(s.charAt(1), 10))); + } + + protected void read() { + Path path = Platform.getConfigFolder().resolve("roughlyenoughitems/pinyin.properties"); + this.fuzzySet.clear(); + if (Files.exists(path)) { + try { + Properties properties = new Properties(); + try (InputStream stream = Files.newInputStream(path)) { + properties.load(stream); + } + for (IntList key : this.fuzzyMap.keySet()) { + if (properties.getOrDefault("Fuzzy:" + new String(key.toIntArray(), 0, key.size()), "false").equals("true")) { + this.fuzzySet.add(key); + } + } + } catch (Exception e) { + e.printStackTrace(); + try { + Files.deleteIfExists(path); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + } + this.write(); + } + + protected void write() { + Path path = Platform.getConfigFolder().resolve("roughlyenoughitems/pinyin.properties"); + Properties properties = new Properties(); + for (IntList key : this.fuzzyMap.keySet()) { + if (this.fuzzySet.contains(key)) { + properties.put("Fuzzy_" + new String(key.toIntArray(), 0, key.size()), "true"); + } + } + try (OutputStream stream = Files.newOutputStream(path, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE)) { + properties.store(stream, "Pinyin Options"); + } catch (IOException e) { + throw new RuntimeException(e); + } } @Override @@ -112,25 +179,127 @@ public class PinyinInputMethod extends UniHanInputMethod implements CharacterUnp } @Override - protected ExpendedChar asExpendedChar(String string) { - List codepoints = new ArrayList<>(string.length() + 1); - int[] tone = {-1}; - string.codePoints().forEach(codepoint -> { - if (codepoint == 'ü') { - codepoints.add(IntList.of('v')); - return; - } - ToneEntry toneEntry = toneMap.get(codepoint); + public List getOptionsMenuEntries() { + List innerEntries = new ArrayList<>(); + this.fuzzyMap.forEach((from, to) -> { + innerEntries.add(FavoriteMenuEntry.createToggle(new TextComponent("%s -> %s".formatted(new String(from.toIntArray(), 0, from.size()), new String(to.toIntArray(), 0, to.size()))), + new BooleanValue() { + @Override + public boolean getAsBoolean() { + return PinyinInputMethod.this.fuzzySet.contains(from); + } + + @Override + public void accept(boolean t) { + if (t) { + PinyinInputMethod.this.fuzzySet.add(from); + } else { + PinyinInputMethod.this.fuzzySet.remove(from); + } + PinyinInputMethod.this.write(); + PinyinInputMethod.this.dataMap.clear(); + PinyinInputMethod.this.load(); + } + })); + }); + return List.of(FavoriteMenuEntry.createSubMenu(new TranslatableComponent("text.rei.input.methods.pinyin.fuzzy.matching"), + innerEntries)); + } + + @Override + protected List asExpendedChars(String string) { + List[] codepoints = new List[3]; + int skip = 2; + int tone = -1; + char[] chars = string.toCharArray(); + if (chars[0] == 's' && chars[1] == 'h') { + codepoints[0] = this.expendInitials("sh"); + } else if (chars[0] == 'c' && chars[1] == 'h') { + codepoints[0] = this.expendInitials("ch"); + } else if (chars[0] == 'z' && chars[1] == 'h') { + codepoints[0] = this.expendInitials("zh"); + } else { + skip = 1; + ToneEntry toneEntry = toneMap.get(chars[0]); if (toneEntry == null) { - codepoints.add(IntList.of(codepoint)); + codepoints[0] = this.expendInitials(chars[0] + ""); } else { - codepoints.add(IntList.of(toneEntry.codepoint)); - tone[0] = toneEntry.tone; + codepoints[0] = this.expendInitials(((char) toneEntry.codepoint()) + ""); + tone = toneEntry.tone(); } - }); - if (tone[0] != -1) { - codepoints.add(IntList.of(Character.forDigit(tone[0], 10))); } - return new ExpendedChar(codepoints); + StringBuilder builder = new StringBuilder(); + for (int i = skip; i < chars.length; i++) { + char c = chars[i]; + if (c == 'ü') { + builder.append('v'); + } else { + ToneEntry toneEntry = toneMap.get(c); + if (toneEntry == null) { + builder.append(c); + } else { + builder.append((char) toneEntry.codepoint()); + tone = toneEntry.tone(); + } + } + } + int length = 2; + if (builder.isEmpty()) { + List[] expendSingles = this.expendSingles(codepoints[0]); + codepoints[0] = expendSingles[0]; + if (expendSingles.length > 1) { + codepoints[1] = expendSingles[1]; + } else { + length = 1; + } + } else { + codepoints[1] = this.expendFinals(builder.toString()); + } + if (tone != -1) { + codepoints[++length - 1] = List.of(IntList.of(Character.forDigit(tone, 10))); + } + int combinations = 1; + for (int i = 0; i < length; i++) { + combinations *= codepoints[i].size(); + } + List[] results = new List[combinations]; + int[] current = new int[length]; + for (int i = 0; i < combinations; i++) { + results[i] = new ArrayList<>(); + for (int k = 0; k < length; k++) { + results[i].add(codepoints[k].get(current[k])); + } + + for (int k = 0; k < length; k++) { + if (current[k] + 1 < codepoints[k].size()) { + current[k]++; + break; + } else { + current[k] = 0; + } + } + } + + return CollectionUtils.map(results, ExpendedChar::new); + } + + protected List[] expendSingles(List codepoint) { + return new List[]{codepoint}; + } + + protected List expendSimple(String string) { + IntList codepoints = IntList.of(string.codePoints().toArray()); + if (this.fuzzySet.contains(codepoints)) { + return List.of(codepoints, this.fuzzyMap.get(codepoints)); + } + return List.of(codepoints); + } + + protected List expendInitials(String string) { + return this.expendSimple(string); + } + + protected List expendFinals(String string) { + return this.expendSimple(string); } } diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/search/method/unihan/UniHanInputMethod.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/search/method/unihan/UniHanInputMethod.java index 91619f644..c9819c107 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/search/method/unihan/UniHanInputMethod.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/search/method/unihan/UniHanInputMethod.java @@ -68,7 +68,7 @@ public abstract class UniHanInputMethod implements InputMethod { String[] strings = data.split(getFieldDelimiter()); List sequences = dataMap.computeIfAbsent(codepoint, value -> new ArrayList<>(strings.length)); for (String string : strings) { - sequences.add(asExpendedChar(string)); + sequences.addAll(asExpendedChars(string)); } } }); @@ -77,8 +77,8 @@ public abstract class UniHanInputMethod implements InputMethod { } } - protected ExpendedChar asExpendedChar(String string) { - return new ExpendedChar(CollectionUtils.map(IntList.of(string.codePoints().toArray()), IntList::of)); + protected List asExpendedChars(String string) { + return List.of(new ExpendedChar(CollectionUtils.map(IntList.of(string.codePoints().toArray()), IntList::of))); } @Override -- cgit