Beispiel #1
0
        static public void CanTrimEnd()
        {
            StringSlice firstString = "Trim Me \r";
            StringSlice trimmed     = firstString.TrimEnd();

            Assert.AreEqual("Trim Me", trimmed.ToString());
        }
Beispiel #2
0
        public void It_Can_Render_LiteralTags()
        {
            StringSlice content  = new StringSlice("I'm a literal tag");
            var         settings = new RendererSettingsBuilder().BuildSettings();

            var context = new Context(
                new { },
                settings,
                settings.RenderSettings);

            var stringRenderer   = new StringRender(StreamWriter, settings.RendererPipeline);
            var rawTokenRenderer = new LiteralTokenRenderer();

            rawTokenRenderer.Write(
                stringRenderer,
                new LiteralToken()
            {
                Content = new[] { content }
            }, context);

            StreamWriter.Flush();
            MemStream.Position = 0;
            var sr    = new StreamReader(MemStream);
            var myStr = sr.ReadToEnd();

            Assert.Equal(content.ToString(), myStr);
        }
Beispiel #3
0
        static public void CanTrimBoth()
        {
            StringSlice firstString = "\n \tTrim Me\t\n";
            StringSlice trimmed     = firstString.Trim();

            Assert.AreEqual("Trim Me", trimmed.ToString());
        }
Beispiel #4
0
        private bool TryParseCheckCompoundPatternIntoCompoundPatterns(string parameterText, List <PatternEntry> entries)
        {
            var parameters = parameterText.SliceOnTabOrSpace();

            if (parameters.Length == 0)
            {
                Builder.LogWarning("Failed to parse compound pattern from: " + parameterText);
                return(false);
            }

            var         pattern    = parameters[0];
            StringSlice pattern2   = StringSlice.Null;
            StringSlice pattern3   = StringSlice.Null;
            var         condition  = default(FlagValue);
            var         condition2 = default(FlagValue);

            var slashIndex = pattern.IndexOf('/');

            if (slashIndex >= 0)
            {
                if (!TryParseFlag(pattern.Subslice(slashIndex + 1), out condition))
                {
                    Builder.LogWarning($"Failed to parse compound pattern 1 {pattern} from: {parameterText}");
                    return(false);
                }

                pattern = pattern.Subslice(0, slashIndex);
            }

            if (parameters.Length >= 2)
            {
                pattern2   = parameters[1];
                slashIndex = pattern2.IndexOf('/');
                if (slashIndex >= 0)
                {
                    if (!TryParseFlag(pattern2.Subslice(slashIndex + 1), out condition2))
                    {
                        Builder.LogWarning($"Failed to parse compound pattern 2 {pattern2} from: {parameterText}");
                        return(false);
                    }

                    pattern2 = pattern2.Subslice(0, slashIndex);
                }

                if (parameters.Length >= 3)
                {
                    pattern3 = parameters[2];
                    Builder.EnableOptions(AffixConfigOptions.SimplifiedCompound);
                }
            }

            entries.Add(new PatternEntry(
                            Builder.Dedup(pattern.ToString()),
                            Builder.Dedup(pattern2.ToString()),
                            Builder.Dedup(pattern3.ToString()),
                            condition,
                            condition2));

            return(true);
        }
Beispiel #5
0
            // re-use string keys using lru cache
            string StringFromCache(StringSlice slice)
            {
                int length             = _stringCache.Length;
                int lowestCounter      = int.MaxValue;
                int lowestCounterIndex = 0;

                for (int i = 0; i < length; ++i)
                {
                    if (slice.Equals(_stringCache[i].key))
                    {
                        _stringCache[i].counter = ++_stringCacheCounter;
                        return(_stringCache[i].result);
                    }

                    var counter = _stringCache[i].counter;
                    if (counter < lowestCounter)
                    {
                        lowestCounter      = counter;
                        lowestCounterIndex = i;
                    }
                }

                string result = slice.ToString();

                _stringCache[lowestCounterIndex].key     = slice;
                _stringCache[lowestCounterIndex].result  = result;
                _stringCache[lowestCounterIndex].counter = ++_stringCacheCounter;
                return(result);
            }
