src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts/PredictionTimeline/beforeServerInput
class PredictionTimeline:	beforeServerInput(input: string): string {
		const originalInput = input;
		if (this._inputBuffer) {
			input = this._inputBuffer + input;
			this._inputBuffer = undefined;
		}

		if (!this._expected.length) {
			this._clearPredictionState();
			return input;
		}

		const buffer = this._getActiveBuffer();
		if (!buffer) {
			this._clearPredictionState();
			return input;
		}

		let output = '';

		const reader = new StringReader(input);
		const startingGen = this._expected[0].gen;
		const emitPredictionOmitted = () => {
			const omit = reader.eatRe(PREDICTION_OMIT_RE);
			if (omit) {
				output += omit[0];
			}
		};

		ReadLoop: while (this._expected.length && reader.remaining > 0) {
			emitPredictionOmitted();

			const { p: prediction, gen } = this._expected[0];
			const cursor = this.physicalCursor(buffer);
			const beforeTestReaderIndex = reader.index;
			switch (prediction.matches(reader, this._lookBehind)) {
				case MatchResult.Success: {
					// if the input character matches what the next prediction expected, undo
					// the prediction and write the real character out.
					const eaten = input.slice(beforeTestReaderIndex, reader.index);
					if (gen === startingGen) {
						output += prediction.rollForwards?.(cursor, eaten);
					} else {
						prediction.apply(buffer, this.physicalCursor(buffer)); // move cursor for additional apply
						output += eaten;
					}

					this._succeededEmitter.fire(prediction);
					this._lookBehind = prediction;
					this._expected.shift();
					break;
				}
				case MatchResult.Buffer:
					// on a buffer, store the remaining data and completely read data
					// to be output as normal.
					this._inputBuffer = input.slice(beforeTestReaderIndex);
					reader.index = input.length;
					break ReadLoop;
				case MatchResult.Failure: {
					// on a failure, roll back all remaining items in this generation
					// and clear predictions, since they are no longer valid
					const rollback = this._expected.filter(p => p.gen === startingGen).reverse();
					output += rollback.map(({ p }) => p.rollback(this.physicalCursor(buffer))).join('');
					if (rollback.some(r => r.p.affectsStyle)) {
						// reading the current style should generally be safe, since predictions
						// always restore the style if they modify it.
						output += attributesToSeq(core(this.terminal)._inputHandler._curAttrData);
					}
					this._clearPredictionState();
					this._failedEmitter.fire(prediction);
					break ReadLoop;
				}
			}
		}

		emitPredictionOmitted();

		// Extra data (like the result of running a command) should cause us to
		// reset the cursor
		if (!reader.eof) {
			output += reader.rest;
			this._clearPredictionState();
		}

		// If we passed a generation boundary, apply the current generation's predictions
		if (this._expected.length && startingGen !== this._expected[0].gen) {
			for (const { p, gen } of this._expected) {
				if (gen !== this._expected[0].gen) {
					break;
				}
				if (p.affectsStyle) {
					this._style.expectIncomingStyle();
				}

				output += p.apply(buffer, this.physicalCursor(buffer));
			}
		}

		if (!this._showPredictions) {
			return originalInput;
		}

		if (output.length === 0 || output === input) {
			return output;
		}

		if (this._physicalCursor) {
			output += this._physicalCursor.moveInstruction();
		}

		// prevent cursor flickering while typing
		output = HIDE_CURSOR + output + SHOW_CURSOR;

		return output;
	}