package net.minecraft.command.completion; import java.util.regex.Matcher; import java.util.regex.Pattern; import net.minecraft.command.completion.TabCompletionData.Weighted; import net.minecraft.command.parser.CompletionParser.CompletionData; public class TabCompletion extends TabCompletionBase { private final String replacement; private final double[] weights; private final double maxWeight; private static final double vowelWeight = 0.5; public TabCompletion(final Pattern pattern, final String replacement, final String name) { super(pattern, name); this.replacement = replacement; this.weights = new double[replacement.length()]; double maxWeight = 0.0; int lastWordStart = 0; int wordCount = 0; for (int i = 0; i < replacement.length(); ++i) { final char c = replacement.charAt(i); if (i > 0 && Character.isUpperCase(c)) { lastWordStart = i; ++wordCount; } final double decay = decay(i - lastWordStart); maxWeight += (this.weights[i] = weight(c) * decay * decay * decay(wordCount)); if (i > 0 && c == '_') { lastWordStart = i; ++wordCount; } } this.maxWeight = maxWeight; } public static final double decay(final int distance) { return 1.0 / (distance + 1); } public TabCompletion(final String replacement, final String keyword, final String name, final boolean allowSpaces) { this(createPattern(keyword, allowSpaces), replacement, name); } public TabCompletion(final String replacement, final String keyword, final String name) { this(createPattern(keyword, true), replacement, name); } public TabCompletion(final String keyword, final String name, final boolean allowSpaces) { this(keyword, keyword, name, allowSpaces); } public TabCompletion(final String name, final boolean allowSpaces) { this(name, name, name, allowSpaces); } public TabCompletion(final String keyword, final String name) { this(keyword, keyword, name); } public TabCompletion(final String name) { this(name, name, name); } private static boolean isVowel(final char c) { final char lowerC = Character.toLowerCase(c); return lowerC == 'e' || lowerC == 'o' || lowerC == 'a' || lowerC == 'i' || lowerC == 'u'; } private static double weight(final char c) { return isVowel(c) ? vowelWeight : 1.0; } private static Pattern createPattern(final String keyword, final boolean allowSpaces) { final StringBuilder sb = new StringBuilder(keyword.length() * 3 + (allowSpaces ? 13 : 9)); // 3 * #chars [char + '?+'] + 9:5 ['\A(\s*+)(':'\A()('] - 2 [no '?+' after first] + 6 [')?+\Z'] at the end) sb.append(allowSpaces ? "\\A(\\s*+)(" : "\\A()("); sb.append(keyword, 0, 1); for (int i = 1; i < keyword.length(); ++i) { sb.append(keyword.charAt(i)); sb.append("?+"); } sb.append(")?+\\z"); return Pattern.compile(sb.toString(), Pattern.CASE_INSENSITIVE); } public static void addEscaped(final StringBuilder sb, final char c) { switch (c) { case '(': case '[': case '{': case '\\': case '^': case '-': case '=': case '$': case '!': case '|': case ']': case '}': case ')': case '?': case '*': case '+': case '.': sb.append('\\'); } sb.append(c); } public static class Escaped extends TabCompletion { public Escaped(final String replacement, final String keyword, final String name, final boolean allowSpaces) { super(createPattern(keyword, allowSpaces), replacement, name); } private static Pattern createPattern(final String keyword, final boolean allowSpaces) { final StringBuilder sb = new StringBuilder(keyword.length() * 3 + (allowSpaces ? 13 : 9)); // 3 * #chars [char + '?+'] + 9:5 ['\A(\s*+)(':'\A()('] - 2 [no '?+' after first] + 6 [')?+\Z'] at the end) sb.append(allowSpaces ? "\\A(\\s*+)(" : "\\A()("); addEscaped(sb, keyword.charAt(0)); for (int i = 1; i < keyword.length(); ++i) { addEscaped(sb, keyword.charAt(i)); sb.append("?+"); } sb.append(")?+\\z"); return Pattern.compile(sb.toString(), Pattern.CASE_INSENSITIVE); } public Escaped(final String keyword, final String name) { this(keyword, keyword, name, true); } public Escaped(final String name) { this(name, name, true); } public Escaped(final String keyword, final String name, final boolean allowSpaces) { this(keyword, keyword, name, allowSpaces); } public Escaped(final String name, final boolean allowSpaces) { this(name, name, allowSpaces); } } @Override public boolean complexFit() { return true; } @Override public double weightOffset(final Matcher m, final CompletionData cData) { return 0.0; } @Override public double[] getWeights(final Matcher m, final CompletionData cData) { return this.weights; } @Override public double getMaxWeight(final Matcher m, final CompletionData cData) { return this.maxWeight; } @Override public int getCursorOffset(final Matcher m, final CompletionData cData) { return 0; } @Override public int getSkipOffset(final Matcher m, final CompletionData cData) { return m.group(1).length(); } @Override public String getReplacementString(final Matcher m, final CompletionData cData) { return this.replacement; } @Override public boolean fullMatch(final Matcher m, final CompletionData cData, final String replacement) { return m.group(2) != null && m.group(2).length() == replacement.length(); } public static class SingleChar extends ITabCompletion { private final String replacement; private final char lowerReplacement; public SingleChar(final char replacement, final String name) { super(name); this.replacement = String.valueOf(replacement); this.lowerReplacement = Character.toLowerCase(replacement); } public SingleChar(final char name) { super(String.valueOf(name)); this.replacement = this.name; this.lowerReplacement = Character.toLowerCase(name); } public static final Pattern leadingSpacePattern = Pattern.compile("\\A\\s*+\\z"); @Override public Weighted getMatchData(final int startIndex, final CompletionData cData) { final Matcher m = leadingSpacePattern.matcher(cData.toMatch).region(startIndex, cData.cursorIndex); if (!m.find()) return null; final int cursorIndex = cData.cursorIndex; if (cursorIndex < cData.lowerToMatch.length() && cData.lowerToMatch.charAt(cursorIndex) == this.lowerReplacement) return null; return new Weighted(this.name, cursorIndex, cursorIndex, this.replacement, cursorIndex + 1, this.weight()); } public double weight() { return 0.0; } } }