Beispiel #6
0
        /// <summary>
        /// Parses the given string into a TagString.
        /// </summary>
        public void Parse(ref TagString outTarget, StringSlice inInput, object inContext = null)
        {
            if (outTarget == null)
            {
                outTarget = new TagString();
            }
            else
            {
                outTarget.Clear();
            }

            if (inInput.IsEmpty)
            {
                return;
            }

            m_RichBuilder.Length = 0;
            bool bModified;

            ProcessInput(inInput, outTarget, inContext, out bModified);
            if (bModified)
            {
                outTarget.RichText    = m_RichBuilder.Flush();
                outTarget.VisibleText = m_StrippedBuilder.Flush();
            }
            else
            {
                string originalString = inInput.ToString();
                outTarget.RichText       = outTarget.VisibleText = originalString;
                m_RichBuilder.Length     = 0;
                m_StrippedBuilder.Length = 0;
            }
        }
Beispiel #7
0
        protected bool ParseCodeOptions(
            BlockProcessor state,
            ref StringSlice line,
            IFencedBlock fenced,
            char openingCharacter)
        {
            if (!(fenced is AnnotatedCodeBlock annotatedBlock))
            {
                return(false);
            }

            var result = _codeFenceAnnotationsParser.TryParseCodeFenceOptions(
                line.ToString(),
                state.Context);

            switch (result)
            {
            case FailedCodeFenceOptionParseResult failed:
                foreach (var errorMessage in failed.ErrorMessages)
                {
                    annotatedBlock.Diagnostics.Add(errorMessage);
                }

                return(true);

            case SuccessfulCodeFenceOptionParseResult codeResult:
                annotatedBlock.Annotations = codeResult.Annotations;
                return(true);
            }

            return(false);
        }
Beispiel #8
0
        /// <summary>
        /// Returns the branch name for source control.
        /// </summary>
        static public string GetSourceControlBranchName()
        {
            try
            {
                if (Directory.Exists(".git"))
                {
                    string headData = File.ReadAllText(".git/HEAD");
                    foreach (var line in StringSlice.EnumeratedSplit(headData, new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries))
                    {
                        TagData tag = TagData.Parse(line, TagStringParser.RichTextDelimiters);
                        if (tag.Id == "ref")
                        {
                            StringSlice data = tag.Data;
                            if (data.StartsWith("refs/heads/"))
                            {
                                data = data.Substring(11);
                            }
                            return(data.ToString());
                        }
                    }
                }

                // TODO: More version control types? svn?

                return(null);
            }
            catch (Exception e)
            {
                Debug.LogException(e);
                return(null);
            }
        }
Beispiel #9
0
        public void Create_CreateFlexiSectionHeadingBlock()
        {
            // Arrange
            const int      dummyColumn         = 12;
            const int      dummyLineIndex      = 5;
            var            dummyLine           = new StringSlice("dummyLine", 4, 8);
            BlockProcessor dummyBlockProcessor = MarkdigTypesFactory.CreateBlockProcessor();

            dummyBlockProcessor.Line      = dummyLine;
            dummyBlockProcessor.LineIndex = dummyLineIndex;
            dummyBlockProcessor.Column    = dummyColumn;
            Mock <BlockParser> dummyBlockParser = _mockRepository.Create <BlockParser>();
            Mock <IFlexiSectionBlockOptions>       dummyFlexiSectionBlockOptions = _mockRepository.Create <IFlexiSectionBlockOptions>();
            Mock <FlexiSectionHeadingBlockFactory> mockTestSubject = CreateMockFlexiSectionHeadingBlockFactory();

            mockTestSubject.CallBase = true;
            mockTestSubject.Setup(t => t.SetupIDGenerationAndReferenceLinking(It.IsAny <FlexiSectionHeadingBlock>(),
                                                                              dummyFlexiSectionBlockOptions.Object,
                                                                              dummyBlockProcessor));

            // Act
            FlexiSectionHeadingBlock result = mockTestSubject.Object.Create(dummyBlockProcessor, dummyFlexiSectionBlockOptions.Object, dummyBlockParser.Object);

            // Assert
            _mockRepository.VerifyAll();
            Assert.Equal(dummyColumn, result.Column);
            Assert.Equal(dummyLine.Start, result.Span.Start);
            Assert.Equal(dummyLine.End, result.Span.End);
            Assert.Equal(dummyLineIndex, result.Line);
            Assert.Equal(dummyLine.ToString(), result.Lines.ToString());
        }
