Skip to content

Commit

Permalink
Add Space Mode Settings to Search (#571)
Browse files Browse the repository at this point in the history
Co-authored-by: slprime <[email protected]>
Co-authored-by: boubou19 <[email protected]>
  • Loading branch information
3 people committed Dec 23, 2024
1 parent 23ea7e7 commit d72bb9a
Show file tree
Hide file tree
Showing 6 changed files with 160 additions and 65 deletions.
18 changes: 18 additions & 0 deletions src/main/java/codechicken/nei/NEIClientConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,7 @@ public boolean onClick(int button) {
}

if (state()) {
NEIClientConfig.setIntSetting("inventory.search.spaceMode", 1);
NEIClientConfig.setIntSetting("inventory.search.modNameSearchMode", 0);
NEIClientConfig.setIntSetting("inventory.search.tooltipSearchMode", 0);
NEIClientConfig.setIntSetting("inventory.search.identifierSearchMode", 0);
Expand All @@ -468,6 +469,7 @@ public boolean onClick(int button) {
SearchField.searchParser.prefixRedefinitions.put('@', '%');
SearchField.searchParser.clearCache();
} else {
NEIClientConfig.setIntSetting("inventory.search.spaceMode", 0);
NEIClientConfig.setIntSetting("inventory.search.modNameSearchMode", 1);
NEIClientConfig.setIntSetting("inventory.search.tooltipSearchMode", 0);
NEIClientConfig.setIntSetting("inventory.search.identifierSearchMode", 0);
Expand All @@ -483,6 +485,22 @@ public boolean onClick(int button) {

});

tag.getTag("inventory.search.spaceMode").setComment("Search Space Rules").getIntValue(0);
API.addOption(new OptionCycled("inventory.search.spaceMode", 3, true) {

@Override
public boolean onClick(int button) {
// SearchField.searchParser.clearCache();
return super.onClick(button);
}

@Override
public boolean isEnabled() {
return !tag.getTag("inventory.search.format").getBooleanValue();
}

});

tag.getTag("inventory.search.modNameSearchMode").setComment("Search mode for Mod Names (prefix: @)")
.getIntValue(1);
API.addOption(new OptionCycled("inventory.search.modNameSearchMode", 3, true) {
Expand Down
49 changes: 18 additions & 31 deletions src/main/java/codechicken/nei/SearchTextFormatter.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package codechicken.nei;

import java.util.List;
import java.util.StringJoiner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import net.minecraft.util.EnumChatFormatting;

import codechicken.nei.FormattedTextField.TextFormatter;
import codechicken.nei.SearchTokenParser.SearchToken;

public class SearchTextFormatter implements TextFormatter {

Expand All @@ -17,63 +17,50 @@ public SearchTextFormatter(SearchTokenParser searchParser) {
}

public String format(String text) {
final String[] parts = text.split("\\|");
final Pattern splitPattern = searchParser.getSplitPattern();
final String[] parts = (text + "| ").split("\\|");
StringJoiner formattedText = new StringJoiner(EnumChatFormatting.GRAY + "|");

for (String filterText : parts) {
Matcher filterMatcher = splitPattern.matcher(filterText);
for (int i = 0; i < parts.length - 1; i++) {
final String filterText = parts[i];
final List<SearchToken> tokens = searchParser.splitSearchText(filterText);
StringBuilder formattedPart = new StringBuilder();
int startIndex = 0;

while (filterMatcher.find()) {
boolean ignore = "-".equals(filterMatcher.group(2));
String firstChar = filterMatcher.group(3);
String token = filterMatcher.group(4);
boolean quotes = token.length() > 1 && token.startsWith("\"") && token.endsWith("\"");

if (quotes) {
token = token.substring(1, token.length() - 1);
}

formattedPart.append(filterText.substring(startIndex, filterMatcher.start()));
for (SearchToken token : tokens) {
formattedPart.append(filterText.substring(startIndex, token.start));
EnumChatFormatting tokenColor = EnumChatFormatting.RESET;

if (!firstChar.isEmpty()) {
tokenColor = searchParser.getProvider(firstChar.charAt(0)).getHighlightedColor();
if (token.firstChar != null) {
tokenColor = searchParser.getProvider(token.firstChar).getHighlightedColor();
}

if (ignore) {
if (token.ignore) {
formattedPart.append(EnumChatFormatting.BLUE + "-");
}

if (!firstChar.isEmpty()) {
formattedPart.append(tokenColor + firstChar);
if (token.firstChar != null) {
formattedPart.append(tokenColor + String.valueOf(token.firstChar));
}

if (quotes) {
if (token.quotes) {
formattedPart.append(EnumChatFormatting.GOLD + "\"");
}

if (!token.isEmpty()) {
formattedPart.append(tokenColor + token);
if (!token.rawText.isEmpty()) {
formattedPart.append(tokenColor + token.rawText);
}

if (quotes) {
if (token.quotes) {
formattedPart.append(EnumChatFormatting.GOLD + "\"");
}

startIndex = filterMatcher.end();
startIndex = token.end;
}

formattedPart.append(filterText.substring(startIndex, filterText.length()));
formattedText.add(formattedPart);
}

if (text.endsWith("|")) {
formattedText.add("");
}

return formattedText.toString();
}
}
130 changes: 101 additions & 29 deletions src/main/java/codechicken/nei/SearchTokenParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
Expand All @@ -21,8 +22,6 @@
import codechicken.nei.ItemList.NegatedItemFilter;
import codechicken.nei.ItemList.NothingItemFilter;
import codechicken.nei.api.ItemFilter;
import gnu.trove.map.TCharCharMap;
import gnu.trove.map.hash.TCharCharHashMap;

public class SearchTokenParser {

Expand All @@ -41,6 +40,20 @@ public static SearchMode fromInt(int value) {
}
}

public static class SearchToken {

public boolean ignore = false;
public boolean quotes = false;
public Character firstChar = null;

public String[] words;

public String rawText = "";
public int start = 0;
public int end = 0;

}

public static interface ISearchParserProvider {

public ItemFilter getFilter(String searchText);
Expand Down Expand Up @@ -99,7 +112,7 @@ protected boolean removeEldestEntry(Map.Entry<String, ItemFilter> eldest) {

protected final List<ISearchParserProvider> searchProviders;
protected final ProvidersCache providersCache = new ProvidersCache();
protected final TCharCharMap prefixRedefinitions = new TCharCharHashMap();
protected final Map<Character, Character> prefixRedefinitions = new HashMap<>();

public SearchTokenParser(List<ISearchParserProvider> searchProviders) {
this.searchProviders = searchProviders;
Expand Down Expand Up @@ -169,7 +182,64 @@ public synchronized ItemFilter getFilter(String filterText) {

}

public Pattern getSplitPattern() {
public List<SearchToken> splitSearchText(String filterText) {

if (filterText.isEmpty()) {
return Collections.emptyList();
}

final List<SearchToken> tokens = new ArrayList<>();
final String prefixes = getPrefixes();
final int spaceMode = NEIClientConfig.getIntSetting("inventory.search.spaceMode");
// The regular expression first tries to match a string that starts with a space or the beginning of the string,
// followed by a sequence of characters that do not contain special characters -"@#$%&, and ends with a space
// and the characters -"@#$%& or the end of the string.
final String patternPart1 = "(?<tokenA>(^|\\s+)(?:[^" + Pattern.quote(" -\"" + prefixes)
+ "]).+?(?=\\s+["
+ Pattern.quote("-\"" + prefixes)
+ "]|$))";
// If the first condition is not met, it tries to match a sequence of - characters, followed by @#$%&
// characters, then either a quoted string or non-space characters.
final String patternPart2 = "((?<ignore>-*)(?<firstChar>[" + Pattern.quote(prefixes)
+ "]*)(?<tokenB>\\\".*?(?:\\\"|$)|\\S+\\s*))";
final Pattern pattern = Pattern.compile(spaceMode == 0 ? patternPart2 : (patternPart1 + "|" + patternPart2));
final Matcher filterMatcher = pattern.matcher(filterText);

while (filterMatcher.find()) {
String firstChar = filterMatcher.group("firstChar");
SearchToken token = new SearchToken();
token.start = filterMatcher.start();
token.end = filterMatcher.end();
token.ignore = "-".equals(filterMatcher.group("ignore"));
token.rawText = spaceMode == 0 ? null : filterMatcher.group("tokenA");

if (firstChar != null && !firstChar.isEmpty()) {
token.firstChar = firstChar.charAt(0);
}

if (token.rawText == null) { // spaceMode == 0
token.rawText = filterMatcher.group("tokenB");
token.quotes = token.rawText.length() > 1 && token.rawText.startsWith("\"")
&& token.rawText.endsWith("\"");

if (token.quotes) {
token.rawText = token.rawText.substring(1, token.rawText.length() - 1);
}

token.words = new String[] { token.rawText.trim() };
} else if (spaceMode == 2) {
token.words = token.rawText.trim().split("\\s+");
} else {
token.words = new String[] { token.rawText.trim() };
}

tokens.add(token);
}

return tokens;
}

private String getPrefixes() {
StringBuilder prefixes = new StringBuilder().append('\0');

for (ISearchParserProvider provider : getProviders()) {
Expand All @@ -178,14 +248,11 @@ public Pattern getSplitPattern() {
}
}

return Pattern.compile("((-*)([" + Pattern.quote(prefixes.toString()) + "]*)(\\\".*?(?:\\\"|$)|\\S+))");
return prefixes.toString();
}

public char getRedefinedPrefix(char prefix) {
if (this.prefixRedefinitions.containsKey(prefix)) {
return this.prefixRedefinitions.get(prefix);
}
return prefix;
return this.prefixRedefinitions.getOrDefault(prefix, prefix);
}

private ItemFilter parseSearchText(String filterText) {
Expand All @@ -194,54 +261,59 @@ private ItemFilter parseSearchText(String filterText) {
return null;
}

final Matcher filterMatcher = getSplitPattern().matcher(filterText);
final AllMultiItemFilter searchTokens = new AllMultiItemFilter();
final List<SearchToken> tokens = splitSearchText(filterText);

while (filterMatcher.find()) {
boolean ignore = "-".equals(filterMatcher.group(2));
String firstChar = filterMatcher.group(3);
String token = filterMatcher.group(4);
boolean quotes = token.length() > 1 && token.startsWith("\"") && token.endsWith("\"");

if (quotes) {
token = token.substring(1, token.length() - 1);
}

if (!token.isEmpty()) {
ItemFilter result = parseToken(firstChar, token);
for (SearchToken token : tokens) {
if (!token.rawText.isEmpty()) {
ItemFilter result = parseToken(token);

if (ignore) {
if (token.ignore) {
searchTokens.filters.add(new NegatedItemFilter(result));
} else {
searchTokens.filters.add(result);
}
} else if (!ignore) {
} else if (!token.ignore) {
searchTokens.filters.add(new NothingItemFilter());
}
}

return searchTokens;
}

private ItemFilter parseToken(String firstChar, String token) {
final ISearchParserProvider provider = firstChar.isEmpty() ? null : this.getProvider(firstChar.charAt(0));
private ItemFilter parseToken(SearchToken token) {
final ISearchParserProvider provider = token.firstChar == null ? null : this.getProvider(token.firstChar);

if (provider == null || provider.getSearchMode() == SearchMode.NEVER) {
final List<ItemFilter> filters = new ArrayList<>();

for (ISearchParserProvider _provider : getProviders()) {
if (_provider.getSearchMode() == SearchMode.ALWAYS) {
ItemFilter filter = _provider.getFilter(token);
AllMultiItemFilter filter = generateFilters(_provider, token.words);

if (filter != null) {
if (!filter.filters.isEmpty()) {
filters.add(filter);
}
}
}

return filters.isEmpty() ? new NothingItemFilter() : new AnyMultiItemFilter(filters);
} else {
return provider.getFilter(token);
return generateFilters(provider, token.words);
}
}

private AllMultiItemFilter generateFilters(ISearchParserProvider provider, String[] words) {
final AllMultiItemFilter filters = new AllMultiItemFilter();

for (String work : words) {
final ItemFilter filter = provider.getFilter(work);

if (filter != null) {
filters.filters.add(filter);
}
}

return filters;
}
}
9 changes: 4 additions & 5 deletions src/main/java/codechicken/nei/config/OptionButton.java
Original file line number Diff line number Diff line change
Expand Up @@ -75,26 +75,25 @@ public String getButtonText() {
}

public String getTooltip() {
String tip = null;

if (tooltip != null) {
String s = translateN(tooltip);

if (!s.equals(namespaced(tooltip))) {
tip = s;
return s;
}
}

if (tip == null && getPrefix() != null) {
if (getPrefix() != null) {
final int width = getStringWidth(getPrefix());
final Rectangle b = buttonSize();

if (width >= b.x) {
tip = translateN(name);
return translateN(name);
}
}

return tip;
return null;
}

public void drawPrefix() {
Expand Down
12 changes: 12 additions & 0 deletions src/main/java/codechicken/nei/config/OptionCycled.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,18 @@ public String getPrefix() {
return prefixed ? translateN(name) : null;
}

@Override
public String getTooltip() {
String tooltip = name + "." + value() + ".tip";
String s = translateN(tooltip);

if (!s.equals(namespaced(tooltip))) {
return s;
}

return super.getTooltip();
}

@Override
public boolean onClick(int button) {
return cycle();
Expand Down
Loading

0 comments on commit d72bb9a

Please sign in to comment.