예제 #1
0
            /// <summary>
            /// Updates the currently stored 'best result' if the current result is better.
            /// Returns 'true' if no further work is required and we can break early, or
            /// 'false' if we need to keep on going.
            ///
            /// If 'weight' is better than 'bestWeight' and matchSpanToAdd is not null, then
            /// matchSpanToAdd will be added to matchedSpansInReverse.
            /// </summary>
            private bool UpdateBestResultIfBetter(
                CamelCaseResult result, ref CamelCaseResult?bestResult, TextSpan?matchSpanToAdd)
            {
                if (matchSpanToAdd != null)
                {
                    result = result.WithAddedMatchedSpan(matchSpanToAdd.Value);
                }

                if (!IsBetter(result, bestResult))
                {
                    // Even though we matched this current candidate hump we failed to match
                    // the remainder of the pattern.  Continue to the next candidate hump
                    // to see if our pattern character will match it and potentially succeed.
                    result.Free();

                    // We need to keep going.
                    return(false);
                }

                // This was result was better than whatever previous best result we had was.
                // Free and overwrite the existing best results, and keep going.
                bestResult?.Free();
                bestResult = result;

                // We found a path that allowed us to match everything contiguously
                // from the beginning.  This is the best match possible.  So we can
                // just break out now and return this result.
                return(GetKind(result) == PatternMatchKind.CamelCaseExact);
            }
예제 #2
0
        private static PatternMatchKind GetCamelCaseKind(CamelCaseResult result, ArrayBuilder <TextSpan> candidateHumps)
        {
            var toEnd = result.MatchCount == candidateHumps.Count;

            if (result.FromStart)
            {
                if (result.Contiguous)
                {
                    // We contiguously matched humps from the start of this candidate.  If we
                    // matched all the humps, then this was an exact match, otherwise it was a
                    // contiguous prefix match
                    return(toEnd
                        ? PatternMatchKind.CamelCaseExact
                        : PatternMatchKind.CamelCasePrefix);
                }
                else
                {
                    return(PatternMatchKind.CamelCaseNonContiguousPrefix);
                }
            }
            else
            {
                // We didn't match from the start.  Distinguish between a match whose humps are all
                // contiguous, and one that isn't.
                return(result.Contiguous
                    ? PatternMatchKind.CamelCaseSubstring
                    : PatternMatchKind.CamelCaseNonContiguousSubstring);
            }
        }
예제 #3
0
            private bool IsBetter(CamelCaseResult result, CamelCaseResult?currentBestResult)
            {
                if (currentBestResult == null)
                {
                    // We have no current best.  So this result is the best.
                    return(true);
                }

                return(GetKind(result) < GetKind(currentBestResult.Value));
            }
예제 #4
0
        private static PatternMatchKind GetCamelCaseKind(CamelCaseResult result)
        {
            /* CamelCase PatternMatchKind truth table:
             * | FromStart | ToEnd | Contiguous || PatternMatchKind                |
             * | True      | True  | True       || CamelCaseExact                  |
             * | True      | True  | False      || CamelCaseNonContiguousPrefix    |
             * | True      | False | True       || CamelCasePrefix                 |
             * | True      | False | False      || CamelCaseNonContiguousPrefix    |
             * | False     | True  | True       || CamelCaseSubstring              |
             * | False     | True  | False      || CamelCaseNonContiguousSubstring |
             * | False     | False | True       || CamelCaseSubstring              |
             * | False     | False | False      || CamelCaseNonContiguousSubstring |
             */

            if (result.FromStart)
            {
                if (result.Contiguous)
                {
                    // We contiguously matched humps from the start of this candidate.  If we
                    // matched all the humps, then this was an exact match, otherwise it was a
                    // contiguous prefix match
                    return(result.ToEnd
                        ? PatternMatchKind.CamelCaseExact
                        : PatternMatchKind.CamelCasePrefix);
                }
                else
                {
                    return(PatternMatchKind.CamelCaseNonContiguousPrefix);
                }
            }
            else
            {
                // We didn't match from the start.  Distinguish between a match whose humps are all
                // contiguous, and one that isn't.
                return(result.Contiguous
                    ? PatternMatchKind.CamelCaseSubstring
                    : PatternMatchKind.CamelCaseNonContiguousSubstring);
            }
        }
