Esempio n. 1
0
            private PatternMatch?TryMatch(string candidate, bool fuzzyMatch)
            {
                if (fuzzyMatch && !_allowFuzzyMatching)
                {
                    return(null);
                }

                var containerParts      = candidate.Split(_containerSplitCharacters, StringSplitOptions.RemoveEmptyEntries);
                var patternSegmentCount = _patternSegments.Length;
                var containerPartCount  = containerParts.Length;

                if (patternSegmentCount > containerPartCount)
                {
                    // There weren't enough container parts to match against the pattern parts.
                    // So this definitely doesn't match.
                    return(null);
                }

                // So far so good.  Now break up the container for the candidate and check if all
                // the dotted parts match up correctly.

                PatternMatch?match = null, result = null;

                for (int i = patternSegmentCount - 1, j = containerPartCount - 1;
                     i >= 0;
                     i--, j--)
                {
                    var segment       = _patternSegments[i];
                    var containerName = containerParts[j];

                    // Add up the lengths of all the container parts before this one, as well is the split characters that were removed.
                    int containerOffset = j;
                    for (int k = 0; k < j; k++)
                    {
                        containerOffset += containerParts[k].Length;
                    }

                    result = MatchPatternSegment(containerName, segment, fuzzyMatch, containerOffset);
                    if (!result.HasValue)
                    {
                        // This container didn't match the pattern piece.  So there's no match at all.
                        return(null);
                    }

                    match = match?.Merge(result.Value, PatternMatchMergeStrategy.Container) ?? result;
                }

                return(match);
            }
Esempio n. 2
0
        /// <summary>
        /// Internal helper for MatchPatternInternal
        /// </summary>
        /// <remarks>
        /// PERF: Designed to minimize allocations in common cases.
        /// If there's no match, then null is returned.
        /// If there's a single match, or the caller only wants the first match, then it is returned (as a Nullable)
        /// If there are multiple matches they are merged.
        /// </remarks>
        /// <param name="candidate">The word being tested.</param>
        /// <param name="segment">The segment of the pattern to check against the candidate.</param>
        /// <param name="fuzzyMatch">If a fuzzy match should be performed</param>
        /// <param name="spanOffset">Index of segment[0] in candidate, used to merge matches together.</param>
        /// <returns>Returns a match if found. Otherwise it is null.</returns>
        private PatternMatch?MatchPatternSegment(
            string candidate,
            PatternSegment segment,
            bool fuzzyMatch,
            int segmentOffset)
        {
            if (fuzzyMatch && !_allowFuzzyMatching)
            {
                return(null);
            }

            // First check if the segment matches as is.  This is also useful if the segment contains
            // characters we would normally strip when splitting into parts that we also may want to
            // match in the candidate.  For example if the segment is "@int" and the candidate is
            // "@int", then that will show up as an exact match here.
            //
            // Note: if the segment contains a space or an asterisk then we must assume that it's a
            // multi-word segment.
            PatternMatch?match = null;

            if (!ContainsSpaceOrAsterisk(segment.TotalTextChunk.Text))
            {
                match = MatchPatternChunk(
                    candidate, segment.TotalTextChunk, punctuationStripped: false, fuzzyMatch: fuzzyMatch, chunkOffset: segmentOffset);
                if (match != null)
                {
                    return(match);
                }
            }

            // The logic for pattern matching is now as follows:
            //
            // 1) Break the segment passed in into words.  Breaking is rather simple and a
            //    good way to think about it that if gives you all the individual alphanumeric words
            //    of the pattern.
            //
            // 2) For each word try to match the word against the candidate value.
            //
            // 3) Matching is as follows:
            //
            //   a) Check if the word matches the candidate entirely, in an case insensitive or
            //    sensitive manner.  If it does, return that there was an exact match.
            //
            //   b) Check if the word is a prefix of the candidate, in a case insensitive or
            //      sensitive manner.  If it does, return that there was a prefix match.
            //
            //   c) If the word is entirely lowercase, then check if it is contained anywhere in the
            //      candidate in a case insensitive manner.  If so, return that there was a substring
            //      match.
            //
            //      Note: We only have a substring match if the lowercase part is prefix match of
            //      some word part. That way we don't match something like 'Class' when the user
            //      types 'a'. But we would match 'FooAttribute' (since 'Attribute' starts with
            //      'a').
            //
            //   d) If the word was not entirely lowercase, then check if it is contained in the
            //      candidate in a case *sensitive* manner. If so, return that there was a substring
            //      match.
            //
            //   e) If the word was entirely lowercase, then attempt a special lower cased camel cased
            //      match.  i.e. cofipro would match CodeFixProvider.
            //
            //   f) If the word was not entirely lowercase, then attempt a normal camel cased match.
            //      i.e. CoFiPro would match CodeFixProvider, but CofiPro would not.
            //
            //   g) The word is all lower case. Is it a case insensitive substring of the candidate starting
            //      on a part boundary of the candidate?
            //
            // 4) Merge matches back together using the Simple strategy.
            //
            // Only if all words have some sort of match is the pattern considered matched.

            int chunkOffset       = segmentOffset;
            var subWordTextChunks = segment.SubWordTextChunks;

            foreach (var subWordTextChunk in subWordTextChunks)
            {
                // Try to match the candidate with this word
                var result = MatchPatternChunk(
                    candidate, subWordTextChunk, punctuationStripped: true, fuzzyMatch: fuzzyMatch, chunkOffset: chunkOffset);
                if (result == null)
                {
                    return(null);
                }

                match = match?.Merge(result.Value, PatternMatchMergeStrategy.Simple) ?? result.Value;
            }

            return(match);
        }