Beispiel #10
0
        protected bool ParseCodeOptions(
            BlockProcessor state,
            ref StringSlice line,
            IFencedBlock fenced)
        {
            if (!(fenced is AnnotatedCodeBlock codeLinkBlock))
            {
                return(false);
            }

            var result = codeFenceAnnotationsParser.TryParseCodeFenceOptions(line.ToString(),
                                                                             state.Context);

            switch (result)
            {
            case NoCodeFenceOptions _:
                return(false);

            case FailedCodeFenceOptionParseResult failed:
                foreach (var errorMessage in failed.ErrorMessages)
                {
                    codeLinkBlock.Diagnostics.Add(errorMessage);
                }

                break;

            case SuccessfulCodeFenceOptionParseResult successful:
                codeLinkBlock.Annotations = successful.Annotations;
                break;
            }

            return(true);
        }
Beispiel #11
0
        private bool PlantUmlInfoParser(BlockProcessor state, ref StringSlice line, IFencedBlock fenced, char openingCharacter)
        {
            string infoString;
            string argString = null;

            var c = line.CurrentChar;
            // An info string cannot contain any backsticks
            int firstSpace = -1;

            for (int i = line.Start; i <= line.End; i++)
            {
                c = line.Text[i];
                if (c == '`')
                {
                    return(false);
                }

                if (firstSpace < 0 && c.IsSpaceOrTab())
                {
                    firstSpace = i;
                }
            }

            if (firstSpace > 0)
            {
                infoString = line.Text.Substring(line.Start, firstSpace - line.Start).Trim();

                // Skip any spaces after info string
                firstSpace++;
                while (true)
                {
                    c = line[firstSpace];
                    if (c.IsSpaceOrTab())
                    {
                        firstSpace++;
                    }
                    else
                    {
                        break;
                    }
                }

                argString = line.Text.Substring(firstSpace, line.End - firstSpace + 1).Trim();
            }
            else
            {
                infoString = line.ToString().Trim();
            }

            if (infoString != "plantuml")
            {
                return(false);
            }

            fenced.Info      = HtmlHelper.Unescape(infoString);
            fenced.Arguments = HtmlHelper.Unescape(argString);

            return(true);
        }
Beispiel #12
0
        public void ComponentParserReadUntil()
        {
            TestComponentParser parser   = new TestComponentParser();
            StringSlice         slice    = new StringSlice("[!clientId:NameOfComponent(- or |)] # comment", 2, 45);
            StringSlice         clientId = parser.CallReadUntil(slice, ':');

            Expect.AreEqual("clientId", clientId.ToString());
        }
Beispiel #13
0
        public static string NextPart(ref string text)
        {
            StringSlice ssText   = StringSlice.Prepare(text);
            StringSlice ssResult = SliceNextPart(ref ssText);

            text = ssText.ToString();
            return(ssResult.ToString());
        }