예제 #5
0
 private PatternMatchKind GetKind(CamelCaseResult result)
 => PatternMatcher.GetCamelCaseKind(result, _candidateHumps);
예제 #6
0
        private PatternMatchKind?TryUpperCaseCamelCaseMatch(
            string candidate,
            ArrayBuilder <TextSpan> candidateHumps,
            TextChunk patternChunk,
            CompareOptions compareOption,
            out ImmutableArray <TextSpan> matchedSpans)
        {
            var patternHumps = patternChunk.PatternHumps;

            // Note: we may have more pattern parts than candidate parts.  This is because multiple
            // pattern parts may match a candidate part.  For example "SiUI" against "SimpleUI".
            // We'll have 3 pattern parts Si/U/I against two candidate parts Simple/UI.  However, U
            // and I will both match in UI.

            var  currentCandidateHump = 0;
            var  currentPatternHump   = 0;
            int? firstMatch           = null;
            bool?contiguous           = null;

            var patternHumpCount   = patternHumps.Count;
            var candidateHumpCount = candidateHumps.Count;

            using var _ = ArrayBuilder <TextSpan> .GetInstance(out var matchSpans);

            while (true)
            {
                // Let's consider our termination cases
                if (currentPatternHump == patternHumpCount)
                {
                    Debug.Assert(firstMatch.HasValue);
                    Debug.Assert(contiguous.HasValue);

                    var matchCount = matchSpans.Count;
                    matchedSpans = _includeMatchedSpans
                        ? new NormalizedTextSpanCollection(matchSpans).ToImmutableArray()
                        : ImmutableArray <TextSpan> .Empty;

                    var camelCaseResult = new CamelCaseResult(firstMatch == 0, contiguous.Value, matchCount, null);
                    return(GetCamelCaseKind(camelCaseResult, candidateHumps));
                }
                else if (currentCandidateHump == candidateHumpCount)
                {
                    // No match, since we still have more of the pattern to hit
                    matchedSpans = ImmutableArray <TextSpan> .Empty;
                    return(null);
                }

                var candidateHump            = candidateHumps[currentCandidateHump];
                var gotOneMatchThisCandidate = false;

                // Consider the case of matching SiUI against SimpleUIElement. The candidate parts
                // will be Simple/UI/Element, and the pattern parts will be Si/U/I.  We'll match 'Si'
                // against 'Simple' first.  Then we'll match 'U' against 'UI'. However, we want to
                // still keep matching pattern parts against that candidate part.
                for (; currentPatternHump < patternHumpCount; currentPatternHump++)
                {
                    var patternChunkCharacterSpan = patternHumps[currentPatternHump];

                    if (gotOneMatchThisCandidate)
                    {
                        // We've already gotten one pattern part match in this candidate.  We will
                        // only continue trying to consume pattern parts if the last part and this
                        // part are both upper case.
                        if (!char.IsUpper(patternChunk.Text[patternHumps[currentPatternHump - 1].Start]) ||
                            !char.IsUpper(patternChunk.Text[patternHumps[currentPatternHump].Start]))
                        {
                            break;
                        }
                    }

                    if (!PartStartsWith(candidate, candidateHump, patternChunk.Text, patternChunkCharacterSpan, compareOption))
                    {
                        break;
                    }

                    matchSpans.Add(new TextSpan(candidateHump.Start, patternChunkCharacterSpan.Length));
                    gotOneMatchThisCandidate = true;

                    firstMatch ??= currentCandidateHump;

                    // If we were contiguous, then keep that value.  If we weren't, then keep that
                    // value.  If we don't know, then set the value to 'true' as an initial match is
                    // obviously contiguous.
                    contiguous ??= true;

                    candidateHump = new TextSpan(candidateHump.Start + patternChunkCharacterSpan.Length, candidateHump.Length - patternChunkCharacterSpan.Length);
                }

                // Check if we matched anything at all.  If we didn't, then we need to unset the
                // contiguous bit if we currently had it set.
                // If we haven't set the bit yet, then that means we haven't matched anything so
                // far, and we don't want to change that.
                if (!gotOneMatchThisCandidate && contiguous.HasValue)
                {
                    contiguous = false;
                }

                // Move onto the next candidate.
                currentCandidateHump++;
            }
        }
예제 #7
0
 private static PatternMatchKind GetKind(CamelCaseResult result)
 => PatternMatcher.GetCamelCaseKind(result);