internal static async Task<IEnumerable<ValueTuple<DeclaredSymbolInfo, Document, IEnumerable<PatternMatch>>>> FindNavigableDeclaredSymbolInfos(Project project, string pattern, CancellationToken cancellationToken) { var patternMatcher = new PatternMatcher(pattern); var result = new List<ValueTuple<DeclaredSymbolInfo, Document, IEnumerable<PatternMatch>>>(); foreach (var document in project.Documents) { cancellationToken.ThrowIfCancellationRequested(); var declaredSymbolInfos = await document.GetDeclaredSymbolInfosAsync(cancellationToken).ConfigureAwait(false); foreach (var declaredSymbolInfo in declaredSymbolInfos) { cancellationToken.ThrowIfCancellationRequested(); var patternMatches = patternMatcher.GetMatches( GetSearchName(declaredSymbolInfo), declaredSymbolInfo.FullyQualifiedContainerName, includeMatchSpans: false); if (patternMatches != null) { result.Add(ValueTuple.Create(declaredSymbolInfo, document, patternMatches)); } } } return result; }
private static async Task<ImmutableArray<ValueTuple<DeclaredSymbolInfo, Document, IEnumerable<PatternMatch>>>> FindNavigableDeclaredSymbolInfos( Project project, Document searchDocument, string pattern, CancellationToken cancellationToken) { using (var patternMatcher = new PatternMatcher(pattern, allowFuzzyMatching: true)) { var result = ArrayBuilder<ValueTuple<DeclaredSymbolInfo, Document, IEnumerable<PatternMatch>>>.GetInstance(); foreach (var document in project.Documents) { if (searchDocument != null && document != searchDocument) { continue; } cancellationToken.ThrowIfCancellationRequested(); var declarationInfo = await document.GetDeclarationInfoAsync(cancellationToken).ConfigureAwait(false); foreach (var declaredSymbolInfo in declarationInfo.DeclaredSymbolInfos) { cancellationToken.ThrowIfCancellationRequested(); var patternMatches = patternMatcher.GetMatches( GetSearchName(declaredSymbolInfo), declaredSymbolInfo.FullyQualifiedContainerName, includeMatchSpans: false); if (patternMatches != null) { result.Add(ValueTuple.Create(declaredSymbolInfo, document, patternMatches)); } } } return result.ToImmutableAndFree(); } }
internal static async Task<IEnumerable<ValueTuple<DeclaredSymbolInfo, Document, IEnumerable<PatternMatch>>>> FindNavigableDeclaredSymbolInfos(Project project, string pattern, CancellationToken cancellationToken) { var generatedCodeRecognitionService = project.LanguageServices.WorkspaceServices.GetService<IGeneratedCodeRecognitionService>(); var patternMatcher = new PatternMatcher(pattern); var result = new List<ValueTuple<DeclaredSymbolInfo, Document, IEnumerable<PatternMatch>>>(); foreach (var document in project.Documents.Where(d => !generatedCodeRecognitionService?.IsGeneratedCode(d) ?? true)) { cancellationToken.ThrowIfCancellationRequested(); var declaredSymbolInfos = await document.GetDeclaredSymbolInfosAsync(cancellationToken).ConfigureAwait(false); foreach (var declaredSymbolInfo in declaredSymbolInfos) { cancellationToken.ThrowIfCancellationRequested(); var patternMatches = patternMatcher.GetMatches( GetSearchName(declaredSymbolInfo), declaredSymbolInfo.FullyQualifiedContainerName); if (patternMatches != null) { result.Add(ValueTuple.Create(declaredSymbolInfo, document, patternMatches)); } } } return result; }
private static IEnumerable<PatternMatch> TryGetDotSeparatedPatternMatches( PatternMatcher patternMatcher, string[] dotSeparatedPatternComponents, DeclaredSymbolInfo declaredSymbolInfo) { if (dotSeparatedPatternComponents == null || declaredSymbolInfo.FullyQualifiedContainerName == null || declaredSymbolInfo.Name == null) { return null; } // First, check that the last part of the dot separated pattern matches the name of the // declared symbol. If not, then there's no point in proceeding and doing the more // expensive work. var symbolNameMatch = patternMatcher.MatchPattern(GetSearchName(declaredSymbolInfo), dotSeparatedPatternComponents.Last()); if (symbolNameMatch == null) { return null; } // So far so good. Now break up the container for the symbol and check if all // the dotted parts match up correctly. var totalMatch = symbolNameMatch.ToList(); var containerParts = declaredSymbolInfo.FullyQualifiedContainerName .Split(DotArray, StringSplitOptions.RemoveEmptyEntries) .ToList(); // -1 because the last part was checked against hte name, and only the rest // of the parts are checked against the container. if (dotSeparatedPatternComponents.Length - 1 > containerParts.Count) { // There weren't enough container parts to match against the pattern parts. // So this definitely doesn't match. return null; } for (int i = dotSeparatedPatternComponents.Length - 2, j = containerParts.Count - 1; i >= 0; i--, j--) { var dotPattern = dotSeparatedPatternComponents[i]; var containerName = containerParts[j]; var containerMatch = patternMatcher.MatchPattern(containerName, dotPattern); if (containerMatch == null) { // This container didn't match the pattern piece. So there's no match at all. return null; } totalMatch.AddRange(containerMatch); } // Success, this symbol's full name matched against the dotted name the user was asking // about. return totalMatch; }
protected PatternMatcher GetPatternMatcher(string value) { lock (_gate) { PatternMatcher patternMatcher; if (!_patternMatcherMap.TryGetValue(value, out patternMatcher)) { patternMatcher = new PatternMatcher(value, verbatimIdentifierPrefixIsWordCharacter: true); _patternMatcherMap.Add(value, patternMatcher); } return patternMatcher; } }
public Searcher( Solution solution, IAsynchronousOperationListener asyncListener, ItemDisplayFactory displayFactory, INavigateToCallback callback, string searchPattern, CancellationToken cancellationToken) { _solution = solution; _displayFactory = displayFactory; _callback = callback; _searchPattern = searchPattern; _patternMatcher = new PatternMatcher(); _cancellationToken = cancellationToken; _progress = new ProgressTracker(callback.ReportProgress); _asyncListener = asyncListener; }
private static IEnumerable<PatternMatch> TryMatch( string pattern, string[] dotSeparatedPatternComponents, PatternMatcher patternMatcher, DeclaredSymbolInfo declaredSymbolInfo) { var matches1 = TryGetDotSeparatedPatternMatches(patternMatcher, dotSeparatedPatternComponents, declaredSymbolInfo); var matches2 = patternMatcher.MatchPattern(GetSearchName(declaredSymbolInfo), pattern); if (matches1 == null) { return matches2; } if (matches2 == null) { return matches1; } return matches1.Concat(matches2); }
internal static async Task<IEnumerable<ValueTuple<DeclaredSymbolInfo, Document, IEnumerable<PatternMatch>>>> FindNavigableDeclaredSymbolInfos(Project project, string pattern, CancellationToken cancellationToken) { var patternMatcher = new PatternMatcher(); var dotSeparatedPatternComponents = pattern.Contains(".") ? pattern.Split(DotArray, StringSplitOptions.RemoveEmptyEntries) : null; var result = new List<ValueTuple<DeclaredSymbolInfo, Document, IEnumerable<PatternMatch>>>(); foreach (var document in project.Documents) { cancellationToken.ThrowIfCancellationRequested(); var declaredSymbolInfos = await document.GetDeclaredSymbolInfosAsync(cancellationToken).ConfigureAwait(false); foreach (var declaredSymbolInfo in declaredSymbolInfos) { cancellationToken.ThrowIfCancellationRequested(); var patternMatches = TryMatch(pattern, dotSeparatedPatternComponents, patternMatcher, declaredSymbolInfo); if (patternMatches != null) { result.Add(ValueTuple.Create(declaredSymbolInfo, document, patternMatches)); } } } return result; }
private static IEnumerable<PatternMatch> TryMatchMultiWordPattern(string candidate, string pattern) { IList<TextSpan> expectedSpans; MarkupTestFile.GetSpans(candidate, out candidate, out expectedSpans); var matches = new PatternMatcher(pattern).GetMatches(candidate, includeMatchSpans: true); if (matches == null) { Assert.True(expectedSpans == null || expectedSpans.Count == 0); } else { var actualSpans = matches.SelectMany(m => m.MatchedSpans).OrderBy(s => s.Start).ToList(); Assert.Equal(expectedSpans, actualSpans); } return matches; }
private static PatternMatch? TryMatchSingleWordPattern(string candidate, string pattern) { IList<TextSpan> spans; MarkupTestFile.GetSpans(candidate, out candidate, out spans); var match = new PatternMatcher(pattern).MatchSingleWordPattern_ForTestingOnly(candidate); if (match == null) { Assert.True(spans == null || spans.Count == 0); } else { Assert.Equal(match.Value.MatchedSpans, spans); } return match; }
private PatternMatcher GetFallbackPatternMatcher(string value) { lock (_gate) { PatternMatcher patternMatcher; if (!_fallbackPatternMatcherMap.TryGetValue(value, out patternMatcher)) { patternMatcher = new PatternMatcher(value, EnUSCultureInfo, verbatimIdentifierPrefixIsWordCharacter: true); _fallbackPatternMatcherMap.Add(value, patternMatcher); } return patternMatcher; } }
private PatternMatcher GetPatternMatcher( string value, CultureInfo culture, Dictionary<CultureInfo, Dictionary<string, PatternMatcher>> map) { lock (_gate) { Dictionary<string, PatternMatcher> innerMap; if (!map.TryGetValue(culture, out innerMap)) { innerMap = new Dictionary<string, PatternMatcher>(); map[culture] = innerMap; } PatternMatcher patternMatcher; if (!innerMap.TryGetValue(value, out patternMatcher)) { patternMatcher = new PatternMatcher(value, culture, verbatimIdentifierPrefixIsWordCharacter: true, allowFuzzyMatching: false); innerMap.Add(value, patternMatcher); } return patternMatcher; } }
internal async Task<IEnumerable<ValueTuple<ISymbol, IEnumerable<PatternMatch>>>> FindNavigableSourceSymbolsAsync( Project project, CancellationToken cancellationToken) { var results = new List<ValueTuple<ISymbol, IEnumerable<PatternMatch>>>(); // The compiler API only supports a predicate which is given a symbol's name. Because // we only have the name, and nothing else, we need to check it against the last segment // of the pattern. i.e. if the pattern is 'Console.WL' and we are given 'WriteLine', then // we don't want to check the whole pattern against it (as it will clearly fail), instead // we only want to check the 'WL' portion. Then, after we get all the candidate symbols // we'll check if the full name matches the full pattern. var patternMatcher = new PatternMatcher(_searchPattern); var symbols = await SymbolFinder.FindSourceDeclarationsAsync( project, k => patternMatcher.GetMatchesForLastSegmentOfPattern(k) != null, SymbolFilter.TypeAndMember, cancellationToken).ConfigureAwait(false); symbols = symbols.Where(s => !s.IsConstructor() && !s.IsStaticConstructor() // not constructors, they get matched on type name && !(s is INamespaceSymbol) // not namespaces && s.Locations.Any(loc => loc.IsInSource)); // only source symbols foreach (var symbol in symbols) { cancellationToken.ThrowIfCancellationRequested(); // As an optimization, don't bother getting the container for this symbol if this // isn't a dotted pattern. Getting the container could cause lots of string // allocations that we don't if we're never going to check it. var matches = !patternMatcher.IsDottedPattern ? patternMatcher.GetMatches(GetSearchName(symbol)) : patternMatcher.GetMatches(GetSearchName(symbol), GetContainer(symbol)); if (matches == null) { continue; } results.Add(ValueTuple.Create(symbol, matches)); // also report matching constructors (using same match result as type) var namedType = symbol as INamedTypeSymbol; if (namedType != null) { foreach (var constructor in namedType.Constructors) { // only constructors that were explicitly declared if (!constructor.IsImplicitlyDeclared) { results.Add(ValueTuple.Create((ISymbol)constructor, matches)); } } } // report both parts of partial methods var method = symbol as IMethodSymbol; if (method != null && method.PartialImplementationPart != null) { results.Add(ValueTuple.Create((ISymbol)method, matches)); } } return results; }