mirror of
https://github.com/GTNewHorizons/NotEnoughItems.git
synced 2026-10-26 14:24:07 +00:00
Advanced search expressions and improved recipe search in a new pattern mode: extended+ (#692)
This commit is contained in:
1 parent
c1f7489de1
commit
e5d55a9bd8
47 files changed
+1738
-280
No files matched your search
@@ -1,3 +1,26 @@
|
|||||||
compileJava {
|
compileJava {
|
||||||
options.encoding = "UTF-8"
|
options.encoding = "UTF-8"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
generateGrammarSource {
|
||||||
|
maxHeapSize = "128m"
|
||||||
|
arguments += ['-package', 'codechicken.nei.search', '-visitor', '-no-listener']
|
||||||
|
outputDirectory = file("${projectDir}/build/generated/sources/search/antlr/codechicken/nei/search/")
|
||||||
|
}
|
||||||
|
|
||||||
|
compileJava.dependsOn generateGrammarSource
|
||||||
|
sourcesJar.dependsOn generateGrammarSource
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
main {
|
||||||
|
java.srcDirs += ["build/generated/sources/search/antlr/"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test {
|
||||||
|
useJUnitPlatform()
|
||||||
|
testLogging {
|
||||||
|
events "passed", "skipped", "failed"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
//version: 1707058017
|
//version: 1707058017
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
|
id 'antlr'
|
||||||
id 'com.gtnewhorizons.gtnhconvention'
|
id 'com.gtnewhorizons.gtnhconvention'
|
||||||
}
|
}
|
||||||
+11
-1
@@ -6,5 +6,15 @@ dependencies {
|
|||||||
|
|
||||||
shadowImplementation('org.apache.commons:commons-csv:1.10.0')
|
shadowImplementation('org.apache.commons:commons-csv:1.10.0')
|
||||||
|
|
||||||
compileOnly 'maven.modrinth:forge-essentials:7.5.1'
|
compileOnly('maven.modrinth:forge-essentials:7.5.1')
|
||||||
|
|
||||||
|
antlr('org.antlr:antlr4:4.13.2')
|
||||||
|
shadowImplementation('org.antlr:antlr4-runtime:4.13.2')
|
||||||
|
|
||||||
|
testImplementation(platform('org.junit:junit-bom:5.13.4'))
|
||||||
|
testImplementation('org.junit.jupiter:junit-jupiter')
|
||||||
|
testImplementation("org.mockito:mockito-core:5.+")
|
||||||
|
|
||||||
|
testRuntimeOnly('org.junit.platform:junit-platform-launcher')
|
||||||
|
testRuntimeOnly("org.lwjgl.lwjgl:lwjgl:2.9.3");
|
||||||
}
|
}
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
// Add any additional repositiroes for your dependencies here
|
// Add any additional repositories for your dependencies here
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
maven {
|
maven {
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
lexer grammar SearchExpressionLexer;
|
||||||
|
|
||||||
|
// Antlr4 generates imports with .*
|
||||||
|
@header {
|
||||||
|
// CHECKSTYLE:OFF
|
||||||
|
import codechicken.nei.SearchTokenParser;
|
||||||
|
}
|
||||||
|
|
||||||
|
@members {
|
||||||
|
private SearchTokenParser searchParser;
|
||||||
|
|
||||||
|
public SearchExpressionLexer(CharStream input, SearchTokenParser searchParser) {
|
||||||
|
this(input);
|
||||||
|
this.searchParser = searchParser;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lexer rules
|
||||||
|
REGEX_LEFT : 'r'? '/' -> pushMode(REGEX) ;
|
||||||
|
QUOTE_LEFT : '"' -> pushMode(QUOTED) ;
|
||||||
|
DASH : '-' ;
|
||||||
|
RECIPE_INGREDIENTS : '<&' | '<' ;
|
||||||
|
RECIPE_RESULT : '>&' | '>' ;
|
||||||
|
RECIPE_OTHERS : '^&' | '^' ;
|
||||||
|
PREFIX : CLEAN_SYMBOL {searchParser.hasRedefinedPrefix(getText().charAt(0))}? ;
|
||||||
|
OR : '|' ;
|
||||||
|
LEFT_BRACKET : '{' ;
|
||||||
|
RIGHT_BRACKET : '}' ;
|
||||||
|
PLAIN_TEXT : (CLEAN_SYMBOL | ESCAPED_SYMBOL)+ {!searchParser.hasRedefinedPrefix(getText().charAt(0))}? ;
|
||||||
|
NEWLINE_OR_TAB : [\t\r\n] -> skip ;
|
||||||
|
SPACE : ' ' ;
|
||||||
|
|
||||||
|
fragment ESCAPED_SYMBOL : '\\' . ;
|
||||||
|
fragment CLEAN_SYMBOL : ~[-<>^{}|/\\ "] ;
|
||||||
|
|
||||||
|
mode REGEX;
|
||||||
|
|
||||||
|
REGEX_CONTENT : (~[/] | '\\/')+ ;
|
||||||
|
REGEX_RIGHT : '/' -> popMode ;
|
||||||
|
|
||||||
|
mode QUOTED;
|
||||||
|
|
||||||
|
QUOTED_CONTENT : (~["] | '\\"')+ ;
|
||||||
|
QUOTE_RIGHT : '"' -> popMode ;
|
||||||
|
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
parser grammar SearchExpressionParser;
|
||||||
|
|
||||||
|
// Antlr4 generates imports with .*
|
||||||
|
@header {
|
||||||
|
// CHECKSTYLE:OFF
|
||||||
|
}
|
||||||
|
|
||||||
|
options { tokenVocab=SearchExpressionLexer; }
|
||||||
|
|
||||||
|
// Parser rules
|
||||||
|
|
||||||
|
// Should only be used for recipes. Thanks to antlr inability to properly
|
||||||
|
// inherit grammars, this is included where it's not supposed to be
|
||||||
|
recipeSearchExpression
|
||||||
|
: recipeClauseExpression*
|
||||||
|
;
|
||||||
|
|
||||||
|
recipeClauseExpression
|
||||||
|
: ingredientsPrefix=RECIPE_INGREDIENTS searchExpression[0, $ingredientsPrefix.getText().length() > 1]
|
||||||
|
| resultPrefix=RECIPE_RESULT searchExpression[1, $resultPrefix.getText().length() > 1]
|
||||||
|
| othersPrefix=RECIPE_OTHERS searchExpression[2, $othersPrefix.getText().length() > 1]
|
||||||
|
| searchExpression[3, false]
|
||||||
|
;
|
||||||
|
|
||||||
|
// General search expression rules
|
||||||
|
searchExpression[int type, boolean allRecipe]
|
||||||
|
: orExpression
|
||||||
|
;
|
||||||
|
|
||||||
|
orExpression
|
||||||
|
: sequenceExpression (OR sequenceExpression)*
|
||||||
|
;
|
||||||
|
|
||||||
|
sequenceExpression
|
||||||
|
: (SPACE* unaryExpression SPACE*)+
|
||||||
|
;
|
||||||
|
|
||||||
|
unaryExpression
|
||||||
|
: LEFT_BRACKET orExpression RIGHT_BRACKET?
|
||||||
|
| prefixedExpression
|
||||||
|
| negateExpression
|
||||||
|
| token['\0']
|
||||||
|
;
|
||||||
|
|
||||||
|
negateExpression
|
||||||
|
: DASH unaryExpression
|
||||||
|
;
|
||||||
|
|
||||||
|
prefixedExpression
|
||||||
|
locals [
|
||||||
|
Character prefix
|
||||||
|
]
|
||||||
|
: prefixToken=PREFIX { $prefix = $prefixToken.getText().charAt(0); } token[$prefix]
|
||||||
|
;
|
||||||
|
|
||||||
|
token[Character prefix]
|
||||||
|
: regex[prefix]
|
||||||
|
| quoted[prefix]
|
||||||
|
| PLAIN_TEXT
|
||||||
|
;
|
||||||
|
|
||||||
|
regex[Character prefix]
|
||||||
|
: REGEX_LEFT REGEX_CONTENT REGEX_RIGHT?
|
||||||
|
;
|
||||||
|
|
||||||
|
quoted[Character prefix]
|
||||||
|
: QUOTE_LEFT QUOTED_CONTENT QUOTE_RIGHT?
|
||||||
|
;
|
||||||
|
|
||||||
|
|
||||||
@@ -18,12 +18,12 @@ import com.google.gson.JsonObject;
|
|||||||
import com.google.gson.JsonParser;
|
import com.google.gson.JsonParser;
|
||||||
import com.google.gson.JsonSyntaxException;
|
import com.google.gson.JsonSyntaxException;
|
||||||
|
|
||||||
import codechicken.nei.ItemList.AnyMultiItemFilter;
|
|
||||||
import codechicken.nei.ItemList.EverythingItemFilter;
|
|
||||||
import codechicken.nei.ItemList.NothingItemFilter;
|
|
||||||
import codechicken.nei.PresetsList.Preset;
|
import codechicken.nei.PresetsList.Preset;
|
||||||
import codechicken.nei.PresetsList.PresetMode;
|
import codechicken.nei.PresetsList.PresetMode;
|
||||||
import codechicken.nei.api.ItemFilter;
|
import codechicken.nei.api.ItemFilter;
|
||||||
|
import codechicken.nei.filter.AnyMultiItemFilter;
|
||||||
|
import codechicken.nei.filter.EverythingItemFilter;
|
||||||
|
import codechicken.nei.filter.NothingItemFilter;
|
||||||
import codechicken.nei.util.ItemStackFilterParser;
|
import codechicken.nei.util.ItemStackFilterParser;
|
||||||
|
|
||||||
public class CollapsibleItems {
|
public class CollapsibleItems {
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
package codechicken.nei;
|
|
||||||
|
|
||||||
import net.minecraft.item.ItemStack;
|
|
||||||
|
|
||||||
import codechicken.nei.api.ItemFilter;
|
|
||||||
|
|
||||||
public class DoubleFilteredFilter implements ItemFilter {
|
|
||||||
|
|
||||||
private ItemFilter filter;
|
|
||||||
private ItemList.PatternItemFilter patternFilter;
|
|
||||||
|
|
||||||
public DoubleFilteredFilter(ItemFilter filter, String itemFilter) {
|
|
||||||
this.filter = filter;
|
|
||||||
this.patternFilter = new ItemList.PatternItemFilter(SearchField.getPattern(itemFilter));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean matches(ItemStack item) {
|
|
||||||
if (filter.matches(item)) {
|
|
||||||
return patternFilter.matches(item);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -70,8 +70,6 @@ public class ItemHistoryPanel extends AbstractSubpanel<ItemsGrid<ItemsGridSlot,
|
|||||||
final int useRows = NEIClientConfig.getIntSetting("inventory.history.useRows");
|
final int useRows = NEIClientConfig.getIntSetting("inventory.history.useRows");
|
||||||
final int rows = (int) Math.min(Math.ceil(this.grid.size() * 1f / columns), useRows);
|
final int rows = (int) Math.min(Math.ceil(this.grid.size() * 1f / columns), useRows);
|
||||||
|
|
||||||
System.out.println("rows: " + rows + " " + columns + " " + this.grid.size() + " " + width);
|
|
||||||
|
|
||||||
this.w = width;
|
this.w = width;
|
||||||
this.h = 8 + ItemsGrid.SLOT_SIZE * Math.max(rows, 1);
|
this.h = 8 + ItemsGrid.SLOT_SIZE * Math.max(rows, 1);
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package codechicken.nei;
|
package codechicken.nei;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
@@ -11,14 +10,12 @@ import java.util.Map;
|
|||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.ForkJoinPool;
|
import java.util.concurrent.ForkJoinPool;
|
||||||
import java.util.concurrent.ForkJoinWorkerThread;
|
import java.util.concurrent.ForkJoinWorkerThread;
|
||||||
import java.util.regex.Pattern;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.StreamSupport;
|
import java.util.stream.StreamSupport;
|
||||||
|
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.item.Item;
|
import net.minecraft.item.Item;
|
||||||
import net.minecraft.item.ItemStack;
|
import net.minecraft.item.ItemStack;
|
||||||
import net.minecraft.util.EnumChatFormatting;
|
|
||||||
import net.minecraft.util.IIcon;
|
import net.minecraft.util.IIcon;
|
||||||
|
|
||||||
import com.google.common.collect.ArrayListMultimap;
|
import com.google.common.collect.ArrayListMultimap;
|
||||||
@@ -28,6 +25,7 @@ import codechicken.nei.ThreadOperationTimer.TimeoutException;
|
|||||||
import codechicken.nei.api.ItemFilter;
|
import codechicken.nei.api.ItemFilter;
|
||||||
import codechicken.nei.api.ItemFilter.ItemFilterProvider;
|
import codechicken.nei.api.ItemFilter.ItemFilterProvider;
|
||||||
import codechicken.nei.api.ItemInfo;
|
import codechicken.nei.api.ItemInfo;
|
||||||
|
import codechicken.nei.filter.AllMultiItemFilter;
|
||||||
import codechicken.nei.recipe.InformationHandler;
|
import codechicken.nei.recipe.InformationHandler;
|
||||||
import codechicken.nei.search.TooltipFilter;
|
import codechicken.nei.search.TooltipFilter;
|
||||||
|
|
||||||
@@ -55,117 +53,6 @@ public class ItemList {
|
|||||||
*/
|
*/
|
||||||
public static boolean loadFinished;
|
public static boolean loadFinished;
|
||||||
|
|
||||||
public static class EverythingItemFilter implements ItemFilter {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean matches(ItemStack item) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class NothingItemFilter implements ItemFilter {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean matches(ItemStack item) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class NegatedItemFilter implements ItemFilter {
|
|
||||||
|
|
||||||
public ItemFilter filter;
|
|
||||||
|
|
||||||
public NegatedItemFilter(ItemFilter filter) {
|
|
||||||
this.filter = filter;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean matches(ItemStack item) {
|
|
||||||
return this.filter == null || !this.filter.matches(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class PatternItemFilter implements ItemFilter {
|
|
||||||
|
|
||||||
public Pattern pattern;
|
|
||||||
|
|
||||||
public PatternItemFilter(Pattern pattern) {
|
|
||||||
this.pattern = pattern;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean matches(ItemStack item) {
|
|
||||||
String displayName = EnumChatFormatting.getTextWithoutFormattingCodes(item.getDisplayName());
|
|
||||||
|
|
||||||
if (displayName != null && !displayName.isEmpty() && pattern.matcher(displayName).find()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item.hasDisplayName()) {
|
|
||||||
displayName = EnumChatFormatting
|
|
||||||
.getTextWithoutFormattingCodes(item.getItem().getItemStackDisplayName(item));
|
|
||||||
|
|
||||||
return displayName != null && !displayName.isEmpty() && pattern.matcher(displayName).find();
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class AllMultiItemFilter implements ItemFilter {
|
|
||||||
|
|
||||||
public List<ItemFilter> filters;
|
|
||||||
|
|
||||||
public AllMultiItemFilter(List<ItemFilter> filters) {
|
|
||||||
this.filters = filters;
|
|
||||||
}
|
|
||||||
|
|
||||||
public AllMultiItemFilter(ItemFilter... filters) {
|
|
||||||
this(new LinkedList<>(Arrays.asList(filters)));
|
|
||||||
}
|
|
||||||
|
|
||||||
public AllMultiItemFilter() {
|
|
||||||
this(new LinkedList<>());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean matches(ItemStack item) {
|
|
||||||
for (ItemFilter filter : filters) try {
|
|
||||||
if (filter != null && !filter.matches(item)) return false;
|
|
||||||
} catch (Exception e) {
|
|
||||||
NEIClientConfig.logger
|
|
||||||
.error("Exception filtering " + item + " with " + filter + " (" + e.getMessage() + ")", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class AnyMultiItemFilter implements ItemFilter {
|
|
||||||
|
|
||||||
public List<ItemFilter> filters;
|
|
||||||
|
|
||||||
public AnyMultiItemFilter(List<ItemFilter> filters) {
|
|
||||||
this.filters = filters;
|
|
||||||
}
|
|
||||||
|
|
||||||
public AnyMultiItemFilter() {
|
|
||||||
this(new LinkedList<>());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean matches(ItemStack item) {
|
|
||||||
for (ItemFilter filter : filters) try {
|
|
||||||
if (filter != null && filter.matches(item)) return true;
|
|
||||||
} catch (Exception e) {
|
|
||||||
NEIClientConfig.logger
|
|
||||||
.error("Exception filtering " + item + " with " + filter + " (" + e.getMessage() + ")", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static interface ItemsLoadedCallback {
|
public static interface ItemsLoadedCallback {
|
||||||
|
|
||||||
public void itemsLoaded();
|
public void itemsLoaded();
|
||||||
|
|||||||
@@ -500,12 +500,15 @@ public class NEIClientConfig {
|
|||||||
API.addOption(new OptionCycled("inventory.search.widgetAutofocus", 3, true));
|
API.addOption(new OptionCycled("inventory.search.widgetAutofocus", 3, true));
|
||||||
|
|
||||||
tag.getTag("inventory.search.patternMode").setComment("Search Mode").getIntValue(1);
|
tag.getTag("inventory.search.patternMode").setComment("Search Mode").getIntValue(1);
|
||||||
API.addOption(new OptionCycled("inventory.search.patternMode", 3, true) {
|
API.addOption(new OptionCycled("inventory.search.patternMode", 4, true) {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onClick(int button) {
|
public boolean onClick(int button) {
|
||||||
|
if (!super.onClick(button)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
SearchField.searchParser.clearCache();
|
SearchField.searchParser.clearCache();
|
||||||
return super.onClick(button);
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
@@ -575,8 +578,11 @@ public class NEIClientConfig {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onClick(int button) {
|
public boolean onClick(int button) {
|
||||||
|
if (!super.onClick(button)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
SearchField.searchParser.clearCache();
|
SearchField.searchParser.clearCache();
|
||||||
return super.onClick(button);
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -600,8 +606,11 @@ public class NEIClientConfig {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onClick(int button) {
|
public boolean onClick(int button) {
|
||||||
|
if (!super.onClick(button)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
SearchField.searchParser.clearCache();
|
SearchField.searchParser.clearCache();
|
||||||
return super.onClick(button);
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -624,8 +633,11 @@ public class NEIClientConfig {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onClick(int button) {
|
public boolean onClick(int button) {
|
||||||
|
if (!super.onClick(button)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
SearchField.searchParser.clearCache();
|
SearchField.searchParser.clearCache();
|
||||||
return super.onClick(button);
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -648,8 +660,11 @@ public class NEIClientConfig {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onClick(int button) {
|
public boolean onClick(int button) {
|
||||||
|
if (!super.onClick(button)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
SearchField.searchParser.clearCache();
|
SearchField.searchParser.clearCache();
|
||||||
return super.onClick(button);
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -672,8 +687,11 @@ public class NEIClientConfig {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onClick(int button) {
|
public boolean onClick(int button) {
|
||||||
|
if (!super.onClick(button)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
SearchField.searchParser.clearCache();
|
SearchField.searchParser.clearCache();
|
||||||
return super.onClick(button);
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -691,6 +709,9 @@ public class NEIClientConfig {
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
tag.getTag("inventory.search.logSearchExceptions").setComment("Search exceptions for extended+")
|
||||||
|
.getBooleanValue(false);
|
||||||
|
|
||||||
String prefixRedefinitions = tag.getTag("inventory.search.prefixRedefinitions").setComment(
|
String prefixRedefinitions = tag.getTag("inventory.search.prefixRedefinitions").setComment(
|
||||||
"Redefine search prefixes by providing a char-to-char map (JSON). The keys are the original prefixes, the values the new ones. Example: { \"$\": \"€\", \"#\": \"+\", \"@\": \"°\", \"%\": \"!\" }")
|
"Redefine search prefixes by providing a char-to-char map (JSON). The keys are the original prefixes, the values the new ones. Example: { \"$\": \"€\", \"#\": \"+\", \"@\": \"°\", \"%\": \"!\" }")
|
||||||
.getValue("{}");
|
.getValue("{}");
|
||||||
@@ -701,6 +722,7 @@ public class NEIClientConfig {
|
|||||||
};
|
};
|
||||||
Map<Character, Character> charMap = new Gson().fromJson(prefixRedefinitions, typeToken.getType());
|
Map<Character, Character> charMap = new Gson().fromJson(prefixRedefinitions, typeToken.getType());
|
||||||
SearchField.searchParser.prefixRedefinitions.putAll(charMap);
|
SearchField.searchParser.prefixRedefinitions.putAll(charMap);
|
||||||
|
SearchField.searchParser.updateRedefinedPrefixes();
|
||||||
} catch (JsonParseException e) {
|
} catch (JsonParseException e) {
|
||||||
logger.warn("Failed to convert prefix redefinitions from JSON to CharToCharMap:", e);
|
logger.warn("Failed to convert prefix redefinitions from JSON to CharToCharMap:", e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,11 +68,11 @@ public class PresetsList {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static class RecipesFilter implements IRecipeFilterProvider, IRecipeFilter {
|
protected static class PresetRecipeFilter implements IRecipeFilterProvider, IRecipeFilter {
|
||||||
|
|
||||||
public Set<String> cache;
|
public Set<String> cache;
|
||||||
|
|
||||||
public IRecipeFilter getFilter() {
|
public IRecipeFilter getRecipeFilter() {
|
||||||
|
|
||||||
if (cache == null) {
|
if (cache == null) {
|
||||||
cache = PresetsList.presets.stream().filter(p -> p.enabled && p.mode == PresetMode.REMOVE)
|
cache = PresetsList.presets.stream().filter(p -> p.enabled && p.mode == PresetMode.REMOVE)
|
||||||
@@ -145,7 +145,7 @@ public class PresetsList {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static final List<Preset> presets = new ArrayList<>();
|
public static final List<Preset> presets = new ArrayList<>();
|
||||||
protected static RecipesFilter recipeFilter = new RecipesFilter();
|
protected static PresetRecipeFilter recipeFilter = new PresetRecipeFilter();
|
||||||
protected static ItemPanelFilter itemFilter = new ItemPanelFilter();
|
protected static ItemPanelFilter itemFilter = new ItemPanelFilter();
|
||||||
|
|
||||||
static {
|
static {
|
||||||
|
|||||||
@@ -3,10 +3,13 @@ package codechicken.nei;
|
|||||||
import net.minecraft.util.EnumChatFormatting;
|
import net.minecraft.util.EnumChatFormatting;
|
||||||
|
|
||||||
import codechicken.nei.SearchField.GuiSearchField;
|
import codechicken.nei.SearchField.GuiSearchField;
|
||||||
|
import codechicken.nei.api.IRecipeFilter;
|
||||||
|
import codechicken.nei.api.IRecipeFilter.IRecipeFilterProvider;
|
||||||
import codechicken.nei.api.ItemFilter;
|
import codechicken.nei.api.ItemFilter;
|
||||||
|
import codechicken.nei.api.ItemFilter.ItemFilterProvider;
|
||||||
import codechicken.nei.util.TextHistory;
|
import codechicken.nei.util.TextHistory;
|
||||||
|
|
||||||
public abstract class RecipeSearchField extends TextField {
|
public abstract class RecipeSearchField extends TextField implements ItemFilterProvider, IRecipeFilterProvider {
|
||||||
|
|
||||||
private static final TextHistory history = new TextHistory();
|
private static final TextHistory history = new TextHistory();
|
||||||
|
|
||||||
@@ -58,10 +61,16 @@ public abstract class RecipeSearchField extends TextField {
|
|||||||
return EnumChatFormatting.getTextWithoutFormattingCodes(s);
|
return EnumChatFormatting.getTextWithoutFormattingCodes(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public ItemFilter getFilter() {
|
public ItemFilter getFilter() {
|
||||||
return ((GuiSearchField) field).getFilter();
|
return ((GuiSearchField) field).getFilter();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IRecipeFilter getRecipeFilter() {
|
||||||
|
return ((GuiSearchField) field).getRecipeFilter();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setFocus(boolean focus) {
|
public void setFocus(boolean focus) {
|
||||||
final boolean previousFocus = field.isFocused();
|
final boolean previousFocus = field.isFocused();
|
||||||
|
|||||||
@@ -13,11 +13,13 @@ import net.minecraft.util.EnumChatFormatting;
|
|||||||
import net.minecraftforge.fluids.FluidStack;
|
import net.minecraftforge.fluids.FluidStack;
|
||||||
|
|
||||||
import codechicken.lib.gui.GuiDraw;
|
import codechicken.lib.gui.GuiDraw;
|
||||||
import codechicken.nei.ItemList.NothingItemFilter;
|
|
||||||
import codechicken.nei.SearchTokenParser.ISearchParserProvider;
|
import codechicken.nei.SearchTokenParser.ISearchParserProvider;
|
||||||
import codechicken.nei.SearchTokenParser.SearchMode;
|
import codechicken.nei.SearchTokenParser.SearchMode;
|
||||||
|
import codechicken.nei.api.IRecipeFilter;
|
||||||
|
import codechicken.nei.api.IRecipeFilter.IRecipeFilterProvider;
|
||||||
import codechicken.nei.api.ItemFilter;
|
import codechicken.nei.api.ItemFilter;
|
||||||
import codechicken.nei.api.ItemFilter.ItemFilterProvider;
|
import codechicken.nei.api.ItemFilter.ItemFilterProvider;
|
||||||
|
import codechicken.nei.filter.NothingItemFilter;
|
||||||
import codechicken.nei.recipe.StackInfo;
|
import codechicken.nei.recipe.StackInfo;
|
||||||
import codechicken.nei.util.TextHistory;
|
import codechicken.nei.util.TextHistory;
|
||||||
|
|
||||||
@@ -88,7 +90,7 @@ public class SearchField extends TextField implements ItemFilterProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class GuiSearchField extends FormattedTextField {
|
public static class GuiSearchField extends FormattedTextField implements ItemFilterProvider, IRecipeFilterProvider {
|
||||||
|
|
||||||
protected final SearchTokenParser searchParser;
|
protected final SearchTokenParser searchParser;
|
||||||
|
|
||||||
@@ -104,14 +106,24 @@ public class SearchField extends TextField implements ItemFilterProvider {
|
|||||||
setPlaceholder(NEIClientUtils.translate("inventory.search.placeholder"));
|
setPlaceholder(NEIClientUtils.translate("inventory.search.placeholder"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public ItemFilter getFilter() {
|
public ItemFilter getFilter() {
|
||||||
return getFilter(getText());
|
return getFilter(getText());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IRecipeFilter getRecipeFilter() {
|
||||||
|
return getRecipeFilter(getText());
|
||||||
|
}
|
||||||
|
|
||||||
public ItemFilter getFilter(String filterText) {
|
public ItemFilter getFilter(String filterText) {
|
||||||
return this.searchParser.getFilter(filterText);
|
return this.searchParser.getFilter(filterText);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IRecipeFilter getRecipeFilter(String filterText) {
|
||||||
|
return this.searchParser.getRecipeFilter(filterText);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated
|
@Deprecated
|
||||||
@@ -272,7 +284,7 @@ public class SearchField extends TextField implements ItemFilterProvider {
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
// regex and extended+
|
||||||
if (!search.isEmpty()) {
|
if (!search.isEmpty()) {
|
||||||
try {
|
try {
|
||||||
return Pattern.compile(search, Pattern.MULTILINE | Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);
|
return Pattern.compile(search, Pattern.MULTILINE | Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);
|
||||||
@@ -305,6 +317,10 @@ public class SearchField extends TextField implements ItemFilterProvider {
|
|||||||
case 2:
|
case 2:
|
||||||
text = text.replaceAll("[{}()\\[\\].+*?^$\\\\|]", "\\\\$0");
|
text = text.replaceAll("[{}()\\[\\].+*?^$\\\\|]", "\\\\$0");
|
||||||
break;
|
break;
|
||||||
|
case 3:
|
||||||
|
text = text.replaceAll("\"", "\\\\$0");
|
||||||
|
text = "\"" + text + "\"";
|
||||||
|
return text;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import net.minecraft.util.EnumChatFormatting;
|
|||||||
|
|
||||||
import codechicken.nei.FormattedTextField.TextFormatter;
|
import codechicken.nei.FormattedTextField.TextFormatter;
|
||||||
import codechicken.nei.SearchTokenParser.SearchToken;
|
import codechicken.nei.SearchTokenParser.SearchToken;
|
||||||
|
import codechicken.nei.search.SearchExpressionFormatVisitor;
|
||||||
|
import codechicken.nei.search.SearchExpressionUtils;
|
||||||
|
|
||||||
public class SearchTextFormatter implements TextFormatter {
|
public class SearchTextFormatter implements TextFormatter {
|
||||||
|
|
||||||
@@ -17,8 +19,17 @@ public class SearchTextFormatter implements TextFormatter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String format(String text) {
|
public String format(String text) {
|
||||||
|
final int patternMode = NEIClientConfig.getIntSetting("inventory.search.patternMode");
|
||||||
|
if (patternMode == 3) {
|
||||||
|
final int spaceModeEnabled = NEIClientConfig.getIntSetting("inventory.search.spaceMode");
|
||||||
|
if (spaceModeEnabled == 1) {
|
||||||
|
text = SearchTokenParser.SPACE_PATTERN.matcher(text).replaceAll("\\\\ ");
|
||||||
|
}
|
||||||
|
final SearchExpressionFormatVisitor visitor = new SearchExpressionFormatVisitor(searchParser);
|
||||||
|
return SearchExpressionUtils.visitSearchExpression(text, visitor);
|
||||||
|
} else {
|
||||||
final String[] parts = (text + "| ").split("\\|");
|
final String[] parts = (text + "| ").split("\\|");
|
||||||
StringJoiner formattedText = new StringJoiner(EnumChatFormatting.GRAY + "|");
|
StringJoiner formattedText = new StringJoiner(SearchExpressionUtils.HIGHLIGHTS.OR + "|");
|
||||||
|
|
||||||
for (int i = 0; i < parts.length - 1; i++) {
|
for (int i = 0; i < parts.length - 1; i++) {
|
||||||
final String filterText = parts[i];
|
final String filterText = parts[i];
|
||||||
@@ -35,7 +46,7 @@ public class SearchTextFormatter implements TextFormatter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (token.ignore) {
|
if (token.ignore) {
|
||||||
formattedPart.append(EnumChatFormatting.BLUE + "-");
|
formattedPart.append(SearchExpressionUtils.HIGHLIGHTS.NEGATE + "-");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (token.firstChar != null) {
|
if (token.firstChar != null) {
|
||||||
@@ -43,7 +54,7 @@ public class SearchTextFormatter implements TextFormatter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (token.quotes) {
|
if (token.quotes) {
|
||||||
formattedPart.append(EnumChatFormatting.GOLD + "\"");
|
formattedPart.append(SearchExpressionUtils.HIGHLIGHTS.QUOTED + "\"");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!token.rawText.isEmpty()) {
|
if (!token.rawText.isEmpty()) {
|
||||||
@@ -51,7 +62,7 @@ public class SearchTextFormatter implements TextFormatter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (token.quotes) {
|
if (token.quotes) {
|
||||||
formattedPart.append(EnumChatFormatting.GOLD + "\"");
|
formattedPart.append(SearchExpressionUtils.HIGHLIGHTS.QUOTED + "\"");
|
||||||
}
|
}
|
||||||
|
|
||||||
startIndex = token.end;
|
startIndex = token.end;
|
||||||
@@ -64,3 +75,4 @@ public class SearchTextFormatter implements TextFormatter {
|
|||||||
return formattedText.toString();
|
return formattedText.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
@@ -4,8 +4,10 @@ import java.util.ArrayList;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@@ -15,12 +17,17 @@ import net.minecraft.client.resources.Language;
|
|||||||
import net.minecraft.item.ItemStack;
|
import net.minecraft.item.ItemStack;
|
||||||
import net.minecraft.util.EnumChatFormatting;
|
import net.minecraft.util.EnumChatFormatting;
|
||||||
|
|
||||||
import codechicken.nei.ItemList.AllMultiItemFilter;
|
import codechicken.nei.api.IRecipeFilter;
|
||||||
import codechicken.nei.ItemList.AnyMultiItemFilter;
|
|
||||||
import codechicken.nei.ItemList.EverythingItemFilter;
|
|
||||||
import codechicken.nei.ItemList.NegatedItemFilter;
|
|
||||||
import codechicken.nei.ItemList.NothingItemFilter;
|
|
||||||
import codechicken.nei.api.ItemFilter;
|
import codechicken.nei.api.ItemFilter;
|
||||||
|
import codechicken.nei.filter.AllMultiItemFilter;
|
||||||
|
import codechicken.nei.filter.AnyItemRecipeFilter;
|
||||||
|
import codechicken.nei.filter.AnyMultiItemFilter;
|
||||||
|
import codechicken.nei.filter.EverythingItemFilter;
|
||||||
|
import codechicken.nei.filter.NegatedItemFilter;
|
||||||
|
import codechicken.nei.filter.NothingItemFilter;
|
||||||
|
import codechicken.nei.search.ItemFilterVisitor;
|
||||||
|
import codechicken.nei.search.RecipeFilterVisitor;
|
||||||
|
import codechicken.nei.search.SearchExpressionUtils;
|
||||||
|
|
||||||
public class SearchTokenParser {
|
public class SearchTokenParser {
|
||||||
|
|
||||||
@@ -83,7 +90,7 @@ public class SearchTokenParser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class IsRegisteredItemFilter implements ItemFilter {
|
public static class IsRegisteredItemFilter implements ItemFilter {
|
||||||
|
|
||||||
public ItemFilter filter;
|
public ItemFilter filter;
|
||||||
|
|
||||||
@@ -99,27 +106,34 @@ public class SearchTokenParser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static final Pattern SPACE_PATTERN = Pattern.compile(" ");
|
||||||
|
|
||||||
protected final LRUCache<String, ItemFilter> filtersCache = new LRUCache<>(20);
|
protected final LRUCache<String, ItemFilter> filtersCache = new LRUCache<>(20);
|
||||||
protected final List<ISearchParserProvider> searchProviders;
|
protected final List<ISearchParserProvider> searchProviders;
|
||||||
|
protected final Set<Character> redefinedPrefixes = new HashSet<>();
|
||||||
protected final ProvidersCache providersCache = new ProvidersCache();
|
protected final ProvidersCache providersCache = new ProvidersCache();
|
||||||
protected final Map<Character, Character> prefixRedefinitions = new HashMap<>();
|
protected final Map<Character, Character> prefixRedefinitions = new HashMap<>();
|
||||||
|
|
||||||
public SearchTokenParser(List<ISearchParserProvider> searchProviders) {
|
public SearchTokenParser(List<ISearchParserProvider> searchProviders) {
|
||||||
this.searchProviders = searchProviders;
|
this.searchProviders = searchProviders;
|
||||||
|
updateRedefinedPrefixes();
|
||||||
}
|
}
|
||||||
|
|
||||||
public SearchTokenParser() {
|
public SearchTokenParser() {
|
||||||
this(new ArrayList<>());
|
this(new ArrayList<>());
|
||||||
|
updateRedefinedPrefixes();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addProvider(ISearchParserProvider provider) {
|
public void addProvider(ISearchParserProvider provider) {
|
||||||
this.searchProviders.add(provider);
|
this.searchProviders.add(provider);
|
||||||
|
updateRedefinedPrefixes(provider);
|
||||||
this.providersCache.clear();
|
this.providersCache.clear();
|
||||||
this.filtersCache.clear();
|
this.filtersCache.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clearCache() {
|
public void clearCache() {
|
||||||
this.filtersCache.clear();
|
this.filtersCache.clear();
|
||||||
|
updateRedefinedPrefixes();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected List<ISearchParserProvider> getProviders() {
|
protected List<ISearchParserProvider> getProviders() {
|
||||||
@@ -149,21 +163,46 @@ public class SearchTokenParser {
|
|||||||
return providersCache.providers;
|
return providersCache.providers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean hasRedefinedPrefix(char ch) {
|
||||||
|
return redefinedPrefixes.contains(ch);
|
||||||
|
}
|
||||||
|
|
||||||
public ISearchParserProvider getProvider(char ch) {
|
public ISearchParserProvider getProvider(char ch) {
|
||||||
return getProviders().stream()
|
if (!hasRedefinedPrefix(ch)) {
|
||||||
.filter(
|
return null;
|
||||||
provider -> provider.getSearchMode() == SearchMode.PREFIX
|
}
|
||||||
&& getRedefinedPrefix(provider.getPrefix()) == ch)
|
for (ISearchParserProvider provider : getProviders()) {
|
||||||
.findFirst().orElse(null);
|
if (provider.getSearchMode() == SearchMode.PREFIX && getRedefinedPrefix(provider.getPrefix()) == ch) {
|
||||||
|
return provider;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ItemFilter> getAlwaysProvidersFilters(String searchText) {
|
||||||
|
final List<ItemFilter> filters = new ArrayList<>();
|
||||||
|
for (ISearchParserProvider provider : getProviders()) {
|
||||||
|
if (provider.getSearchMode() == SearchMode.ALWAYS) {
|
||||||
|
filters.add(provider.getFilter(searchText));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return filters;
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized ItemFilter getFilter(String filterText) {
|
public synchronized ItemFilter getFilter(String filterText) {
|
||||||
filterText = EnumChatFormatting.getTextWithoutFormattingCodes(filterText).toLowerCase();
|
filterText = EnumChatFormatting.getTextWithoutFormattingCodes(filterText).toLowerCase();
|
||||||
|
if (filterText == null || filterText.isEmpty()) {
|
||||||
|
return new EverythingItemFilter();
|
||||||
|
}
|
||||||
|
final int spaceMode = NEIClientConfig.getIntSetting("inventory.search.spaceMode");
|
||||||
|
final int patternMode = NEIClientConfig.getIntSetting("inventory.search.patternMode");
|
||||||
|
|
||||||
|
if (patternMode != 3) {
|
||||||
return this.filtersCache.computeIfAbsent(filterText, text -> {
|
return this.filtersCache.computeIfAbsent(filterText, text -> {
|
||||||
final String[] parts = text.split("\\|");
|
final String[] parts = text.split("\\|");
|
||||||
final List<ItemFilter> searchTokens = Arrays.stream(parts).map(this::parseSearchText).filter(s -> s != null)
|
final List<ItemFilter> searchTokens = Arrays.stream(parts).map(this::parseSearchText)
|
||||||
.collect(Collectors.toList());
|
.filter(s -> s != null).collect(Collectors.toList());
|
||||||
|
|
||||||
if (searchTokens.isEmpty()) {
|
if (searchTokens.isEmpty()) {
|
||||||
return new EverythingItemFilter();
|
return new EverythingItemFilter();
|
||||||
@@ -172,8 +211,38 @@ public class SearchTokenParser {
|
|||||||
} else {
|
} else {
|
||||||
return new IsRegisteredItemFilter(new AnyMultiItemFilter(searchTokens));
|
return new IsRegisteredItemFilter(new AnyMultiItemFilter(searchTokens));
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
if (spaceMode == 1) {
|
||||||
|
filterText = SearchTokenParser.SPACE_PATTERN.matcher(filterText).replaceAll("\\\\ ");
|
||||||
|
}
|
||||||
|
return this.filtersCache.computeIfAbsent(filterText, text -> {
|
||||||
|
final ItemFilterVisitor visitor = new ItemFilterVisitor(this);
|
||||||
|
final ItemFilter searchToken = SearchExpressionUtils.visitSearchExpression(text, visitor);
|
||||||
|
|
||||||
|
return new IsRegisteredItemFilter(searchToken);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized IRecipeFilter getRecipeFilter(String filterText) {
|
||||||
|
final int patternMode = NEIClientConfig.getIntSetting("inventory.search.patternMode");
|
||||||
|
if (patternMode != 3) {
|
||||||
|
return new AnyItemRecipeFilter(getFilter(filterText));
|
||||||
|
}
|
||||||
|
filterText = EnumChatFormatting.getTextWithoutFormattingCodes(filterText).toLowerCase();
|
||||||
|
if (filterText == null || filterText.isEmpty()) {
|
||||||
|
return new AnyItemRecipeFilter(new EverythingItemFilter());
|
||||||
|
}
|
||||||
|
final int spaceModeEnabled = NEIClientConfig.getIntSetting("inventory.search.spaceMode");
|
||||||
|
|
||||||
|
if (spaceModeEnabled == 1) {
|
||||||
|
filterText = SearchTokenParser.SPACE_PATTERN.matcher(filterText).replaceAll("\\\\ ");
|
||||||
|
}
|
||||||
|
|
||||||
|
final RecipeFilterVisitor visitor = new RecipeFilterVisitor(this);
|
||||||
|
return SearchExpressionUtils.visitSearchExpression(filterText, visitor);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -235,21 +304,33 @@ public class SearchTokenParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String getPrefixes() {
|
private String getPrefixes() {
|
||||||
StringBuilder prefixes = new StringBuilder().append('\0');
|
final StringBuilder builder = new StringBuilder();
|
||||||
|
for (Character redefinedPrefix : redefinedPrefixes) {
|
||||||
for (ISearchParserProvider provider : getProviders()) {
|
builder.append(redefinedPrefix);
|
||||||
if (provider.getSearchMode() == SearchMode.PREFIX) {
|
|
||||||
prefixes.append(getRedefinedPrefix(provider.getPrefix()));
|
|
||||||
}
|
}
|
||||||
}
|
return builder.toString();
|
||||||
|
|
||||||
return prefixes.toString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public char getRedefinedPrefix(char prefix) {
|
public char getRedefinedPrefix(char prefix) {
|
||||||
return this.prefixRedefinitions.getOrDefault(prefix, prefix);
|
return this.prefixRedefinitions.getOrDefault(prefix, prefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void updateRedefinedPrefixes() {
|
||||||
|
this.redefinedPrefixes.clear();
|
||||||
|
for (ISearchParserProvider provider : getProviders()) {
|
||||||
|
if (provider.getSearchMode() == SearchTokenParser.SearchMode.PREFIX) {
|
||||||
|
redefinedPrefixes.add(getRedefinedPrefix(provider.getPrefix()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.redefinedPrefixes.add('\0');
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateRedefinedPrefixes(ISearchParserProvider provider) {
|
||||||
|
if (provider.getSearchMode() == SearchMode.PREFIX) {
|
||||||
|
this.redefinedPrefixes.add(getRedefinedPrefix(provider.getPrefix()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private ItemFilter parseSearchText(String filterText) {
|
private ItemFilter parseSearchText(String filterText) {
|
||||||
|
|
||||||
if (filterText.isEmpty()) {
|
if (filterText.isEmpty()) {
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import java.util.Set;
|
|||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.locks.Lock;
|
import java.util.concurrent.locks.Lock;
|
||||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
@@ -31,13 +32,13 @@ import com.google.gson.JsonParser;
|
|||||||
import codechicken.core.gui.GuiScrollSlot;
|
import codechicken.core.gui.GuiScrollSlot;
|
||||||
import codechicken.lib.gui.GuiDraw;
|
import codechicken.lib.gui.GuiDraw;
|
||||||
import codechicken.lib.vec.Rectangle4i;
|
import codechicken.lib.vec.Rectangle4i;
|
||||||
import codechicken.nei.ItemList.AnyMultiItemFilter;
|
|
||||||
import codechicken.nei.ItemList.NothingItemFilter;
|
|
||||||
import codechicken.nei.SearchTokenParser.ISearchParserProvider;
|
import codechicken.nei.SearchTokenParser.ISearchParserProvider;
|
||||||
import codechicken.nei.SearchTokenParser.SearchMode;
|
import codechicken.nei.SearchTokenParser.SearchMode;
|
||||||
import codechicken.nei.api.API;
|
import codechicken.nei.api.API;
|
||||||
import codechicken.nei.api.ItemFilter;
|
import codechicken.nei.api.ItemFilter;
|
||||||
import codechicken.nei.api.ItemFilter.ItemFilterProvider;
|
import codechicken.nei.api.ItemFilter.ItemFilterProvider;
|
||||||
|
import codechicken.nei.filter.AnyMultiItemFilter;
|
||||||
|
import codechicken.nei.filter.NothingItemFilter;
|
||||||
import codechicken.nei.guihook.GuiContainerManager;
|
import codechicken.nei.guihook.GuiContainerManager;
|
||||||
import codechicken.nei.guihook.IContainerTooltipHandler;
|
import codechicken.nei.guihook.IContainerTooltipHandler;
|
||||||
import codechicken.nei.recipe.GuiCraftingRecipe;
|
import codechicken.nei.recipe.GuiCraftingRecipe;
|
||||||
@@ -369,12 +370,18 @@ public class SubsetWidget extends Button implements ItemFilterProvider, IContain
|
|||||||
private static class DefaultParserProvider implements ISearchParserProvider {
|
private static class DefaultParserProvider implements ISearchParserProvider {
|
||||||
|
|
||||||
public ItemFilter getFilter(String searchText) {
|
public ItemFilter getFilter(String searchText) {
|
||||||
final String pathPart = searchText.replaceAll("\\s+", "").toLowerCase();
|
final int patternMode = NEIClientConfig.getIntSetting("inventory.search.patternMode");
|
||||||
|
Pattern pattern = null;
|
||||||
|
if (patternMode != 3) {
|
||||||
|
searchText = searchText.replaceAll("\\s+", "").toLowerCase();
|
||||||
|
} else {
|
||||||
|
pattern = SearchField.getPattern(searchText, patternMode);
|
||||||
|
}
|
||||||
final AnyMultiItemFilter filter = new AnyMultiItemFilter();
|
final AnyMultiItemFilter filter = new AnyMultiItemFilter();
|
||||||
final Set<ItemStack> filteredItems = new HashSet<>();
|
final Set<ItemStack> filteredItems = new HashSet<>();
|
||||||
|
|
||||||
for (SubsetTag tag : tags.values()) {
|
for (SubsetTag tag : tags.values()) {
|
||||||
if (tag.filter != null && tag.path.contains(pathPart)) {
|
if (tag.filter != null && matches(tag.path, searchText, pattern)) {
|
||||||
filteredItems.addAll(tag.items);
|
filteredItems.addAll(tag.items);
|
||||||
filter.filters.add(tag.filter);
|
filter.filters.add(tag.filter);
|
||||||
}
|
}
|
||||||
@@ -395,6 +402,14 @@ public class SubsetWidget extends Button implements ItemFilterProvider, IContain
|
|||||||
public SearchMode getSearchMode() {
|
public SearchMode getSearchMode() {
|
||||||
return SearchMode.fromInt(NEIClientConfig.getIntSetting("inventory.search.subsetsSearchMode"));
|
return SearchMode.fromInt(NEIClientConfig.getIntSetting("inventory.search.subsetsSearchMode"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean matches(String name, String searchText, Pattern pattern) {
|
||||||
|
if (pattern != null) {
|
||||||
|
return pattern.matcher(name).find();
|
||||||
|
} else {
|
||||||
|
return name.contains(searchText);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ public interface IRecipeFilter {
|
|||||||
|
|
||||||
public static interface IRecipeFilterProvider {
|
public static interface IRecipeFilterProvider {
|
||||||
|
|
||||||
public IRecipeFilter getFilter();
|
public IRecipeFilter getRecipeFilter();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean matches(IRecipeHandler handler, List<PositionedStack> ingredients, PositionedStack result,
|
public boolean matches(IRecipeHandler handler, List<PositionedStack> ingredients, PositionedStack result,
|
||||||
|
|||||||
@@ -44,8 +44,6 @@ import com.google.common.collect.ArrayListMultimap;
|
|||||||
import codechicken.core.featurehack.GameDataManipulator;
|
import codechicken.core.featurehack.GameDataManipulator;
|
||||||
import codechicken.nei.InfiniteStackSizeHandler;
|
import codechicken.nei.InfiniteStackSizeHandler;
|
||||||
import codechicken.nei.InfiniteToolHandler;
|
import codechicken.nei.InfiniteToolHandler;
|
||||||
import codechicken.nei.ItemList.AnyMultiItemFilter;
|
|
||||||
import codechicken.nei.ItemList.PatternItemFilter;
|
|
||||||
import codechicken.nei.ItemMobSpawner;
|
import codechicken.nei.ItemMobSpawner;
|
||||||
import codechicken.nei.ItemStackMap;
|
import codechicken.nei.ItemStackMap;
|
||||||
import codechicken.nei.ItemStackSet;
|
import codechicken.nei.ItemStackSet;
|
||||||
@@ -58,6 +56,8 @@ import codechicken.nei.config.ArrayDumper;
|
|||||||
import codechicken.nei.config.HandlerDumper;
|
import codechicken.nei.config.HandlerDumper;
|
||||||
import codechicken.nei.config.ItemPanelDumper;
|
import codechicken.nei.config.ItemPanelDumper;
|
||||||
import codechicken.nei.config.RegistryDumper;
|
import codechicken.nei.config.RegistryDumper;
|
||||||
|
import codechicken.nei.filter.AnyMultiItemFilter;
|
||||||
|
import codechicken.nei.filter.PatternItemFilter;
|
||||||
import codechicken.nei.guihook.GuiContainerManager;
|
import codechicken.nei.guihook.GuiContainerManager;
|
||||||
import codechicken.nei.recipe.BrewingRecipeHandler;
|
import codechicken.nei.recipe.BrewingRecipeHandler;
|
||||||
import codechicken.nei.recipe.RecipeItemInputHandler;
|
import codechicken.nei.recipe.RecipeItemInputHandler;
|
||||||
|
|||||||
@@ -16,9 +16,6 @@ import codechicken.core.gui.GuiWidget;
|
|||||||
import codechicken.lib.gui.GuiDraw;
|
import codechicken.lib.gui.GuiDraw;
|
||||||
import codechicken.lib.vec.Rectangle4i;
|
import codechicken.lib.vec.Rectangle4i;
|
||||||
import codechicken.nei.CollapsibleItems;
|
import codechicken.nei.CollapsibleItems;
|
||||||
import codechicken.nei.ItemList.AllMultiItemFilter;
|
|
||||||
import codechicken.nei.ItemList.AnyMultiItemFilter;
|
|
||||||
import codechicken.nei.ItemList.NegatedItemFilter;
|
|
||||||
import codechicken.nei.ItemsGrid;
|
import codechicken.nei.ItemsGrid;
|
||||||
import codechicken.nei.ItemsGrid.ItemsGridSlot;
|
import codechicken.nei.ItemsGrid.ItemsGridSlot;
|
||||||
import codechicken.nei.NEIClientConfig;
|
import codechicken.nei.NEIClientConfig;
|
||||||
@@ -28,6 +25,9 @@ import codechicken.nei.PresetsList.Preset;
|
|||||||
import codechicken.nei.SearchField.GuiSearchField;
|
import codechicken.nei.SearchField.GuiSearchField;
|
||||||
import codechicken.nei.TextField;
|
import codechicken.nei.TextField;
|
||||||
import codechicken.nei.api.ItemFilter;
|
import codechicken.nei.api.ItemFilter;
|
||||||
|
import codechicken.nei.filter.AllMultiItemFilter;
|
||||||
|
import codechicken.nei.filter.AnyMultiItemFilter;
|
||||||
|
import codechicken.nei.filter.NegatedItemFilter;
|
||||||
import codechicken.nei.guihook.GuiContainerManager;
|
import codechicken.nei.guihook.GuiContainerManager;
|
||||||
import codechicken.nei.guihook.IContainerTooltipHandler;
|
import codechicken.nei.guihook.IContainerTooltipHandler;
|
||||||
import codechicken.nei.util.TextHistory;
|
import codechicken.nei.util.TextHistory;
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package codechicken.nei.filter;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import codechicken.nei.PositionedStack;
|
||||||
|
import codechicken.nei.api.IRecipeFilter;
|
||||||
|
import codechicken.nei.api.ItemFilter;
|
||||||
|
import codechicken.nei.recipe.IRecipeHandler;
|
||||||
|
|
||||||
|
public class AllIngredientsRecipeFilter extends AllRecipeFilter implements IRecipeFilter {
|
||||||
|
|
||||||
|
public AllIngredientsRecipeFilter(ItemFilter filter) {
|
||||||
|
super(filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matches(IRecipeHandler handler, List<PositionedStack> ingredients, PositionedStack result,
|
||||||
|
List<PositionedStack> others) {
|
||||||
|
|
||||||
|
return allMatch(ingredients);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package codechicken.nei.filter;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import net.minecraft.item.ItemStack;
|
||||||
|
|
||||||
|
import codechicken.nei.NEIClientConfig;
|
||||||
|
import codechicken.nei.api.ItemFilter;
|
||||||
|
|
||||||
|
public class AllMultiItemFilter implements ItemFilter {
|
||||||
|
|
||||||
|
public final List<ItemFilter> filters;
|
||||||
|
|
||||||
|
public AllMultiItemFilter(List<ItemFilter> filters) {
|
||||||
|
this.filters = filters;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AllMultiItemFilter(ItemFilter... filters) {
|
||||||
|
this(new LinkedList<>(Arrays.asList(filters)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public AllMultiItemFilter() {
|
||||||
|
this(new LinkedList<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matches(ItemStack item) {
|
||||||
|
for (ItemFilter filter : filters) try {
|
||||||
|
if (filter != null && !filter.matches(item)) return false;
|
||||||
|
} catch (Exception e) {
|
||||||
|
NEIClientConfig.logger
|
||||||
|
.error("Exception filtering " + item + " with " + filter + " (" + e.getMessage() + ")", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package codechicken.nei.filter;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import codechicken.nei.NEIClientConfig;
|
||||||
|
import codechicken.nei.PositionedStack;
|
||||||
|
import codechicken.nei.api.IRecipeFilter;
|
||||||
|
import codechicken.nei.recipe.IRecipeHandler;
|
||||||
|
|
||||||
|
public class AllMultiRecipeFilter implements IRecipeFilter {
|
||||||
|
|
||||||
|
public final List<IRecipeFilter> filters;
|
||||||
|
|
||||||
|
public AllMultiRecipeFilter(List<IRecipeFilter> filters) {
|
||||||
|
this.filters = filters;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AllMultiRecipeFilter(IRecipeFilter filters) {
|
||||||
|
this(Arrays.asList(filters));
|
||||||
|
}
|
||||||
|
|
||||||
|
public AllMultiRecipeFilter() {
|
||||||
|
this(new ArrayList<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matches(IRecipeHandler handler, List<PositionedStack> ingredients, PositionedStack result,
|
||||||
|
List<PositionedStack> others) {
|
||||||
|
for (IRecipeFilter filter : filters) {
|
||||||
|
try {
|
||||||
|
if (filter != null && !filter.matches(handler, ingredients, result, others)) return false;
|
||||||
|
} catch (Exception e) {
|
||||||
|
NEIClientConfig.logger.error("Exception filtering " + handler + " with " + filter, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package codechicken.nei.filter;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import codechicken.nei.PositionedStack;
|
||||||
|
import codechicken.nei.api.IRecipeFilter;
|
||||||
|
import codechicken.nei.api.ItemFilter;
|
||||||
|
import codechicken.nei.recipe.IRecipeHandler;
|
||||||
|
|
||||||
|
public class AllOthersRecipeFilter extends AllRecipeFilter implements IRecipeFilter {
|
||||||
|
|
||||||
|
public AllOthersRecipeFilter(ItemFilter filter) {
|
||||||
|
super(filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matches(IRecipeHandler handler, List<PositionedStack> ingredients, PositionedStack result,
|
||||||
|
List<PositionedStack> others) {
|
||||||
|
|
||||||
|
return allMatch(others);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package codechicken.nei.filter;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import net.minecraft.item.ItemStack;
|
||||||
|
|
||||||
|
import codechicken.nei.PositionedStack;
|
||||||
|
import codechicken.nei.api.ItemFilter;
|
||||||
|
|
||||||
|
public abstract class AllRecipeFilter {
|
||||||
|
|
||||||
|
private final ItemFilter filter;
|
||||||
|
|
||||||
|
protected AllRecipeFilter(ItemFilter filter) {
|
||||||
|
this.filter = filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean allMatch(List<PositionedStack> items) {
|
||||||
|
for (PositionedStack pStack : items) {
|
||||||
|
if (!allMatch(pStack)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean allMatch(PositionedStack pStack) {
|
||||||
|
if (pStack == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ItemStack stack : pStack.items) {
|
||||||
|
if (!filter.matches(stack)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package codechicken.nei.filter;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import codechicken.nei.PositionedStack;
|
||||||
|
import codechicken.nei.api.IRecipeFilter;
|
||||||
|
import codechicken.nei.api.ItemFilter;
|
||||||
|
import codechicken.nei.recipe.IRecipeHandler;
|
||||||
|
|
||||||
|
public class AllSmartResultRecipeFilter extends AllRecipeFilter implements IRecipeFilter {
|
||||||
|
|
||||||
|
public AllSmartResultRecipeFilter(ItemFilter filter) {
|
||||||
|
super(filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matches(IRecipeHandler handler, List<PositionedStack> ingredients, PositionedStack result,
|
||||||
|
List<PositionedStack> others) {
|
||||||
|
if (result != null) {
|
||||||
|
return allMatch(result);
|
||||||
|
} else {
|
||||||
|
// GT recipes use others as a result
|
||||||
|
return allMatch(others);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package codechicken.nei.filter;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import codechicken.nei.PositionedStack;
|
||||||
|
import codechicken.nei.api.IRecipeFilter;
|
||||||
|
import codechicken.nei.api.ItemFilter;
|
||||||
|
import codechicken.nei.recipe.IRecipeHandler;
|
||||||
|
|
||||||
|
public class AnyIngredientsRecipeFilter extends AnyRecipeFilter implements IRecipeFilter {
|
||||||
|
|
||||||
|
public AnyIngredientsRecipeFilter(ItemFilter filter) {
|
||||||
|
super(filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matches(IRecipeHandler handler, List<PositionedStack> ingredients, PositionedStack result,
|
||||||
|
List<PositionedStack> others) {
|
||||||
|
|
||||||
|
return anyMatch(ingredients);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package codechicken.nei.filter;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import codechicken.nei.PositionedStack;
|
||||||
|
import codechicken.nei.api.IRecipeFilter;
|
||||||
|
import codechicken.nei.api.ItemFilter;
|
||||||
|
import codechicken.nei.recipe.IRecipeHandler;
|
||||||
|
|
||||||
|
public class AnyItemRecipeFilter extends AnyRecipeFilter implements IRecipeFilter {
|
||||||
|
|
||||||
|
public AnyItemRecipeFilter(ItemFilter filter) {
|
||||||
|
super(filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matches(IRecipeHandler handler, List<PositionedStack> ingredients, PositionedStack result,
|
||||||
|
List<PositionedStack> others) {
|
||||||
|
return anyMatch(ingredients) || anyMatch(result) || anyMatch(others);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package codechicken.nei.filter;
|
||||||
|
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import net.minecraft.item.ItemStack;
|
||||||
|
|
||||||
|
import codechicken.nei.NEIClientConfig;
|
||||||
|
import codechicken.nei.api.ItemFilter;
|
||||||
|
|
||||||
|
public class AnyMultiItemFilter implements ItemFilter {
|
||||||
|
|
||||||
|
public final List<ItemFilter> filters;
|
||||||
|
|
||||||
|
public AnyMultiItemFilter(List<ItemFilter> filters) {
|
||||||
|
this.filters = filters;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AnyMultiItemFilter() {
|
||||||
|
this(new LinkedList<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matches(ItemStack item) {
|
||||||
|
for (ItemFilter filter : filters) try {
|
||||||
|
if (filter != null && filter.matches(item)) return true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
NEIClientConfig.logger
|
||||||
|
.error("Exception filtering " + item + " with " + filter + " (" + e.getMessage() + ")", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package codechicken.nei.filter;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import codechicken.nei.PositionedStack;
|
||||||
|
import codechicken.nei.api.IRecipeFilter;
|
||||||
|
import codechicken.nei.api.ItemFilter;
|
||||||
|
import codechicken.nei.recipe.IRecipeHandler;
|
||||||
|
|
||||||
|
public class AnyOthersRecipeFilter extends AnyRecipeFilter implements IRecipeFilter {
|
||||||
|
|
||||||
|
public AnyOthersRecipeFilter(ItemFilter filter) {
|
||||||
|
super(filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matches(IRecipeHandler handler, List<PositionedStack> ingredients, PositionedStack result,
|
||||||
|
List<PositionedStack> others) {
|
||||||
|
|
||||||
|
return anyMatch(others);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package codechicken.nei.filter;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import net.minecraft.item.ItemStack;
|
||||||
|
|
||||||
|
import codechicken.nei.PositionedStack;
|
||||||
|
import codechicken.nei.api.ItemFilter;
|
||||||
|
|
||||||
|
public abstract class AnyRecipeFilter {
|
||||||
|
|
||||||
|
private final ItemFilter filter;
|
||||||
|
|
||||||
|
protected AnyRecipeFilter(ItemFilter filter) {
|
||||||
|
this.filter = filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean anyMatch(List<PositionedStack> items) {
|
||||||
|
for (PositionedStack pStack : items) {
|
||||||
|
if (anyMatch(pStack)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean anyMatch(PositionedStack pStack) {
|
||||||
|
if (pStack == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ItemStack stack : pStack.items) {
|
||||||
|
if (filter.matches(stack)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package codechicken.nei.filter;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import codechicken.nei.PositionedStack;
|
||||||
|
import codechicken.nei.api.IRecipeFilter;
|
||||||
|
import codechicken.nei.api.ItemFilter;
|
||||||
|
import codechicken.nei.recipe.IRecipeHandler;
|
||||||
|
|
||||||
|
public class AnySmartResultRecipeFilter extends AnyRecipeFilter implements IRecipeFilter {
|
||||||
|
|
||||||
|
public AnySmartResultRecipeFilter(ItemFilter filter) {
|
||||||
|
super(filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matches(IRecipeHandler handler, List<PositionedStack> ingredients, PositionedStack result,
|
||||||
|
List<PositionedStack> others) {
|
||||||
|
if (result != null) {
|
||||||
|
return anyMatch(result);
|
||||||
|
} else {
|
||||||
|
// GT recipes use others as a result
|
||||||
|
return anyMatch(others);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package codechicken.nei.filter;
|
||||||
|
|
||||||
|
import net.minecraft.item.ItemStack;
|
||||||
|
|
||||||
|
import codechicken.nei.SearchField;
|
||||||
|
import codechicken.nei.api.ItemFilter;
|
||||||
|
|
||||||
|
public class DoubleItemFilter implements ItemFilter {
|
||||||
|
|
||||||
|
private final ItemFilter filter;
|
||||||
|
private final PatternItemFilter patternFilter;
|
||||||
|
|
||||||
|
public DoubleItemFilter(ItemFilter filter, String itemFilter) {
|
||||||
|
this.filter = filter;
|
||||||
|
this.patternFilter = new PatternItemFilter(SearchField.getPattern(itemFilter));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matches(ItemStack item) {
|
||||||
|
if (filter.matches(item)) {
|
||||||
|
return patternFilter.matches(item);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package codechicken.nei.filter;
|
||||||
|
|
||||||
|
import net.minecraft.item.ItemStack;
|
||||||
|
|
||||||
|
import codechicken.nei.api.ItemFilter;
|
||||||
|
|
||||||
|
public class EverythingItemFilter implements ItemFilter {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matches(ItemStack item) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package codechicken.nei.filter;
|
||||||
|
|
||||||
|
import net.minecraft.item.ItemStack;
|
||||||
|
|
||||||
|
import codechicken.nei.api.ItemFilter;
|
||||||
|
|
||||||
|
public class NegatedItemFilter implements ItemFilter {
|
||||||
|
|
||||||
|
private final ItemFilter filter;
|
||||||
|
|
||||||
|
public NegatedItemFilter(ItemFilter filter) {
|
||||||
|
this.filter = filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matches(ItemStack item) {
|
||||||
|
return this.filter == null || !this.filter.matches(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package codechicken.nei.filter;
|
||||||
|
|
||||||
|
import net.minecraft.item.ItemStack;
|
||||||
|
|
||||||
|
import codechicken.nei.api.ItemFilter;
|
||||||
|
|
||||||
|
public class NothingItemFilter implements ItemFilter {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matches(ItemStack item) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package codechicken.nei.filter;
|
||||||
|
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import net.minecraft.item.ItemStack;
|
||||||
|
import net.minecraft.util.EnumChatFormatting;
|
||||||
|
|
||||||
|
import codechicken.nei.api.ItemFilter;
|
||||||
|
|
||||||
|
public class PatternItemFilter implements ItemFilter {
|
||||||
|
|
||||||
|
public final Pattern pattern;
|
||||||
|
|
||||||
|
public PatternItemFilter(Pattern pattern) {
|
||||||
|
this.pattern = pattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matches(ItemStack item) {
|
||||||
|
String displayName = EnumChatFormatting.getTextWithoutFormattingCodes(item.getDisplayName());
|
||||||
|
|
||||||
|
if (displayName != null && !displayName.isEmpty() && pattern.matcher(displayName).find()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.hasDisplayName()) {
|
||||||
|
displayName = EnumChatFormatting
|
||||||
|
.getTextWithoutFormattingCodes(item.getItem().getItemStackDisplayName(item));
|
||||||
|
|
||||||
|
return displayName != null && !displayName.isEmpty() && pattern.matcher(displayName).find();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -43,6 +43,7 @@ import codechicken.nei.api.ItemFilter;
|
|||||||
import codechicken.nei.api.TaggedInventoryArea;
|
import codechicken.nei.api.TaggedInventoryArea;
|
||||||
import codechicken.nei.drawable.DrawableBuilder;
|
import codechicken.nei.drawable.DrawableBuilder;
|
||||||
import codechicken.nei.drawable.DrawableResource;
|
import codechicken.nei.drawable.DrawableResource;
|
||||||
|
import codechicken.nei.filter.AllMultiRecipeFilter;
|
||||||
import codechicken.nei.guihook.GuiContainerManager;
|
import codechicken.nei.guihook.GuiContainerManager;
|
||||||
import codechicken.nei.guihook.IContainerTooltipHandler;
|
import codechicken.nei.guihook.IContainerTooltipHandler;
|
||||||
import codechicken.nei.guihook.IGuiClientSide;
|
import codechicken.nei.guihook.IGuiClientSide;
|
||||||
@@ -118,86 +119,6 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
|
|||||||
|
|
||||||
private int yShift = 0;
|
private int yShift = 0;
|
||||||
|
|
||||||
public static class ItemRecipeFilter implements IRecipeFilter {
|
|
||||||
|
|
||||||
public ItemFilter filter;
|
|
||||||
|
|
||||||
public ItemRecipeFilter(ItemFilter filter) {
|
|
||||||
this.filter = filter;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean matches(IRecipeHandler handler, List<PositionedStack> ingredients, PositionedStack result,
|
|
||||||
List<PositionedStack> others) {
|
|
||||||
|
|
||||||
if (matchPositionedStack(ingredients)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (matchPositionedStack(result)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (matchPositionedStack(others)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean matchPositionedStack(List<PositionedStack> items) {
|
|
||||||
for (PositionedStack pStack : items) {
|
|
||||||
if (matchPositionedStack(pStack)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean matchPositionedStack(PositionedStack pStack) {
|
|
||||||
if (pStack == null) return false;
|
|
||||||
|
|
||||||
for (ItemStack stack : pStack.items) {
|
|
||||||
if (filter.matches(stack)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class AllMultiRecipeFilter implements IRecipeFilter {
|
|
||||||
|
|
||||||
public List<IRecipeFilter> filters;
|
|
||||||
|
|
||||||
public AllMultiRecipeFilter(List<IRecipeFilter> filters) {
|
|
||||||
this.filters = filters;
|
|
||||||
}
|
|
||||||
|
|
||||||
public AllMultiRecipeFilter(IRecipeFilter filters) {
|
|
||||||
this(Arrays.asList(filters));
|
|
||||||
}
|
|
||||||
|
|
||||||
public AllMultiRecipeFilter() {
|
|
||||||
this(new ArrayList<>());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean matches(IRecipeHandler handler, List<PositionedStack> ingredients, PositionedStack result,
|
|
||||||
List<PositionedStack> others) {
|
|
||||||
for (IRecipeFilter filter : filters) {
|
|
||||||
try {
|
|
||||||
if (filter != null && !filter.matches(handler, ingredients, result, others)) return false;
|
|
||||||
} catch (Exception e) {
|
|
||||||
NEIClientConfig.logger.error("Exception filtering " + handler + " with " + filter, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static final RestartableTask updateFilter = new RestartableTask("NEI Recipe Filtering") {
|
protected static final RestartableTask updateFilter = new RestartableTask("NEI Recipe Filtering") {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -214,7 +135,7 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
|
|||||||
searchHandler.setSearchIndices(null);
|
searchHandler.setSearchIndices(null);
|
||||||
guiRecipe.changePage(0);
|
guiRecipe.changePage(0);
|
||||||
} else {
|
} else {
|
||||||
final IRecipeFilter filter = new ItemRecipeFilter(GuiRecipe.searchField.getFilter());
|
final IRecipeFilter filter = GuiRecipe.searchField.getRecipeFilter();
|
||||||
final List<Integer> filtered = searchHandler.getSearchResult(filter);
|
final List<Integer> filtered = searchHandler.getSearchResult(filter);
|
||||||
|
|
||||||
if (filtered == null) {
|
if (filtered == null) {
|
||||||
@@ -546,7 +467,7 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
|
|||||||
|
|
||||||
synchronized (recipeFilterers) {
|
synchronized (recipeFilterers) {
|
||||||
for (IRecipeFilterProvider p : recipeFilterers) {
|
for (IRecipeFilterProvider p : recipeFilterers) {
|
||||||
IRecipeFilter filter = p.getFilter();
|
IRecipeFilter filter = p.getRecipeFilter();
|
||||||
if (filter != null) {
|
if (filter != null) {
|
||||||
recipeFilter.filters.add(filter);
|
recipeFilter.filters.add(filter);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package codechicken.nei.search;
|
||||||
|
|
||||||
|
import codechicken.nei.SearchTokenParser;
|
||||||
|
|
||||||
|
public abstract class AbstractSearchExpressionVisitor<T> extends SearchExpressionParserBaseVisitor<T> {
|
||||||
|
|
||||||
|
protected final SearchTokenParser searchParser;
|
||||||
|
|
||||||
|
protected AbstractSearchExpressionVisitor(SearchTokenParser searchParser) {
|
||||||
|
this.searchParser = searchParser;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SearchTokenParser getSearchParser() {
|
||||||
|
return searchParser;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,155 @@
|
|||||||
|
package codechicken.nei.search;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import org.antlr.v4.runtime.ParserRuleContext;
|
||||||
|
import org.antlr.v4.runtime.tree.ParseTree;
|
||||||
|
import org.antlr.v4.runtime.tree.RuleNode;
|
||||||
|
|
||||||
|
import codechicken.nei.NEIClientConfig;
|
||||||
|
import codechicken.nei.SearchTokenParser;
|
||||||
|
import codechicken.nei.api.ItemFilter;
|
||||||
|
import codechicken.nei.filter.AllMultiItemFilter;
|
||||||
|
import codechicken.nei.filter.AnyMultiItemFilter;
|
||||||
|
import codechicken.nei.filter.NegatedItemFilter;
|
||||||
|
import codechicken.nei.filter.NothingItemFilter;
|
||||||
|
|
||||||
|
public class ItemFilterVisitor extends AbstractSearchExpressionVisitor<ItemFilter> {
|
||||||
|
|
||||||
|
private static final Pattern REGEX_ESCAPED_SPACE_PATTERN = Pattern.compile("([^\\\\](?:\\\\\\\\)+)?\\\\ ");
|
||||||
|
private static final Pattern PLAIN_TEXT_ESCAPED_PATTERN = Pattern.compile("\\\\(.)");
|
||||||
|
private static final Pattern ESCAPED_QUOTE_PATTERN = Pattern.compile("\\\\\"");
|
||||||
|
private static final Pattern ESCAPED_SPACE_PATTERN = Pattern.compile("\\\\ ");
|
||||||
|
|
||||||
|
public ItemFilterVisitor(SearchTokenParser searchParser) {
|
||||||
|
super(searchParser);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ItemFilter visitChildren(RuleNode node) {
|
||||||
|
if (node instanceof ParserRuleContext) {
|
||||||
|
return visitChildren((ParserRuleContext) node, null);
|
||||||
|
} else {
|
||||||
|
return defaultResult();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ItemFilter visitOrExpression(SearchExpressionParser.OrExpressionContext ctx) {
|
||||||
|
return visitChildren(ctx, AnyMultiItemFilter::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ItemFilter visitSequenceExpression(SearchExpressionParser.SequenceExpressionContext ctx) {
|
||||||
|
return visitChildren(ctx, AllMultiItemFilter::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ItemFilter visitNegateExpression(SearchExpressionParser.NegateExpressionContext ctx) {
|
||||||
|
return new NegatedItemFilter(visitChildren(ctx));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ItemFilter visitPrefixedExpression(SearchExpressionParser.PrefixedExpressionContext ctx) {
|
||||||
|
if (ctx.token() != null) {
|
||||||
|
final String cleanText = getTokenCleanText(ctx.token());
|
||||||
|
final SearchTokenParser.ISearchParserProvider provider = searchParser.getProvider(ctx.token().prefix);
|
||||||
|
if (cleanText == null || provider == null) {
|
||||||
|
return defaultResult();
|
||||||
|
}
|
||||||
|
return provider.getFilter(cleanText);
|
||||||
|
}
|
||||||
|
return defaultResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ItemFilter visitToken(SearchExpressionParser.TokenContext ctx) {
|
||||||
|
final String cleanText = getTokenCleanText(ctx);
|
||||||
|
if (cleanText != null) {
|
||||||
|
return getAlwaysProvidersFilter(cleanText);
|
||||||
|
}
|
||||||
|
return defaultResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ItemFilter defaultResult() {
|
||||||
|
return new NothingItemFilter();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getTokenCleanText(SearchExpressionParser.TokenContext ctx) {
|
||||||
|
String cleanText = null;
|
||||||
|
if (ctx.PLAIN_TEXT() != null) {
|
||||||
|
cleanText = Pattern.quote(
|
||||||
|
PLAIN_TEXT_ESCAPED_PATTERN.matcher(ctx.PLAIN_TEXT().getSymbol().getText())
|
||||||
|
// Unescape everything in search expression
|
||||||
|
.replaceAll("$1"));
|
||||||
|
} else if (ctx.regex() != null) {
|
||||||
|
if (ctx.regex().REGEX_CONTENT() != null) {
|
||||||
|
cleanText = ctx.regex().REGEX_CONTENT().getSymbol().getText();
|
||||||
|
final int spaceModeEnabled = NEIClientConfig.getIntSetting("inventory.search.spaceMode");
|
||||||
|
// Unescape spaces
|
||||||
|
if (spaceModeEnabled == 1) {
|
||||||
|
cleanText = REGEX_ESCAPED_SPACE_PATTERN.matcher(cleanText).replaceAll("$1 ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (ctx.quoted() != null) {
|
||||||
|
if (ctx.quoted().QUOTED_CONTENT() != null) {
|
||||||
|
cleanText = ctx.quoted().QUOTED_CONTENT().getSymbol().getText();
|
||||||
|
// Unescape quotes
|
||||||
|
cleanText = ESCAPED_QUOTE_PATTERN.matcher(cleanText).replaceAll("\"");
|
||||||
|
cleanText = Pattern.quote(cleanText);
|
||||||
|
final int spaceModeEnabled = NEIClientConfig.getIntSetting("inventory.search.spaceMode");
|
||||||
|
// Unescape spaces
|
||||||
|
if (spaceModeEnabled == 1) {
|
||||||
|
cleanText = ESCAPED_SPACE_PATTERN.matcher(cleanText).replaceAll(" ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cleanText;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ItemFilter visitChildren(ParserRuleContext node, Function<List<ItemFilter>, ItemFilter> filterConstructor) {
|
||||||
|
if (node.children != null && !node.children.isEmpty()) {
|
||||||
|
// By default return any found rule child filter (there should be only one)
|
||||||
|
if (filterConstructor == null) {
|
||||||
|
for (final ParseTree child : node.children) {
|
||||||
|
if (child instanceof RuleNode) {
|
||||||
|
return visit(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return defaultResult();
|
||||||
|
// Otherwise create a filter out of rule childrens' filters
|
||||||
|
} else {
|
||||||
|
final List<ItemFilter> filters = new ArrayList<>();
|
||||||
|
for (final ParseTree child : node.children) {
|
||||||
|
if (child instanceof RuleNode) {
|
||||||
|
filters.add(visit(child));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return constructFilter(filters, filterConstructor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return defaultResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
private ItemFilter getAlwaysProvidersFilter(String searchText) {
|
||||||
|
final List<ItemFilter> filters = searchParser.getAlwaysProvidersFilters(searchText);
|
||||||
|
return constructFilter(filters, AnyMultiItemFilter::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ItemFilter constructFilter(List<ItemFilter> filters, Function<List<ItemFilter>, ItemFilter> constructor) {
|
||||||
|
if (!filters.isEmpty()) {
|
||||||
|
// Propagate the result up
|
||||||
|
if (filters.size() == 1) {
|
||||||
|
return filters.get(0);
|
||||||
|
}
|
||||||
|
return constructor.apply(filters);
|
||||||
|
} else {
|
||||||
|
return defaultResult();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
package codechicken.nei.search;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import codechicken.nei.SearchTokenParser;
|
||||||
|
import codechicken.nei.api.IRecipeFilter;
|
||||||
|
import codechicken.nei.api.ItemFilter;
|
||||||
|
import codechicken.nei.filter.AllIngredientsRecipeFilter;
|
||||||
|
import codechicken.nei.filter.AllMultiRecipeFilter;
|
||||||
|
import codechicken.nei.filter.AllOthersRecipeFilter;
|
||||||
|
import codechicken.nei.filter.AllSmartResultRecipeFilter;
|
||||||
|
import codechicken.nei.filter.AnyIngredientsRecipeFilter;
|
||||||
|
import codechicken.nei.filter.AnyItemRecipeFilter;
|
||||||
|
import codechicken.nei.filter.AnyOthersRecipeFilter;
|
||||||
|
import codechicken.nei.filter.AnySmartResultRecipeFilter;
|
||||||
|
import codechicken.nei.filter.EverythingItemFilter;
|
||||||
|
|
||||||
|
public class RecipeFilterVisitor extends AbstractSearchExpressionVisitor<IRecipeFilter> {
|
||||||
|
|
||||||
|
private final ItemFilterVisitor itemFilterVisitor;
|
||||||
|
|
||||||
|
public RecipeFilterVisitor(SearchTokenParser searchParser) {
|
||||||
|
super(searchParser);
|
||||||
|
itemFilterVisitor = new ItemFilterVisitor(searchParser);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IRecipeFilter visitRecipeSearchExpression(SearchExpressionParser.RecipeSearchExpressionContext ctx) {
|
||||||
|
if (ctx.recipeClauseExpression() != null) {
|
||||||
|
final List<IRecipeFilter> filters = new ArrayList<>();
|
||||||
|
for (SearchExpressionParser.RecipeClauseExpressionContext clauseCtx : ctx.recipeClauseExpression()) {
|
||||||
|
filters.add(createRecipeFilter(clauseCtx.searchExpression()));
|
||||||
|
}
|
||||||
|
return constructFilter(filters);
|
||||||
|
}
|
||||||
|
return defaultResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected IRecipeFilter defaultResult() {
|
||||||
|
return new AnyItemRecipeFilter(new EverythingItemFilter());
|
||||||
|
}
|
||||||
|
|
||||||
|
private IRecipeFilter createRecipeFilter(SearchExpressionParser.SearchExpressionContext ctx) {
|
||||||
|
if (ctx == null) {
|
||||||
|
return defaultResult();
|
||||||
|
}
|
||||||
|
final ItemFilter itemFilter = itemFilterVisitor.visitSearchExpression(ctx);
|
||||||
|
switch (ctx.type) {
|
||||||
|
case 0:
|
||||||
|
return getAllOrAnyFilter(
|
||||||
|
ctx.allRecipe,
|
||||||
|
itemFilter,
|
||||||
|
AnyIngredientsRecipeFilter::new,
|
||||||
|
AllIngredientsRecipeFilter::new);
|
||||||
|
case 1:
|
||||||
|
return getAllOrAnyFilter(
|
||||||
|
ctx.allRecipe,
|
||||||
|
itemFilter,
|
||||||
|
AnySmartResultRecipeFilter::new,
|
||||||
|
AllSmartResultRecipeFilter::new);
|
||||||
|
case 2:
|
||||||
|
return getAllOrAnyFilter(
|
||||||
|
ctx.allRecipe,
|
||||||
|
itemFilter,
|
||||||
|
AnyOthersRecipeFilter::new,
|
||||||
|
AllOthersRecipeFilter::new);
|
||||||
|
// Doesn't support all by default
|
||||||
|
case 3:
|
||||||
|
return getAllOrAnyFilter(ctx.allRecipe, itemFilter, AnyItemRecipeFilter::new, AnyItemRecipeFilter::new);
|
||||||
|
default:
|
||||||
|
return defaultResult();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private IRecipeFilter getAllOrAnyFilter(boolean allRecipe, ItemFilter itemFilter,
|
||||||
|
Function<ItemFilter, IRecipeFilter> createAnyFilter, Function<ItemFilter, IRecipeFilter> createAllFilter) {
|
||||||
|
if (allRecipe) {
|
||||||
|
return createAllFilter.apply(itemFilter);
|
||||||
|
} else {
|
||||||
|
return createAnyFilter.apply(itemFilter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private IRecipeFilter constructFilter(List<IRecipeFilter> filters) {
|
||||||
|
if (!filters.isEmpty()) {
|
||||||
|
// Propagate the result up
|
||||||
|
if (filters.size() == 1) {
|
||||||
|
return filters.get(0);
|
||||||
|
}
|
||||||
|
return new AllMultiRecipeFilter(filters);
|
||||||
|
} else {
|
||||||
|
return defaultResult();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package codechicken.nei.search;
|
||||||
|
|
||||||
|
import org.antlr.v4.runtime.BaseErrorListener;
|
||||||
|
import org.antlr.v4.runtime.RecognitionException;
|
||||||
|
import org.antlr.v4.runtime.Recognizer;
|
||||||
|
|
||||||
|
import codechicken.nei.NEIClientConfig;
|
||||||
|
|
||||||
|
public class SearchExpressionErrorListener extends BaseErrorListener {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol, int line, int charPositionInLine,
|
||||||
|
String msg, RecognitionException e) {
|
||||||
|
NEIClientConfig.logger
|
||||||
|
.error("NEI Search grammar exception on line " + line + " char " + charPositionInLine + ": " + msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
package codechicken.nei.search;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import net.minecraft.util.EnumChatFormatting;
|
||||||
|
|
||||||
|
import org.antlr.v4.runtime.ParserRuleContext;
|
||||||
|
import org.antlr.v4.runtime.tree.ParseTree;
|
||||||
|
import org.antlr.v4.runtime.tree.RuleNode;
|
||||||
|
import org.antlr.v4.runtime.tree.TerminalNode;
|
||||||
|
|
||||||
|
import codechicken.nei.NEIClientConfig;
|
||||||
|
import codechicken.nei.SearchTokenParser;
|
||||||
|
import codechicken.nei.SearchTokenParser.ISearchParserProvider;
|
||||||
|
|
||||||
|
public class SearchExpressionFormatVisitor extends AbstractSearchExpressionVisitor<String> {
|
||||||
|
|
||||||
|
private static final Pattern REGEX_ESCAPED_SPACE_PATTERN = Pattern.compile("([^\\\\](?:\\\\\\\\)+)?\\\\ ");
|
||||||
|
private static final Pattern ESCAPED_SPACE_PATTERN = Pattern.compile("\\\\ ");
|
||||||
|
|
||||||
|
public SearchExpressionFormatVisitor(SearchTokenParser searchParser) {
|
||||||
|
super(searchParser);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String visitChildren(RuleNode node) {
|
||||||
|
if (node instanceof ParserRuleContext) {
|
||||||
|
return visitChildren((ParserRuleContext) node, null);
|
||||||
|
} else {
|
||||||
|
return defaultResult();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String visitPrefixedExpression(SearchExpressionParser.PrefixedExpressionContext ctx) {
|
||||||
|
final EnumChatFormatting format = getFormatting(ctx.prefix, null);
|
||||||
|
return format + String.valueOf(ctx.prefix) + visitToken(ctx.token());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String visitToken(SearchExpressionParser.TokenContext ctx) {
|
||||||
|
return getTokenCleanText(ctx, ctx.prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String visitRegex(SearchExpressionParser.RegexContext ctx) {
|
||||||
|
return visitChildren(ctx, ctx.prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String visitQuoted(SearchExpressionParser.QuotedContext ctx) {
|
||||||
|
return visitChildren(ctx, ctx.prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String defaultResult() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getTokenCleanText(SearchExpressionParser.TokenContext ctx, Character prefix) {
|
||||||
|
String cleanText = null;
|
||||||
|
final int spaceModeEnabled = NEIClientConfig.getIntSetting("inventory.search.spaceMode");
|
||||||
|
if (ctx.PLAIN_TEXT() != null) {
|
||||||
|
cleanText = ctx.PLAIN_TEXT().getSymbol().getText();
|
||||||
|
// Unescape spaces
|
||||||
|
if (spaceModeEnabled == 1) {
|
||||||
|
cleanText = ESCAPED_SPACE_PATTERN.matcher(cleanText).replaceAll(" ");
|
||||||
|
}
|
||||||
|
final EnumChatFormatting format = getFormatting(prefix, EnumChatFormatting.RESET);
|
||||||
|
return format + cleanText;
|
||||||
|
} else if (ctx.regex() != null) {
|
||||||
|
cleanText = visitRegex(ctx.regex());
|
||||||
|
if (spaceModeEnabled == 1) {
|
||||||
|
cleanText = REGEX_ESCAPED_SPACE_PATTERN.matcher(cleanText).replaceAll("$1 ");
|
||||||
|
}
|
||||||
|
} else if (ctx.quoted() != null) {
|
||||||
|
cleanText = visitQuoted(ctx.quoted());
|
||||||
|
if (spaceModeEnabled == 1) {
|
||||||
|
cleanText = ESCAPED_SPACE_PATTERN.matcher(cleanText).replaceAll(" ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cleanText;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String formatChild(ParseTree child, Character prefix) {
|
||||||
|
if (child instanceof TerminalNode) {
|
||||||
|
final TerminalNode node = (TerminalNode) child;
|
||||||
|
final int type = node.getSymbol().getType();
|
||||||
|
final String format = Optional.ofNullable(
|
||||||
|
// check if highlight is defined for the token
|
||||||
|
Optional.ofNullable(SearchExpressionUtils.getHighlight(type))
|
||||||
|
// check if highlight is defined for the prefix
|
||||||
|
.orElse(getFormatting(prefix, null))
|
||||||
|
// use default highlight otherwise
|
||||||
|
).map(EnumChatFormatting::toString).orElse("");
|
||||||
|
|
||||||
|
return format + node.getSymbol().getText();
|
||||||
|
} else {
|
||||||
|
return visit(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String visitChildren(ParserRuleContext node, Character prefix) {
|
||||||
|
if (node.children != null && !node.children.isEmpty()) {
|
||||||
|
return node.children.stream().map(child -> formatChild(child, prefix)).collect(Collectors.joining());
|
||||||
|
}
|
||||||
|
return defaultResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
private EnumChatFormatting getFormatting(Character prefix, EnumChatFormatting defaultFormatting) {
|
||||||
|
if (prefix != null) {
|
||||||
|
if (prefix == '\0') {
|
||||||
|
return EnumChatFormatting.RESET;
|
||||||
|
}
|
||||||
|
final ISearchParserProvider provider = searchParser.getProvider(prefix);
|
||||||
|
if (provider != null) {
|
||||||
|
return provider.getHighlightedColor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return defaultFormatting;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
package codechicken.nei.search;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import net.minecraft.util.EnumChatFormatting;
|
||||||
|
|
||||||
|
import org.antlr.v4.runtime.CharStream;
|
||||||
|
import org.antlr.v4.runtime.CharStreams;
|
||||||
|
import org.antlr.v4.runtime.CommonTokenStream;
|
||||||
|
|
||||||
|
import codechicken.nei.NEIClientConfig;
|
||||||
|
import codechicken.nei.SearchTokenParser;
|
||||||
|
|
||||||
|
public class SearchExpressionUtils {
|
||||||
|
|
||||||
|
private static final Map<Integer, EnumChatFormatting> HIGHLIGHT_MAP = new HashMap<>();
|
||||||
|
static {
|
||||||
|
for (HIGHLIGHTS elem : HIGHLIGHTS.values()) {
|
||||||
|
for (int parserType : elem.parserTypes) {
|
||||||
|
HIGHLIGHT_MAP.put(parserType, elem.f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static enum HIGHLIGHTS {
|
||||||
|
|
||||||
|
RECIPE(EnumChatFormatting.GREEN, SearchExpressionParser.RECIPE_INGREDIENTS,
|
||||||
|
SearchExpressionParser.RECIPE_RESULT, SearchExpressionParser.RECIPE_OTHERS),
|
||||||
|
OR(EnumChatFormatting.GRAY, SearchExpressionParser.OR),
|
||||||
|
BRACKETS(EnumChatFormatting.GRAY, SearchExpressionParser.LEFT_BRACKET, SearchExpressionParser.RIGHT_BRACKET),
|
||||||
|
NEGATE(EnumChatFormatting.BLUE, SearchExpressionParser.DASH),
|
||||||
|
REGEX(EnumChatFormatting.AQUA, SearchExpressionParser.REGEX_LEFT, SearchExpressionParser.REGEX_RIGHT),
|
||||||
|
QUOTED(EnumChatFormatting.GOLD, SearchExpressionParser.QUOTE_LEFT, SearchExpressionParser.QUOTE_RIGHT);
|
||||||
|
|
||||||
|
public final EnumChatFormatting f;
|
||||||
|
public final int[] parserTypes;
|
||||||
|
|
||||||
|
private HIGHLIGHTS(EnumChatFormatting format, int... parserTypes) {
|
||||||
|
this.f = format;
|
||||||
|
this.parserTypes = parserTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return this.f.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static EnumChatFormatting getHighlight(Integer parserType) {
|
||||||
|
return HIGHLIGHT_MAP.get(parserType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T visitSearchExpression(String text, AbstractSearchExpressionVisitor<T> visitor) {
|
||||||
|
SearchExpressionParser parser = createSearchExpressionParser(text, visitor.getSearchParser(), visitor);
|
||||||
|
return visitor.visitRecipeSearchExpression(parser.recipeSearchExpression());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T> SearchExpressionParser createSearchExpressionParser(String text, SearchTokenParser searchParser,
|
||||||
|
SearchExpressionParserBaseVisitor<T> visitor) {
|
||||||
|
final boolean doLogExceptions = NEIClientConfig.getBooleanSetting("inventory.search.logSearchExceptions");
|
||||||
|
final CharStream inputStream = CharStreams.fromString(text);
|
||||||
|
final SearchExpressionErrorListener errorListener = new SearchExpressionErrorListener();
|
||||||
|
final SearchExpressionLexer lexer = new SearchExpressionLexer(inputStream, searchParser);
|
||||||
|
lexer.removeErrorListeners();
|
||||||
|
if (doLogExceptions) {
|
||||||
|
lexer.addErrorListener(errorListener);
|
||||||
|
}
|
||||||
|
final CommonTokenStream tokenStream = new CommonTokenStream(lexer);
|
||||||
|
final SearchExpressionParser parser = new SearchExpressionParser(tokenStream);
|
||||||
|
parser.removeErrorListeners();
|
||||||
|
if (doLogExceptions) {
|
||||||
|
parser.addErrorListener(errorListener);
|
||||||
|
}
|
||||||
|
return parser;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -14,11 +14,11 @@ import net.minecraft.nbt.NBTTagCompound;
|
|||||||
import net.minecraft.nbt.NBTTagList;
|
import net.minecraft.nbt.NBTTagList;
|
||||||
import net.minecraftforge.oredict.OreDictionary;
|
import net.minecraftforge.oredict.OreDictionary;
|
||||||
|
|
||||||
import codechicken.nei.ItemList.AllMultiItemFilter;
|
|
||||||
import codechicken.nei.ItemList.AnyMultiItemFilter;
|
|
||||||
import codechicken.nei.ItemList.NegatedItemFilter;
|
|
||||||
import codechicken.nei.NEIServerUtils;
|
import codechicken.nei.NEIServerUtils;
|
||||||
import codechicken.nei.api.ItemFilter;
|
import codechicken.nei.api.ItemFilter;
|
||||||
|
import codechicken.nei.filter.AllMultiItemFilter;
|
||||||
|
import codechicken.nei.filter.AnyMultiItemFilter;
|
||||||
|
import codechicken.nei.filter.NegatedItemFilter;
|
||||||
import cpw.mods.fml.common.registry.FMLControlledNamespacedRegistry;
|
import cpw.mods.fml.common.registry.FMLControlledNamespacedRegistry;
|
||||||
import cpw.mods.fml.common.registry.GameData;
|
import cpw.mods.fml.common.registry.GameData;
|
||||||
|
|
||||||
|
|||||||
@@ -287,6 +287,7 @@ nei.options.inventory.search.patternMode=Search Mode
|
|||||||
nei.options.inventory.search.patternMode.0=Plain
|
nei.options.inventory.search.patternMode.0=Plain
|
||||||
nei.options.inventory.search.patternMode.1=Extended
|
nei.options.inventory.search.patternMode.1=Extended
|
||||||
nei.options.inventory.search.patternMode.2=Regex
|
nei.options.inventory.search.patternMode.2=Regex
|
||||||
|
nei.options.inventory.search.patternMode.3=Extended+
|
||||||
nei.options.inventory.search.spaceMode=Space Works like
|
nei.options.inventory.search.spaceMode=Space Works like
|
||||||
nei.options.inventory.search.spaceMode.0=AND
|
nei.options.inventory.search.spaceMode.0=AND
|
||||||
nei.options.inventory.search.spaceMode.0.tip=Space between non-prefixed tokens works like "AND"
|
nei.options.inventory.search.spaceMode.0.tip=Space between non-prefixed tokens works like "AND"
|
||||||
|
|||||||
@@ -0,0 +1,410 @@
|
|||||||
|
package codechicken.nei;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
import static org.junit.jupiter.api.Named.named;
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.resources.Language;
|
||||||
|
import net.minecraft.client.resources.LanguageManager;
|
||||||
|
import net.minecraft.item.ItemStack;
|
||||||
|
import net.minecraft.util.EnumChatFormatting;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.Arguments;
|
||||||
|
import org.junit.jupiter.params.provider.FieldSource;
|
||||||
|
import org.mockito.MockedStatic;
|
||||||
|
|
||||||
|
import codechicken.nei.SearchField.SearchParserProvider;
|
||||||
|
import codechicken.nei.api.ItemFilter;
|
||||||
|
import codechicken.nei.filter.AllMultiItemFilter;
|
||||||
|
import codechicken.nei.filter.AnyMultiItemFilter;
|
||||||
|
import codechicken.nei.filter.PatternItemFilter;
|
||||||
|
import codechicken.nei.search.IdentifierFilter;
|
||||||
|
|
||||||
|
@DisplayName("Search expression")
|
||||||
|
public class SearchTokenParserTest {
|
||||||
|
|
||||||
|
private static class ModeValue {
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
private final int value;
|
||||||
|
|
||||||
|
public ModeValue(int value, String name) {
|
||||||
|
this.value = value;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final ModeValue[] spaceModes = { new ModeValue(0, "space works like AND"),
|
||||||
|
new ModeValue(1, "space works like SPACE"), new ModeValue(2, "space works like AND-X") };
|
||||||
|
private static final ModeValue[] spaceIsAndMode = { spaceModes[0] };
|
||||||
|
private static final ModeValue[] spaceIsSpaceMode = { spaceModes[1] };
|
||||||
|
private static final ModeValue[] patternModes = { new ModeValue(0, "Plain Text mode"),
|
||||||
|
new ModeValue(1, "Extended mode"), new ModeValue(2, "Regex mode"), new ModeValue(3, "Extended+ mode") };
|
||||||
|
private static final ModeValue[] extendedPlusPatternMode = { patternModes[3] };
|
||||||
|
private static final ModeValue[] extendedPatternModes = { patternModes[1], patternModes[3] };
|
||||||
|
|
||||||
|
private static final SearchParserProvider defaultParserProvider = parserProvider(
|
||||||
|
'\0',
|
||||||
|
"default",
|
||||||
|
SearchTokenParser.SearchMode.ALWAYS,
|
||||||
|
PatternItemFilter::new);
|
||||||
|
|
||||||
|
private static final Function<Pattern, ItemFilter> customCreateFilter = (pattern) -> new IdentifierFilter(pattern) {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matches(ItemStack stack) {
|
||||||
|
return pattern.matcher(getStringIdentifier(stack)).find();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getStringIdentifier(ItemStack stack) {
|
||||||
|
if (stack == itemA) {
|
||||||
|
return "A";
|
||||||
|
}
|
||||||
|
return "F";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getIdentifier(ItemStack stack) {
|
||||||
|
return getStringIdentifier(stack);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private static MockedStatic<NEIClientConfig> config;
|
||||||
|
private static Language language;
|
||||||
|
private static ItemStack itemA;
|
||||||
|
private static ItemStack itemB;
|
||||||
|
private static ItemStack itemC;
|
||||||
|
private static ItemStack itemAB;
|
||||||
|
private static ItemStack itemABC;
|
||||||
|
private static ItemStack itemQuestionMark;
|
||||||
|
|
||||||
|
private static List<Arguments> combinationsProvider(ModeValue[] spaceModes, ModeValue[] patternModes) {
|
||||||
|
List<Arguments> combinations = new ArrayList<>();
|
||||||
|
for (ModeValue s : spaceModes) {
|
||||||
|
for (ModeValue p : patternModes) {
|
||||||
|
combinations
|
||||||
|
.add(Arguments.arguments(named(s.getName(), s.getValue()), named(p.getName(), p.getValue())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return combinations;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final List<Arguments> allModesTestProvider = combinationsProvider(spaceModes, patternModes);
|
||||||
|
|
||||||
|
private static final List<Arguments> extendedPlusAllSpaceModesTestProvider = combinationsProvider(
|
||||||
|
spaceModes,
|
||||||
|
extendedPlusPatternMode);
|
||||||
|
|
||||||
|
private static final List<Arguments> spaceIsSpaceAllModesTestProvider = combinationsProvider(
|
||||||
|
spaceIsSpaceMode,
|
||||||
|
patternModes);
|
||||||
|
|
||||||
|
private static final List<Arguments> spaceIsAndAllModesTestProvider = combinationsProvider(
|
||||||
|
spaceIsAndMode,
|
||||||
|
patternModes);
|
||||||
|
|
||||||
|
private static final List<Arguments> extendedAllSpaceModesTestProvider = combinationsProvider(
|
||||||
|
spaceModes,
|
||||||
|
extendedPatternModes);
|
||||||
|
|
||||||
|
private static final List<Arguments> spaceIsAndExtendedPlusModeTestProvider = combinationsProvider(
|
||||||
|
spaceIsAndMode,
|
||||||
|
extendedPlusPatternMode);
|
||||||
|
|
||||||
|
@BeforeAll
|
||||||
|
public static void initItems() {
|
||||||
|
itemA = mock(ItemStack.class);
|
||||||
|
when(itemA.getDisplayName()).thenReturn("a");
|
||||||
|
itemB = mock(ItemStack.class);
|
||||||
|
when(itemB.getDisplayName()).thenReturn("b");
|
||||||
|
itemC = mock(ItemStack.class);
|
||||||
|
when(itemC.getDisplayName()).thenReturn("c");
|
||||||
|
itemAB = mock(ItemStack.class);
|
||||||
|
when(itemAB.getDisplayName()).thenReturn("a b");
|
||||||
|
itemABC = mock(ItemStack.class);
|
||||||
|
when(itemABC.getDisplayName()).thenReturn("a b c");
|
||||||
|
itemQuestionMark = mock(ItemStack.class);
|
||||||
|
when(itemQuestionMark.getDisplayName()).thenReturn("?");
|
||||||
|
|
||||||
|
MockedStatic<Minecraft> staticMinecraft = mockStatic(Minecraft.class);
|
||||||
|
Minecraft minecraft = mock(Minecraft.class);
|
||||||
|
staticMinecraft.when(() -> Minecraft.getMinecraft()).thenReturn(minecraft);
|
||||||
|
LanguageManager languageManager = mock(LanguageManager.class);
|
||||||
|
when(minecraft.getLanguageManager()).thenReturn(languageManager);
|
||||||
|
language = mock(Language.class);
|
||||||
|
when(languageManager.getCurrentLanguage()).thenReturn(language);
|
||||||
|
when(language.getLanguageCode()).thenReturn("en");
|
||||||
|
config = mockStatic(NEIClientConfig.class);
|
||||||
|
config.when(() -> NEIClientConfig.getBooleanSetting(eq("inventory.search.logSearchExceptions")))
|
||||||
|
.thenReturn(false);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// All modes tests
|
||||||
|
|
||||||
|
@DisplayName("No providers")
|
||||||
|
@ParameterizedTest(name = "{argumentsWithNames}")
|
||||||
|
@FieldSource("allModesTestProvider")
|
||||||
|
public void testWithoutProviders(int spaceMode, int patternMode) {
|
||||||
|
SearchTokenParser searchParser = new SearchTokenParser();
|
||||||
|
Set<ItemStack> matchedItems = testFilterAgainstAllItems(spaceMode, patternMode, searchParser, "test");
|
||||||
|
|
||||||
|
assertEquals(0, matchedItems.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@DisplayName("Or")
|
||||||
|
@ParameterizedTest(name = "{argumentsWithNames}")
|
||||||
|
@FieldSource("allModesTestProvider")
|
||||||
|
public void testOr(int spaceMode, int patternMode) {
|
||||||
|
SearchTokenParser searchParser = new SearchTokenParser();
|
||||||
|
searchParser.addProvider(defaultParserProvider);
|
||||||
|
Set<ItemStack> matchedItems = testFilterAgainstAllItems(
|
||||||
|
spaceMode,
|
||||||
|
patternMode,
|
||||||
|
searchParser,
|
||||||
|
itemA.getDisplayName() + "|" + itemC.getDisplayName());
|
||||||
|
assertEquals(4, matchedItems.size());
|
||||||
|
assertTrue(matchedItems.contains(itemA));
|
||||||
|
assertTrue(matchedItems.contains(itemC));
|
||||||
|
assertTrue(matchedItems.contains(itemAB));
|
||||||
|
assertTrue(matchedItems.contains(itemABC));
|
||||||
|
}
|
||||||
|
|
||||||
|
@DisplayName("Negate")
|
||||||
|
@ParameterizedTest(name = "{argumentsWithNames}")
|
||||||
|
@FieldSource("allModesTestProvider")
|
||||||
|
public void testNegate(int spaceMode, int patternMode) {
|
||||||
|
SearchTokenParser searchParser = new SearchTokenParser();
|
||||||
|
searchParser.addProvider(defaultParserProvider);
|
||||||
|
Set<ItemStack> matchedItems = testFilterAgainstAllItems(
|
||||||
|
spaceMode,
|
||||||
|
patternMode,
|
||||||
|
searchParser,
|
||||||
|
"-" + itemA.getDisplayName());
|
||||||
|
|
||||||
|
assertEquals(3, matchedItems.size());
|
||||||
|
assertTrue(matchedItems.contains(itemB));
|
||||||
|
assertTrue(matchedItems.contains(itemC));
|
||||||
|
assertTrue(matchedItems.contains(itemQuestionMark));
|
||||||
|
}
|
||||||
|
|
||||||
|
@DisplayName("Custom prefix provider")
|
||||||
|
@ParameterizedTest(name = "{argumentsWithNames}")
|
||||||
|
@FieldSource("allModesTestProvider")
|
||||||
|
public void testCustomPrefixProvider(int spaceMode, int patternMode) {
|
||||||
|
SearchTokenParser searchParser = new SearchTokenParser();
|
||||||
|
searchParser.addProvider(defaultParserProvider);
|
||||||
|
searchParser
|
||||||
|
.addProvider(parserProvider('?', "custom", SearchTokenParser.SearchMode.PREFIX, customCreateFilter));
|
||||||
|
|
||||||
|
Set<ItemStack> matchedItems = testFilterAgainstAllItems(spaceMode, patternMode, searchParser, "?A");
|
||||||
|
|
||||||
|
assertEquals(1, matchedItems.size());
|
||||||
|
assertTrue(matchedItems.contains(itemA));
|
||||||
|
}
|
||||||
|
|
||||||
|
@DisplayName("Space mode")
|
||||||
|
@ParameterizedTest(name = "{argumentsWithNames}")
|
||||||
|
@FieldSource("spaceIsSpaceAllModesTestProvider")
|
||||||
|
public void testSpaceMode(int spaceMode, int patternMode) {
|
||||||
|
SearchTokenParser searchParser = new SearchTokenParser();
|
||||||
|
searchParser.addProvider(defaultParserProvider);
|
||||||
|
Set<ItemStack> matchedItems = testFilterAgainstAllItems(
|
||||||
|
spaceMode,
|
||||||
|
patternMode,
|
||||||
|
searchParser,
|
||||||
|
itemAB.getDisplayName());
|
||||||
|
assertEquals(2, matchedItems.size());
|
||||||
|
assertTrue(matchedItems.contains(itemAB));
|
||||||
|
assertTrue(matchedItems.contains(itemABC));
|
||||||
|
}
|
||||||
|
|
||||||
|
@DisplayName("Spaced expression")
|
||||||
|
@ParameterizedTest(name = "{argumentsWithNames}")
|
||||||
|
@FieldSource("spaceIsAndAllModesTestProvider")
|
||||||
|
public void testSpacedExpression(int spaceMode, int patternMode) {
|
||||||
|
SearchTokenParser searchParser = new SearchTokenParser();
|
||||||
|
searchParser.addProvider(defaultParserProvider);
|
||||||
|
Set<ItemStack> matchedItems = testFilterAgainstAllItems(
|
||||||
|
spaceMode,
|
||||||
|
patternMode,
|
||||||
|
searchParser,
|
||||||
|
itemA.getDisplayName() + " " + itemC.getDisplayName());
|
||||||
|
assertEquals(1, matchedItems.size());
|
||||||
|
assertTrue(matchedItems.contains(itemABC));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extended modes tests
|
||||||
|
|
||||||
|
@DisplayName("Regex")
|
||||||
|
@ParameterizedTest(name = "{argumentsWithNames}")
|
||||||
|
@FieldSource("extendedAllSpaceModesTestProvider")
|
||||||
|
public void testRegex(int spaceMode, int patternMode) {
|
||||||
|
SearchTokenParser searchParser = new SearchTokenParser();
|
||||||
|
searchParser.addProvider(defaultParserProvider);
|
||||||
|
Set<ItemStack> matchedItems = testFilterAgainstAllItems(
|
||||||
|
spaceMode,
|
||||||
|
patternMode,
|
||||||
|
searchParser,
|
||||||
|
"r/z?\\s" + itemB.getDisplayName() + "/");
|
||||||
|
|
||||||
|
assertEquals(2, matchedItems.size());
|
||||||
|
assertTrue(matchedItems.contains(itemAB));
|
||||||
|
assertTrue(matchedItems.contains(itemABC));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extended+ specific tests
|
||||||
|
|
||||||
|
@DisplayName("Sequence")
|
||||||
|
@ParameterizedTest(name = "{argumentsWithNames}")
|
||||||
|
@FieldSource("extendedPlusAllSpaceModesTestProvider")
|
||||||
|
public void testSequence(int spaceMode, int patternMode) {
|
||||||
|
SearchTokenParser searchParser = new SearchTokenParser();
|
||||||
|
searchParser.addProvider(defaultParserProvider);
|
||||||
|
searchParser
|
||||||
|
.addProvider(parserProvider('?', "custom", SearchTokenParser.SearchMode.PREFIX, customCreateFilter));
|
||||||
|
|
||||||
|
Set<ItemStack> matchedItems = testFilterAgainstAllItems(
|
||||||
|
spaceMode,
|
||||||
|
patternMode,
|
||||||
|
searchParser,
|
||||||
|
"\"" + itemA.getDisplayName() + "\"?A");
|
||||||
|
|
||||||
|
assertEquals(1, matchedItems.size());
|
||||||
|
assertTrue(matchedItems.contains(itemA));
|
||||||
|
}
|
||||||
|
|
||||||
|
@DisplayName("Brackets")
|
||||||
|
@ParameterizedTest(name = "{argumentsWithNames}")
|
||||||
|
@FieldSource("extendedPlusAllSpaceModesTestProvider")
|
||||||
|
public void testBrackets(int spaceMode, int patternMode) {
|
||||||
|
SearchTokenParser searchParser = new SearchTokenParser();
|
||||||
|
searchParser.addProvider(defaultParserProvider);
|
||||||
|
Set<ItemStack> matchedItems = testFilterAgainstAllItems(
|
||||||
|
spaceMode,
|
||||||
|
patternMode,
|
||||||
|
searchParser,
|
||||||
|
"{" + itemA.getDisplayName() + "|" + itemC.getDisplayName() + "}" + itemB.getDisplayName());
|
||||||
|
|
||||||
|
assertEquals(2, matchedItems.size());
|
||||||
|
assertTrue(matchedItems.contains(itemAB));
|
||||||
|
assertTrue(matchedItems.contains(itemABC));
|
||||||
|
}
|
||||||
|
|
||||||
|
@DisplayName("Quoted")
|
||||||
|
@ParameterizedTest(name = "{argumentsWithNames}")
|
||||||
|
@FieldSource("extendedPlusAllSpaceModesTestProvider")
|
||||||
|
public void testQuoted(int spaceMode, int patternMode) {
|
||||||
|
SearchTokenParser searchParser = new SearchTokenParser();
|
||||||
|
searchParser.addProvider(defaultParserProvider);
|
||||||
|
Set<ItemStack> matchedItems = testFilterAgainstAllItems(
|
||||||
|
spaceMode,
|
||||||
|
patternMode,
|
||||||
|
searchParser,
|
||||||
|
"\"" + itemB.getDisplayName() + " " + itemC.getDisplayName());
|
||||||
|
|
||||||
|
assertEquals(1, matchedItems.size());
|
||||||
|
assertTrue(matchedItems.contains(itemABC));
|
||||||
|
}
|
||||||
|
|
||||||
|
@DisplayName("Escaped symbols")
|
||||||
|
@ParameterizedTest(name = "{argumentsWithNames}")
|
||||||
|
@FieldSource("spaceIsAndExtendedPlusModeTestProvider")
|
||||||
|
public void testEscapedSymbols(int spaceMode, int patternMode) {
|
||||||
|
SearchTokenParser searchParser = new SearchTokenParser();
|
||||||
|
searchParser.addProvider(defaultParserProvider);
|
||||||
|
searchParser
|
||||||
|
.addProvider(parserProvider('?', "custom", SearchTokenParser.SearchMode.PREFIX, customCreateFilter));
|
||||||
|
Set<ItemStack> matchedItems = testFilterAgainstAllItems(
|
||||||
|
spaceMode,
|
||||||
|
patternMode,
|
||||||
|
searchParser,
|
||||||
|
'\\' + itemQuestionMark.getDisplayName());
|
||||||
|
|
||||||
|
assertEquals(1, matchedItems.size());
|
||||||
|
assertTrue(matchedItems.contains(itemQuestionMark));
|
||||||
|
|
||||||
|
matchedItems = testFilterAgainstAllItems(spaceMode, patternMode, searchParser, '\\' + " ");
|
||||||
|
|
||||||
|
assertEquals(2, matchedItems.size());
|
||||||
|
assertTrue(matchedItems.contains(itemAB));
|
||||||
|
assertTrue(matchedItems.contains(itemABC));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Set<ItemStack> testFilterAgainstAllItems(int spaceMode, int patternMode,
|
||||||
|
SearchTokenParser searchParser, String search) {
|
||||||
|
config.when(() -> NEIClientConfig.getIntSetting(eq("inventory.search.patternMode"))).thenReturn(patternMode);
|
||||||
|
config.when(() -> NEIClientConfig.getIntSetting(eq("inventory.search.spaceMode"))).thenReturn(spaceMode);
|
||||||
|
|
||||||
|
ItemFilter filter = searchParser.getFilter(search);
|
||||||
|
if (filter instanceof SearchTokenParser.IsRegisteredItemFilter) {
|
||||||
|
filter = ((SearchTokenParser.IsRegisteredItemFilter) filter).filter;
|
||||||
|
}
|
||||||
|
Set<ItemStack> matchedItems = Stream.of(itemA, itemB, itemC, itemAB, itemABC, itemQuestionMark)
|
||||||
|
.filter(filter::matches).collect(Collectors.toSet());
|
||||||
|
return matchedItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static SearchParserProvider parserProvider(char prefix, String name,
|
||||||
|
SearchTokenParser.SearchMode searchMode, Function<Pattern, ItemFilter> createFilter) {
|
||||||
|
return new SearchParserProvider(prefix, name, EnumChatFormatting.RESET, createFilter) {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SearchTokenParser.SearchMode getSearchMode() {
|
||||||
|
return searchMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Language> getMatchingLanguages() {
|
||||||
|
List<Language> languages = new ArrayList<>();
|
||||||
|
languages.add(language);
|
||||||
|
return languages;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String printResult(Set<ItemStack> items) {
|
||||||
|
return items.stream().map(ItemStack::getDisplayName).collect(Collectors.joining(","));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String printFilterContents(ItemFilter filter) {
|
||||||
|
if (filter instanceof AnyMultiItemFilter) {
|
||||||
|
return "ANY: ("
|
||||||
|
+ ((AnyMultiItemFilter) filter).filters.stream().map(SearchTokenParserTest::printFilterContents)
|
||||||
|
.collect(Collectors.joining(","))
|
||||||
|
+ ")";
|
||||||
|
}
|
||||||
|
if (filter instanceof AllMultiItemFilter) {
|
||||||
|
return "ALL: ("
|
||||||
|
+ ((AllMultiItemFilter) filter).filters.stream().map(SearchTokenParserTest::printFilterContents)
|
||||||
|
.collect(Collectors.joining(","))
|
||||||
|
+ ")";
|
||||||
|
}
|
||||||
|
if (filter instanceof PatternItemFilter) {
|
||||||
|
return "pattern(" + ((PatternItemFilter) filter).pattern + ")";
|
||||||
|
}
|
||||||
|
return filter.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user