private List <string> CreateWords(StringBreaks wordBreaks, string name) { var result = new List <string>(wordBreaks.GetCount()); for (int i = 0, n = wordBreaks.GetCount(); i < n; i++) { var br = wordBreaks[i]; result.Add(name.Substring(br.Start, br.Length)); } return(result); }
private static Words GetLongestBackwardSubsequence(int length, StringBreaks breaks, string baseName) { var breakCount = breaks.GetCount(); var start = breakCount - length; return(GetWords(start, breakCount, breaks, baseName)); }
private static PatternMatchKind GetCamelCaseKind(CamelCaseResult result, StringBreaks candidateHumps) { var toEnd = result.MatchCount == candidateHumps.GetCount(); 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; } }
private static IList<string> PartListToSubstrings(string identifier, StringBreaks parts) { var result = new List<string>(); for (int i = 0, n = parts.GetCount(); i < n; i++) { var span = parts[i]; result.Add(identifier.Substring(span.Start, span.Length)); } return result; }
private static ImmutableArray <IEnumerable <string> > GetInterleavedPatterns(StringBreaks breaks, string baseName) { var result = ArrayBuilder <IEnumerable <string> > .GetInstance(); var breakCount = breaks.GetCount(); result.Add(GetWords(0, breakCount, breaks, baseName)); for (int length = breakCount - 1; length > 0; length--) { // going forward result.Add(GetLongestForwardSubsequence(length, breaks, baseName)); // going backward result.Add(GetLongestBackwardSubsequence(length, breaks, baseName)); } return(result.ToImmutable()); }
private CamelCaseResult?TryMatch( int patternIndex, int candidateHumpIndex, bool?contiguous) { if (patternIndex == _patternText.Length) { // We hit the end. So we were able to match against this candidate. // We are contiguous if our contiguous tracker was not set to false. var matchedSpansInReverse = _includeMatchedSpans ? ArrayBuilder <TextSpan> .GetInstance() : null; return(new CamelCaseResult( fromStart: false, contiguous: contiguous != false, matchCount: 0, matchedSpansInReverse: matchedSpansInReverse)); } var bestResult = default(CamelCaseResult?); // Look for a hump in the candidate that matches the current letter we're on. var patternCharacter = _patternText[patternIndex]; for (int humpIndex = candidateHumpIndex, n = _candidateHumps.GetCount(); humpIndex < n; humpIndex++) { // If we've been contiguous, but we jumped past a hump, then we're no longer contiguous. if (contiguous.HasValue && contiguous.Value) { contiguous = humpIndex == candidateHumpIndex; } var candidateHump = _candidateHumps[humpIndex]; if (char.ToLower(_candidate[candidateHump.Start]) == patternCharacter) { // Found a hump in the candidate string that matches the current pattern // character we're on. i.e. we matched the c in cofipro against the C in // CodeFixProvider. // // Now, for each subsequent character, we need to both try to consume it // as part of the current hump, or see if it should match the next hump. // // Note, if the candidate is something like CodeFixProvider and our pattern // is cofipro, and we've matched the 'f' against the 'F', then the max of // the pattern we'll want to consume is "fip" against "Fix". We don't want // consume parts of the pattern once we reach the next hump. // We matched something. If this was our first match, consider ourselves // contiguous. var localContiguous = contiguous == null ? true : contiguous.Value; var result = TryConsumePatternOrMatchNextHump( patternIndex, humpIndex, localContiguous); if (result == null) { continue; } if (UpdateBestResultIfBetter(result.Value, ref bestResult, matchSpanToAdd: null)) { // We found the best result so far. We can stop immediately. break; } } } return(bestResult); }
private PatternMatchKind?TryUpperCaseCamelCaseMatch( string candidate, StringBreaks candidateHumps, TextChunk patternChunk, CompareOptions compareOption, out ImmutableArray <TextSpan> matchedSpans) { var patternHumps = patternChunk.CharacterSpans; // 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. int currentCandidateHump = 0; int currentPatternHump = 0; int? firstMatch = null; bool?contiguous = null; var patternHumpCount = patternHumps.GetCount(); var candidateHumpCount = candidateHumps.GetCount(); var matchSpans = ArrayBuilder <TextSpan> .GetInstance(); while (true) { // Let's consider our termination cases if (currentPatternHump == patternHumpCount) { Contract.Requires(firstMatch.HasValue); Contract.Requires(contiguous.HasValue); var matchCount = matchSpans.Count; matchedSpans = _includeMatchedSpans ? new NormalizedTextSpanCollection(matchSpans).ToImmutableArray() : ImmutableArray <TextSpan> .Empty; matchSpans.Free(); 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; matchSpans.Free(); return(null); } var candidateHump = candidateHumps[currentCandidateHump]; bool 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 = 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 = 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++; } }