Beispiel #14
0
 public static StringSlice Concat(StringSlice ssA, StringSlice ssB)
 {
     if (object.ReferenceEquals(ssA.str, ssB.str))
     {
         if (ssA.offset + ssA.length == ssB.offset)
         {
             return(StringSlice.Prepare(ssA.str, ssA.offset, ssA.length + ssB.length));
         }
     }
     return(StringSlice.Prepare(ssA.ToString() + ssB.ToString()));
 }
        static private void SetReflectedObject(UnityEngine.Object inRoot, string inPath, object inValue)
        {
            inPath = inPath.Replace("Array.data", "");

            // locate the parent object
            StringSlice path    = inPath;
            int         lastDot = path.LastIndexOf('.');

            if (lastDot < 0)
            {
                return;
            }

            StringSlice parentPath   = path.Substring(0, lastDot);
            StringSlice fieldName    = path.Substring(lastDot + 1);
            object      parentObject = LocateReflectedObject(inRoot, parentPath.ToString());

            if (parentObject == null)
            {
                return;
            }

            Type objType  = parentObject.GetType();
            int  arrayIdx = -1;

            // capture array
            if (fieldName.EndsWith(']'))
            {
                int         elementIndexStart = fieldName.IndexOf('[');
                int         length            = fieldName.Length - elementIndexStart - 1;
                StringSlice elementIndexSlice = fieldName.Substring(elementIndexStart + 1, length);
                arrayIdx  = Convert.ToInt32(elementIndexSlice.ToString());
                fieldName = fieldName.Substring(0, elementIndexStart);
            }

            FieldInfo field = objType.GetField(fieldName.ToString(), InstanceBindingFlags);

            if (field == null)
            {
                return;
            }

            if (arrayIdx >= 0)
            {
                IList list = field.GetValue(parentObject) as IList;
                if (list != null)
                {
                    list[arrayIdx] = inValue;
                }
                return;
            }

            field.SetValue(parentObject, inValue);
        }
Beispiel #16
0
        private static bool TryParseNumberFlag(StringSlice text, out FlagValue value)
        {
            if (!text.IsEmpty && IntEx.TryParseInvariant(text.ToString(), out int integerValue) && integerValue >= char.MinValue && integerValue <= char.MaxValue)
            {
                value = new FlagValue(unchecked ((char)integerValue));
                return(true);
            }

            value = default(FlagValue);
            return(false);
        }
        public void Collapse_CollapsesLeadingWhitespace(string dummyLine, float dummyCollapseLength, string expectedResult)
        {
            // Arrange
            var testSubject      = new LeadingWhitespaceEditorService();
            var dummyStringSlice = new StringSlice(dummyLine);

            // Act
            testSubject.Collapse(ref dummyStringSlice, dummyCollapseLength);

            // Assert
            Assert.Equal(expectedResult, dummyStringSlice.ToString());
        }
        public void Indent_IndentsStringSlice(string dummyLine, int dummyIndentLength, string expectedResult)
        {
            // Arrange
            var testSubject      = new LeadingWhitespaceEditorService();
            var dummyStringSlice = new StringSlice(dummyLine);

            // Act
            StringSlice result = testSubject.Indent(dummyStringSlice, dummyIndentLength);

            // Assert
            Assert.Equal(expectedResult, result.ToString());
        }
        public void Dedent_DedentsLeadingWhitespace(string dummyLine, int dummyDedentLength, string expectedResult)
        {
            // Arrange
            var testSubject      = new LeadingWhitespaceEditorService();
            var dummyStringSlice = new StringSlice(dummyLine);

            // Act
            testSubject.Dedent(ref dummyStringSlice, dummyDedentLength);

            // Assert
            Assert.Equal(expectedResult, dummyStringSlice.ToString());
        }
        public override bool Match(InlineProcessor processor, ref StringSlice slice)
        {
            var  _isMatch = false;
            int  _start, _end;
            var  _url = "";
            char _current;
            var  _fullSlice = slice.ToString().ToLower().TrimStart(OpeningCharacters);

            if (!slice.PeekCharExtra(-1).IsWhiteSpaceOrZero())
            {
                return(_isMatch);
            }

            if (_configuration.BaseUrls.Where(u => _fullSlice.StartsWith(u)).Count() == 0)
            {
                return(_isMatch);
            }

            _start   = slice.Start;
            _end     = _start;
            _current = slice.NextChar();

            while (_current != ']')
            {
                _url    += _current;
                _end     = slice.Start;
                _current = slice.NextChar();
            }

            if (_current == ']')
            {
                slice.NextChar();

                var _inlineStart = processor.GetSourcePosition(slice.Start, out int _line, out int _column);

                processor.Inline = new EmbeddedGist
                {
                    Span =
                    {
                        Start = _inlineStart,
                        End   = _inlineStart + (_end - _start) + 1
                    },
                    Line   = _line,
                    Column = _column,
                    Url    = _url
                };

                _isMatch = true;
            }

            return(_isMatch);
        }
