001/*
002 * This file is part of Baritone.
003 *
004 * Baritone is free software: you can redistribute it and/or modify
005 * it under the terms of the GNU Lesser General Public License as published by
006 * the Free Software Foundation, either version 3 of the License, or
007 * (at your option) any later version.
008 *
009 * Baritone is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
012 * GNU Lesser General Public License for more details.
013 *
014 * You should have received a copy of the GNU Lesser General Public License
015 * along with Baritone.  If not, see <https://www.gnu.org/licenses/>.
016 */
017
018package baritone.api.command.helpers;
019
020import baritone.api.BaritoneAPI;
021import baritone.api.Settings;
022import baritone.api.command.argument.IArgConsumer;
023import baritone.api.command.manager.ICommandManager;
024import baritone.api.event.events.TabCompleteEvent;
025import baritone.api.utils.SettingsUtil;
026import java.util.Comparator;
027import java.util.List;
028import java.util.Locale;
029import java.util.function.Function;
030import java.util.function.Predicate;
031import java.util.stream.Stream;
032import net.minecraft.resources.ResourceLocation;
033
034/**
035 * The {@link TabCompleteHelper} is a <b>single-use</b> object that helps you handle tab completion. It includes helper
036 * methods for appending and prepending streams, sorting, filtering by prefix, and so on.
037 * <p>
038 * The recommended way to use this class is:
039 * <ul>
040 * <li>Create a new instance with the empty constructor</li>
041 * <li>Use {@code append}, {@code prepend} or {@code add<something>} methods to add completions</li>
042 * <li>Sort using {@link #sort(Comparator)} or {@link #sortAlphabetically()} and then filter by prefix using
043 * {@link #filterPrefix(String)}</li>
044 * <li>Get the stream using {@link #stream()}</li>
045 * <li>Pass it up to whatever's calling your tab complete function (i.e.
046 * {@link ICommandManager#tabComplete(String)} or {@link IArgConsumer}#tabCompleteDatatype(IDatatype)})</li>
047 * </ul>
048 * <p>
049 * For advanced users: if you're intercepting {@link TabCompleteEvent}s directly, use {@link #build()} instead for an
050 * array.
051 */
052public class TabCompleteHelper {
053
054    private Stream<String> stream;
055
056    public TabCompleteHelper(String[] base) {
057        stream = Stream.of(base);
058    }
059
060    public TabCompleteHelper(List<String> base) {
061        stream = base.stream();
062    }
063
064    public TabCompleteHelper() {
065        stream = Stream.empty();
066    }
067
068    /**
069     * Appends the specified stream to this {@link TabCompleteHelper} and returns it for chaining
070     *
071     * @param source The stream to append
072     * @return This {@link TabCompleteHelper} after having appended the stream
073     * @see #append(String...)
074     * @see #append(Class)
075     */
076    public TabCompleteHelper append(Stream<String> source) {
077        stream = Stream.concat(stream, source);
078        return this;
079    }
080
081    /**
082     * Appends the specified strings to this {@link TabCompleteHelper} and returns it for chaining
083     *
084     * @param source The stream to append
085     * @return This {@link TabCompleteHelper} after having appended the strings
086     * @see #append(Stream)
087     * @see #append(Class)
088     */
089    public TabCompleteHelper append(String... source) {
090        return append(Stream.of(source));
091    }
092
093    /**
094     * Appends all values of the specified enum to this {@link TabCompleteHelper} and returns it for chaining
095     *
096     * @param num The enum to append the values of
097     * @return This {@link TabCompleteHelper} after having appended the values
098     * @see #append(Stream)
099     * @see #append(String...)
100     */
101    public TabCompleteHelper append(Class<? extends Enum<?>> num) {
102        return append(
103                Stream.of(num.getEnumConstants())
104                        .map(Enum::name)
105                        .map(String::toLowerCase)
106        );
107    }
108
109    /**
110     * Prepends the specified stream to this {@link TabCompleteHelper} and returns it for chaining
111     *
112     * @param source The stream to prepend
113     * @return This {@link TabCompleteHelper} after having prepended the stream
114     * @see #prepend(String...)
115     * @see #prepend(Class)
116     */
117    public TabCompleteHelper prepend(Stream<String> source) {
118        stream = Stream.concat(source, stream);
119        return this;
120    }
121
122    /**
123     * Prepends the specified strings to this {@link TabCompleteHelper} and returns it for chaining
124     *
125     * @param source The stream to prepend
126     * @return This {@link TabCompleteHelper} after having prepended the strings
127     * @see #prepend(Stream)
128     * @see #prepend(Class)
129     */
130    public TabCompleteHelper prepend(String... source) {
131        return prepend(Stream.of(source));
132    }
133
134    /**
135     * Prepends all values of the specified enum to this {@link TabCompleteHelper} and returns it for chaining
136     *
137     * @param num The enum to prepend the values of
138     * @return This {@link TabCompleteHelper} after having prepended the values
139     * @see #prepend(Stream)
140     * @see #prepend(String...)
141     */
142    public TabCompleteHelper prepend(Class<? extends Enum<?>> num) {
143        return prepend(
144                Stream.of(num.getEnumConstants())
145                        .map(Enum::name)
146                        .map(String::toLowerCase)
147        );
148    }
149
150    /**
151     * Apply the specified {@code transform} to every element <b>currently</b> in this {@link TabCompleteHelper} and
152     * return this object for chaining
153     *
154     * @param transform The transform to apply
155     * @return This {@link TabCompleteHelper}
156     */
157    public TabCompleteHelper map(Function<String, String> transform) {
158        stream = stream.map(transform);
159        return this;
160    }
161
162    /**
163     * Apply the specified {@code filter} to every element <b>currently</b> in this {@link TabCompleteHelper} and return
164     * this object for chaining
165     *
166     * @param filter The filter to apply
167     * @return This {@link TabCompleteHelper}
168     */
169    public TabCompleteHelper filter(Predicate<String> filter) {
170        stream = stream.filter(filter);
171        return this;
172    }
173
174    /**
175     * Apply the specified {@code sort} to every element <b>currently</b> in this {@link TabCompleteHelper} and return
176     * this object for chaining
177     *
178     * @param comparator The comparator to use
179     * @return This {@link TabCompleteHelper}
180     */
181    public TabCompleteHelper sort(Comparator<String> comparator) {
182        stream = stream.sorted(comparator);
183        return this;
184    }
185
186    /**
187     * Sort every element <b>currently</b> in this {@link TabCompleteHelper} alphabetically and return this object for
188     * chaining
189     *
190     * @return This {@link TabCompleteHelper}
191     */
192    public TabCompleteHelper sortAlphabetically() {
193        return sort(String.CASE_INSENSITIVE_ORDER);
194    }
195
196    /**
197     * Filter out any element that doesn't start with {@code prefix} and return this object for chaining
198     *
199     * @param prefix The prefix to filter for
200     * @return This {@link TabCompleteHelper}
201     */
202    public TabCompleteHelper filterPrefix(String prefix) {
203        return filter(x -> x.toLowerCase(Locale.US).startsWith(prefix.toLowerCase(Locale.US)));
204    }
205
206    /**
207     * Filter out any element that doesn't start with {@code prefix} and return this object for chaining
208     * <p>
209     * Assumes every element in this {@link TabCompleteHelper} is a {@link ResourceLocation}
210     *
211     * @param prefix The prefix to filter for
212     * @return This {@link TabCompleteHelper}
213     */
214    public TabCompleteHelper filterPrefixNamespaced(String prefix) {
215        ResourceLocation loc = ResourceLocation.tryParse(prefix);
216        if (loc == null) {
217            stream = Stream.empty();
218            return this;
219        }
220        return filterPrefix(loc.toString());
221    }
222
223    /**
224     * @return An array containing every element in this {@link TabCompleteHelper}
225     * @see #stream()
226     */
227    public String[] build() {
228        return stream.toArray(String[]::new);
229    }
230
231    /**
232     * @return A stream containing every element in this {@link TabCompleteHelper}
233     * @see #build()
234     */
235    public Stream<String> stream() {
236        return stream;
237    }
238
239    /**
240     * Appends every command in the specified {@link ICommandManager} to this {@link TabCompleteHelper}
241     *
242     * @param manager A command manager
243     * @return This {@link TabCompleteHelper}
244     */
245    public TabCompleteHelper addCommands(ICommandManager manager) {
246        return append(manager.getRegistry().descendingStream()
247                .flatMap(command -> command.getNames().stream())
248                .distinct()
249        );
250    }
251
252    /**
253     * Appends every setting in the {@link Settings} to this {@link TabCompleteHelper}
254     *
255     * @return This {@link TabCompleteHelper}
256     */
257    public TabCompleteHelper addSettings() {
258        return append(
259                BaritoneAPI.getSettings().allSettings.stream()
260                        .filter(s -> !s.isJavaOnly())
261                        .map(Settings.Setting::getName)
262                        .sorted(String.CASE_INSENSITIVE_ORDER)
263        );
264    }
265
266    /**
267     * Appends every modified setting in the {@link Settings} to this {@link TabCompleteHelper}
268     *
269     * @return This {@link TabCompleteHelper}
270     */
271    public TabCompleteHelper addModifiedSettings() {
272        return append(
273                SettingsUtil.modifiedSettings(BaritoneAPI.getSettings()).stream()
274                        .map(Settings.Setting::getName)
275                        .sorted(String.CASE_INSENSITIVE_ORDER)
276        );
277    }
278
279    /**
280     * Appends every {@link Boolean} setting in the {@link Settings} to this {@link TabCompleteHelper}
281     *
282     * @return This {@link TabCompleteHelper}
283     */
284    public TabCompleteHelper addToggleableSettings() {
285        return append(
286                BaritoneAPI.getSettings().getAllValuesByType(Boolean.class).stream()
287                        .map(Settings.Setting::getName)
288                        .sorted(String.CASE_INSENSITIVE_ORDER)
289        );
290    }
291}