|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Text;
using Microsoft.TemplateEngine.Core.Contracts;
namespace Microsoft.TemplateEngine.Core.Operations
{
public class Region : IOperationProvider
{
public static readonly string OperationName = "region";
private readonly ITokenConfig _end;
private readonly bool _include;
private readonly ITokenConfig _start;
private readonly bool _toggle;
private readonly bool _wholeLine;
private readonly bool _trimWhitespace;
private readonly bool _initialState;
public Region(ITokenConfig start, ITokenConfig end, bool include, bool wholeLine, bool trimWhitespace, string? id, bool initialState)
{
_wholeLine = wholeLine;
_trimWhitespace = trimWhitespace;
_start = start;
_end = end;
_include = include;
_toggle = _start.Equals(_end);
Id = id;
_initialState = initialState;
}
public string? Id { get; }
public IOperation GetOperation(Encoding encoding, IProcessorState processorState)
{
IToken startToken = _start.ToToken(encoding);
IToken endToken = _end.ToToken(encoding);
return new Implementation(this, startToken, endToken, _include, _toggle, Id, _initialState);
}
private class Implementation : IOperation
{
private readonly IToken _endToken;
private readonly bool _includeRegion;
private readonly bool _startAndEndAreSame;
private readonly Region _definition;
private bool _waitingForEnd;
public Implementation(Region owner, IToken startToken, IToken endToken, bool include, bool toggle, string? id, bool initialState)
{
_definition = owner;
_endToken = endToken;
_includeRegion = include;
_startAndEndAreSame = toggle;
Tokens = toggle ? new[] { startToken } : new[] { startToken, endToken };
Id = id;
IsInitialStateOn = string.IsNullOrEmpty(id) || initialState;
}
public IReadOnlyList<IToken> Tokens { get; }
public string? Id { get; }
public bool IsInitialStateOn { get; }
public int HandleMatch(IProcessorState processor, int bufferLength, ref int currentBufferPosition, int token)
{
if (processor.Config.Flags.TryGetValue(Region.OperationName, out bool flag) && !flag)
{
processor.WriteToTarget(Tokens[token].Value, Tokens[token].Start, Tokens[token].Length);
return Tokens[token].Length;
}
processor.WhitespaceHandler(ref bufferLength, ref currentBufferPosition, wholeLine: _definition._wholeLine, trim: _definition._trimWhitespace);
if (_startAndEndAreSame)
{
token = _waitingForEnd ? 1 : 0;
}
//If we're resuming from a region that has been included (we've found the end now)
// just process the end
if (_waitingForEnd && token == 1)
{
_waitingForEnd = false;
return 0;
}
if (token != 0)
{
return 0;
}
//If we're including the region, set that we're waiting for the end and return
// control to the processor
if (_includeRegion)
{
_waitingForEnd = true;
return 0;
}
//If we've made it here, we're skipping stuff, skip all the way to the end of the
// end token
int i = currentBufferPosition;
int j = 0;
for (; j < _endToken.Length; ++j)
{
if (i + j == bufferLength)
{
processor.AdvanceBuffer(i + j);
bufferLength = processor.CurrentBufferLength;
i = -j;
}
//TODO: This should be using one of the tries rather than looking for the byte run directly
if (processor.CurrentBuffer[i + j] != _endToken.Value[j])
{
++i;
j = -1;
}
}
i += j;
processor.WhitespaceHandler(ref bufferLength, ref i, wholeLine: _definition._wholeLine, trim: _definition._trimWhitespace);
currentBufferPosition = i;
return 0;
}
}
}
}
|