Beispiel #21
0
        public override string ToString()
        {
            if (Id.IsEmpty)
            {
                return(Data.ToString());
            }
            if (Data.IsEmpty)
            {
                return(Id.ToString());
            }

            return(string.Format("{0}: {1}", Id, Data));
        }
        /// <summary>
        /// Returns the parent property.
        /// </summary>
        static public SerializedProperty FindPropertyParent(this SerializedProperty inProperty)
        {
            StringSlice path    = inProperty.propertyPath.Replace("Array.data", "");
            int         lastDot = path.LastIndexOf('.');

            if (lastDot < 0)
            {
                return(null);
            }

            StringSlice parentPath = path.Substring(0, lastDot);

            return(inProperty.serializedObject.FindProperty(parentPath.ToString()));
        }
Beispiel #23
0
        /// <summary>
        /// Tries to open a section tag using the slice
        /// </summary>
        /// <param name="processor">The processor</param>
        /// <param name="slice">The slice</param>
        /// <returns>The result of the match</returns>
        public override ParserState TryOpenBlock(Processor processor, ref StringSlice slice)
        {
            var tagStart = slice.Start - processor.CurrentTags.StartTag.Length;
            var index    = slice.Start;

            while (slice[index].IsWhitespace())
            {
                index++;
            }

            var match = slice[index];

            if (match == OpeningTagDelimiter)
            {
                slice.Start = index + 1; // Skip delimiter

                while (slice.CurrentChar.IsWhitespace())
                {
                    slice.NextChar();
                }

                var startIndex = slice.Start;

                // Take characters until closing tag
                while (!slice.IsEmpty && !slice.Match(processor.CurrentTags.EndTag))
                {
                    slice.NextChar();
                }

                var sectionName          = slice.ToString(startIndex, slice.Start).TrimEnd();
                var contentStartPosition = slice.Start + processor.CurrentTags.EndTag.Length;

                var sectionTag = new SectionToken
                {
                    SectionName          = sectionName,
                    StartPosition        = tagStart,
                    ContentStartPosition = contentStartPosition,
                    Parser   = this,
                    IsClosed = false
                };

                processor.CurrentToken = sectionTag;

                slice.Start = contentStartPosition;

                return(ParserState.Break);
            }

            return(ParserState.Continue);
        }
        /// <inheritdoc />
        public virtual StringSlice Indent(StringSlice line, int indentLength)
        {
            if (indentLength < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(indentLength), string.Format(Strings.ArgumentOutOfRangeException_Shared_ValueCannotBeNegative, indentLength));
            }

            if (indentLength == 0)
            {
                return(line);
            }

            // TODO difficult to avoid these allocations without changes to Markdig. While this implementation isn't efficient, it is very useful.
            return(new StringSlice(new string(' ', indentLength) + line.ToString()));
        }
