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