package net.minecraft.command.parser; import java.util.ArrayDeque; import net.minecraft.command.ICommandSender; import net.minecraft.command.ParsingUtilities; import net.minecraft.command.SyntaxErrorException; import net.minecraft.command.arg.ArgWrapper; import net.minecraft.command.collections.MetaColl; import net.minecraft.command.completion.TCDSet; import net.minecraft.command.parser.CompletionParser.ResParserState.CompletionModifier; import net.minecraft.command.type.ICachedParse; import net.minecraft.command.type.metadata.ICompletable; import net.minecraft.command.type.metadata.ICompletable.CompletionCallback; import net.minecraft.command.type.metadata.MetaEntry.PrimitiveHint; import net.minecraft.command.type.metadata.MetaID; import net.minecraft.command.type.metadata.MetaProvider; import net.minecraft.util.BlockPos; public class CompletionParser extends Parser { public static class CompletionData { public final String toMatch; public final String lowerToMatch; public final int cursorIndex; public final ICommandSender sender; public final BlockPos hovered; public CompletionData(final String toMatch, final int cursorIndex, final ICommandSender sender, final BlockPos hovered) { this.toMatch = toMatch; this.lowerToMatch = toMatch.toLowerCase(); this.cursorIndex = cursorIndex; this.sender = sender; this.hovered = hovered; } } private final CompletionData cData; private boolean terminateCompletion = false; private final TCDSet tcDataSet = new TCDSet(); private final ArrayDeque<CompletionCallback> completers = new ArrayDeque<>(); private final ArrayDeque<CompletionCallback> proposed = new ArrayDeque<>(); public CompletionParser(final String toParse, final int startIndex, final CompletionData cData) { super(toParse, startIndex, false); // The Completion-version of the pattern tricks the parser into thinking that the end is not yet reached, thus calling the subparsers for completions this.getMatcher(ParsingUtilities.endingMatcherCompletion); this.cData = cData; this.suppressEx = true; } public CompletionParser(final String toParse, final CompletionData cData) { super(toParse, 0); this.cData = cData; this.suppressEx = true; } public TCDSet getTCDSet() { return this.tcDataSet; } public static final MetaID<PrimitiveHint> hintID = new MetaID<>(MetaColl.typeHint); public static final PrimitiveHint propose = new PrimitiveHint(hintID); public static final PrimitiveHint terminate = new PrimitiveHint(hintID); @Override public <D> void supplyHint(final MetaProvider<D> hint, final D data) { final PrimitiveHint eHint = hint.getData(hintID, this, data); if (eHint == null) return; if (eHint == propose) this.proposeCompletion(); else this.terminateCompletion(); } protected void terminateCompletion() { this.terminateCompletion = true; this.completers.clear(); this.proposed.clear(); } protected void proposeCompletion() { if (this.completers.isEmpty()) return; this.proposed.push(this.completers.pop()); this.completers.push(NULL); } @Override public <D> boolean pushMetadata(final MetaProvider<D> data, final D parserData) { final CompletionCallback completer = data.getData(ICompletable.metaID, this, parserData); if (completer == null) return false; this.completers.push(completer); return true; } @Override public void popMetadata(final MetaProvider<?> data) { if (!data.canProvide(ICompletable.metaID) || this.completers.isEmpty()) return; this.complete(false); } protected void complete(final boolean forceCompletion) { final CompletionCallback completer = this.completers.pop(); if (completer == NULL) this.proposed.pop().complete(this.tcDataSet, this, this.cData); else if (forceCompletion || this.getIndex() == this.cData.cursorIndex) completer.complete(this.tcDataSet, this, this.cData); } protected void complete(final boolean forceCompletion, final int endCount) { for (int i = CompletionParser.this.completers.size(); i > endCount; --i) this.complete(forceCompletion); } protected static abstract class ResParserState extends Parser.IResParserState<ResParserState> { public final CompletionModifier modifier; public ResParserState( final int index, final Context defContext, final IVersionManager<ResParserState, ?> versionManager, final CompletionModifier modifier) { super(index, defContext, versionManager); this.modifier = modifier; } public static enum CompletionModifier { none, terminate, propose; } protected static class Success extends ResParserState { private final ArgWrapper<?> res; public Success(final int index, final Context defContext, final IVersionManager<ResParserState, ?> versionManager, final CompletionModifier modifier, final ArgWrapper<?> res) { super(index, defContext, versionManager, modifier); this.res = res; } @Override public ArgWrapper<?> res() throws SyntaxErrorException { return this.res; } } protected static class Error extends ResParserState { private final SyntaxErrorException ex; public Error(final int index, final Context defContext, final IVersionManager<ResParserState, ?> versionManager, final CompletionModifier modifier, final SyntaxErrorException ex) { super(index, defContext, versionManager, modifier); this.ex = ex; } @Override public ArgWrapper<?> res() throws SyntaxErrorException { throw this.ex; } } } protected static class SnapshotState extends ISnapshotState<ResParserState> { public final int completersCount; public final boolean terminateCompletionTriggered; public SnapshotState(final int index, final Context defContext, final IVersionManager<ResParserState, ?> versionManager, final int completersCount, final boolean terminateCompletionTriggered) { super(index, defContext, versionManager); this.completersCount = completersCount; this.terminateCompletionTriggered = terminateCompletionTriggered; } } @Override protected IVersionManager<?, ?> newVersionManager() { return new VersionManager(); } private static final CompletionCallback markerCompleter = new CompletionCallback() { @Override public void complete(final TCDSet tcDataSet, final Parser parser, final CompletionData cData) { } }; private static final CompletionCallback NULL = new CompletionCallback() { @Override public void complete(final TCDSet tcDataSet, final Parser parser, final CompletionData cData) { } }; private class VersionManager extends IVersionManager<ResParserState, SnapshotState> { @Override public void setState(final ResParserState state) { super.setState(state); switch (state.modifier) { case terminate: CompletionParser.this.terminateCompletion(); return; case propose: CompletionParser.this.proposeCompletion(); case none: } } @Override protected ResParserState parseFetchState(final ICachedParse target, final Context context) throws SyntaxErrorException { final boolean useMarker = CompletionParser.this.completers.isEmpty() || CompletionParser.this.completers.peek() == NULL; if (useMarker) CompletionParser.this.completers.push(markerCompleter); final boolean terminateCompletion = CompletionParser.this.terminateCompletion; CompletionParser.this.terminateCompletion = false; try { final ArgWrapper<?> res = target.iCachedParse(CompletionParser.this, context); final CompletionModifier modifier = CompletionParser.this.terminateCompletion ? CompletionModifier.terminate : (useMarker ? CompletionParser.this.completers.pop() : CompletionParser.this.completers.peek()) == NULL ? CompletionModifier.propose : CompletionModifier.none; if (modifier == CompletionModifier.propose && useMarker) { CompletionParser.this.proposed.pop(); CompletionParser.this.proposeCompletion(); } return new ResParserState.Success(CompletionParser.this.getIndex(), CompletionParser.this.defContext, this, modifier, res); } catch (final SyntaxErrorException ex) { final CompletionModifier modifier = CompletionParser.this.terminateCompletion ? CompletionModifier.terminate : CompletionModifier.none; return new ResParserState.Error(CompletionParser.this.getIndex(), CompletionParser.this.defContext, this, modifier, ex); } finally { CompletionParser.this.terminateCompletion |= terminateCompletion; } } @Override protected SnapshotState saveSnapshot() { final boolean terminateCompletion = CompletionParser.this.terminateCompletion; CompletionParser.this.terminateCompletion = false; return new SnapshotState( CompletionParser.this.getIndex(), CompletionParser.this.defContext, this, CompletionParser.this.completers.size(), terminateCompletion); } @Override protected void restoreSnapshot(final SnapshotState state) { super.restoreSnapshot(state); final int endCount = CompletionParser.this.terminateCompletion ? 0 : state.completersCount; CompletionParser.this.complete(true, endCount); } @Override protected void finalizeSnapshot(final SnapshotState state) { CompletionParser.this.terminateCompletion |= state.terminateCompletionTriggered; } } }