Beispiel #25
0
        /// <summary>
        /// Try to create a block close tag
        /// </summary>
        /// <param name="processor">The processor</param>
        /// <param name="slice">the slice</param>
        /// <param name="token">the current block tag</param>
        /// <returns>If the close was successful</returns>
        public override bool TryClose(Processor processor, ref StringSlice slice, BlockToken token)
        {
            var sectionTag = (SectionToken)token;

            while (slice.CurrentChar.IsWhitespace())
            {
                slice.NextChar();
            }

            var blockStart = slice.Start - processor.CurrentTags.StartTag.Length;

            slice.Start = slice.Start + 1; // Skip the slash

            while (slice.CurrentChar.IsWhitespace())
            {
                slice.NextChar();
            }

            var startIndex = slice.Start;

            // Take characters until closing tag
            while (!slice.IsEmpty && !slice.Match(processor.CurrentTags.EndTag))
            {
                slice.NextChar();
            }

            var sectionName = slice.ToString(startIndex, slice.Start).TrimEnd();

            if (sectionTag.SectionName == sectionName)
            {
                var tagEnd = slice.Start + processor.CurrentTags.EndTag.Length;

                var endTag = new SectionEndToken
                {
                    SectionName        = sectionName,
                    EndPosition        = tagEnd,
                    ContentEndPosition = blockStart,
                    IsClosed           = true
                };

                processor.CurrentToken = endTag;

                slice.Start += processor.CurrentTags.EndTag.Length;
                return(true);
            }

            throw new StubbleException($"Cannot close Block '{sectionName}' at {blockStart}. There is already an unclosed Block '{sectionTag.SectionName}'");
        }
Beispiel #26
0
        internal CodeBlockInfo(FencedCodeBlock codeBlock, StringSlice slice)
        {
            Info           = codeBlock.Info ?? string.Empty;
            Arguments      = codeBlock.Arguments ?? string.Empty;
            Lines          = codeBlock.Lines;
            CodeBlockSlice = slice;
            Source         = CodeBlockSlice.Text;

            int contentStart = Lines.Lines[0].Position;
            var lastLine     = Lines.Last();
            int contentEnd   = lastLine.Position + lastLine.Slice.Length - 1;

            ContentSpan  = new SourceSpan(contentStart, contentEnd);
            ContentSlice = CodeBlockSlice.Reposition(ContentSpan);
            Content      = ContentSlice.ToString();
        }
        public override bool Match(InlineProcessor processor, ref StringSlice slice)
        {
            // [!clientId:NameOfComponent(- or |)] # comment
            // ```
            // init: function(component) {}
            // click: function(component) {}
            // mouseover: function(component){}
            // ... other event handlers etc ...
            // ```
            bool match              = false;
            int  start              = slice.Start;
            int  end                = slice.Start;
            char openSquareBracket  = slice.PeekCharExtra(-1);
            char shouldBeWhitespace = slice.PeekCharExtra(-2);

            if (shouldBeWhitespace.IsWhiteSpaceOrZero() && openSquareBracket == '[')
            {
                slice.NextChar();
                StringSlice clientId              = ReadUntil(slice, ':', out end);
                StringSlice nameOfComponent       = ReadUntil(slice, ']', out end);
                string      nameOfComponentString = nameOfComponent.ToString();
                //bool block = true;
                //if (nameOfComponentString.EndsWith("|"))
                //{
                //    nameOfComponentString = nameOfComponentString.Truncate(1);
                //    block = false;
                //}
                if (ComponentResolver.IsValidComponentName(nameOfComponentString, out LeafInline component))
                {
                    int inlineStart = processor.GetSourcePosition(slice.Start, out int line, out int column);
                    component.Span.Start = inlineStart;
                    component.Span.End   = inlineStart + (end - start) + 1;
                    processor.Inline     = component;

                    match = true;
                }
            }

            return(match);
        }
Beispiel #28
0
        static void AddTicketLink(InlineProcessor processor,
                                  StringSlice slice,
                                  int charactersConsumedInReference,
                                  Uri uri)
        {
            int line, column;

            var startPosition = slice.Start;
            var endPosition   = startPosition + charactersConsumedInReference - 1;
            var linkText      = new StringSlice(slice.Text, startPosition, endPosition);

            var link = new TicketLinkInline {
                Span =
                {
                    Start = processor.GetSourcePosition(startPosition, out line, out column),
                },
                Line       = line,
                Column     = column,
                Url        = uri.ToString(),
                IsClosed   = true,
                IsAutoLink = true,
                Title      = $"Navigate to {linkText.ToString()}",
            };

            link.Span.End = endPosition;
            link.UrlSpan  = link.Span;
            link.GetAttributes().AddClass(TicketLinkClass);

            var linkContent = new LiteralInline {
                Span     = link.Span,
                Line     = line,
                Column   = column,
                Content  = linkText,
                IsClosed = true,
            };

            link.AppendChild(linkContent);

            processor.Inline = link;
        }
Beispiel #29
0
 public static StringSlice Concat(StringSlice ssA, string sB)
 {
     return StringSlice.Prepare(ssA.ToString() + sB);
 }
Beispiel #30
0
 public static StringSlice Concat(StringSlice ssA, StringSlice ssB)
 {
     if (object.ReferenceEquals(ssA.str, ssB.str))
     {
         if (ssA.offset + ssA.length == ssB.offset)
         {
             return StringSlice.Prepare(ssA.str, ssA.offset, ssA.length + ssB.length);
         }
     }
     return StringSlice.Prepare(ssA.ToString() + ssB.ToString());
 }
Beispiel #31
0
 public readonly override string ToString()
 {
     return(Slice.ToString());
 }
        public override bool Match(Processor processor, ref StringSlice slice)
        {
            if (processor is null)
            {
                throw new System.ArgumentNullException(nameof(processor));
            }

            var tagStart = slice.Start - processor.CurrentTags.StartTag.Length;
            var index    = slice.Start;

            while (slice[index].IsWhitespace())
            {
                index++;
            }

            var nameStart = index;

            // Skip whitespace or until end tag
            while (!slice[index].IsWhitespace() && !slice.Match(processor.CurrentTags.EndTag, index - slice.Start))
            {
                index++;
            }

            var name = slice.ToString(nameStart, index);

            // Skip whitespace or until end tag
            while (slice[index].IsWhitespace() && !slice.Match(processor.CurrentTags.EndTag, index - slice.Start))
            {
                index++;
            }

            if (!_helperMap.TryGetValue(name, out var helperRef))
            {
                return(false);
            }

            int contentEnd;
            var argsList = ImmutableArray <HelperArgument> .Empty;

            if (helperRef.ArgumentTypes.Length > 1)
            {
                var argsStart = index;
                slice.Start = index;

                while (!slice.IsEmpty && !slice.Match(processor.CurrentTags.EndTag))
                {
                    slice.NextChar();
                }

                var args = new StringSlice(slice.Text, argsStart, slice.Start - 1);
                args.TrimEnd();
                contentEnd = args.End + 1;

                argsList = ParseArguments(new StringSlice(args.Text, args.Start, args.End));
            }
            else
            {
                while (!slice.IsEmpty && !slice.Match(processor.CurrentTags.EndTag))
                {
                    slice.NextChar();
                }

                contentEnd = slice.Start;
            }

            if (!slice.Match(processor.CurrentTags.EndTag))
            {
                throw new StubbleException($"Unclosed Tag at {slice.Start.ToString(CultureInfo.InvariantCulture)}");
            }

            var tag = new HelperToken
            {
                TagStartPosition     = tagStart,
                ContentStartPosition = nameStart,
                Name = name,
                Args = argsList,
                ContentEndPosition = contentEnd,
                TagEndPosition     = slice.Start + processor.CurrentTags.EndTag.Length,
                IsClosed           = true
            };

            slice.Start += processor.CurrentTags.EndTag.Length;

            processor.CurrentToken          = tag;
            processor.HasSeenNonSpaceOnLine = true;

            return(true);
        }