public async Task <IEnumerable <SearchResult <ISymbol> > > FindDeclarationsAsync(string name, TSimpleNameSyntax nameNode, SymbolFilter filter) { if (name != null && string.IsNullOrWhiteSpace(name)) { return(SpecializedCollections.EmptyEnumerable <SearchResult <ISymbol> >()); } var query = this.Exact ? SearchQuery.Create(name, ignoreCase: true) : SearchQuery.CreateFuzzy(name); var symbols = await FindDeclarationsAsync(name, filter, query).ConfigureAwait(false); if (Exact) { // We did an exact, case insensitive, search. Case sensitive matches should // be preffered though over insensitive ones. return(symbols.Select(s => SearchResult.Create(s.Name, nameNode, s, weight: s.Name == name ? 0 : 1)).ToList()); } // TODO(cyrusn): It's a shame we have to compute this twice. However, there's no // great way to store the original value we compute because it happens deep in the // compiler bowels when we call FindDeclarations. using (var similarityChecker = new WordSimilarityChecker(name)) { return(symbols.Select(s => { double matchCost; var areSimilar = similarityChecker.AreSimilar(s.Name, out matchCost); Debug.Assert(areSimilar); return SearchResult.Create(s.Name, nameNode, s, matchCost); }).ToList()); } }
public void TestCloseMatch() { Assert.True(WordSimilarityChecker.AreSimilar("variabledeclaratorsyntax", "variabledeclaratorsyntaxextensions")); Assert.True(WordSimilarityChecker.AreSimilar("expressionsyntax", "expressionsyntaxextensions")); Assert.True(WordSimilarityChecker.AreSimilar("expressionsyntax", "expressionsyntaxgeneratorvisitor")); }
private async Task CheckItemsAsync( CodeFixContext context, SyntaxToken nameToken, bool isGeneric, CompletionList completionList, WordSimilarityChecker similarityChecker) { var document = context.Document; var cancellationToken = context.CancellationToken; var onlyConsiderGenerics = isGeneric; var results = new MultiDictionary <double, string>(); foreach (var item in completionList.ItemsList) { if (onlyConsiderGenerics && !IsGeneric(item)) { continue; } var candidateText = item.FilterText; if (!similarityChecker.AreSimilar(candidateText, out var matchCost)) { continue; } var insertionText = await GetInsertionTextAsync(document, item, cancellationToken : cancellationToken).ConfigureAwait(false); results.Add(matchCost, insertionText); } var nameText = nameToken.ValueText; var codeActions = results.OrderBy(kvp => kvp.Key) .SelectMany(kvp => kvp.Value.Order()) .Where(t => t != nameText) .Take(3) .Select(n => CreateCodeAction(nameToken, nameText, n, document)) .ToImmutableArrayOrEmpty <CodeAction>(); if (codeActions.Length > 1) { // Wrap the spell checking actions into a single top level suggestion // so as to not clutter the list. context.RegisterCodeFix( CodeAction.CreateWithPriority( CodeActionPriority.Low, string.Format(FeaturesResources.Fix_typo_0, nameText), codeActions, isInlinable: true), context.Diagnostics); } else { context.RegisterFixes(codeActions, context.Diagnostics); } }
private async Task CreateSpellCheckCodeIssueAsync(CodeFixContext context, TSimpleName nameNode, string nameText, CancellationToken cancellationToken) { var document = context.Document; var service = CompletionService.GetService(document); // Disable snippets from ever appearing in the completion items. It's // very unlikely the user would ever mispell a snippet, then use spell- // checking to fix it, then try to invoke the snippet. var originalOptions = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); var options = originalOptions.WithChangedOption(CompletionOptions.SnippetsBehavior, document.Project.Language, SnippetsRule.NeverInclude); var completionList = await service.GetCompletionsAsync( document, nameNode.SpanStart, options : options, cancellationToken : cancellationToken).ConfigureAwait(false); if (completionList == null) { return; } var onlyConsiderGenerics = IsGeneric(nameNode); var results = new MultiDictionary <double, string>(); using (var similarityChecker = new WordSimilarityChecker(nameText, substringsAreSimilar: true)) { foreach (var item in completionList.Items) { if (onlyConsiderGenerics && !IsGeneric(item)) { continue; } var candidateText = item.FilterText; double matchCost; if (!similarityChecker.AreSimilar(candidateText, out matchCost)) { continue; } var insertionText = await GetInsertionTextAsync(document, item, cancellationToken : cancellationToken).ConfigureAwait(false); results.Add(matchCost, insertionText); } } var matches = results.OrderBy(kvp => kvp.Key) .SelectMany(kvp => kvp.Value.Order()) .Where(t => t != nameText) .Take(3) .Select(n => CreateCodeAction(nameNode, nameText, n, document)); context.RegisterFixes(matches, context.Diagnostics); }
public void TestNotCloseMatch() { Assert.False(WordSimilarityChecker.AreSimilar("propertyblocksyntax", "ipropertysymbol")); Assert.False(WordSimilarityChecker.AreSimilar("propertyblocksyntax", "ipropertysymbolextensions")); Assert.False(WordSimilarityChecker.AreSimilar("propertyblocksyntax", "typeblocksyntaxextensions")); Assert.False(WordSimilarityChecker.AreSimilar("fielddeclarationsyntax", "declarationinfo")); Assert.False(WordSimilarityChecker.AreSimilar("fielddeclarationsyntax", "declarationcomputer")); Assert.False(WordSimilarityChecker.AreSimilar("fielddeclarationsyntax", "filelinepositionspan")); Assert.False(WordSimilarityChecker.AreSimilar("variabledeclaratorsyntax", "visualbasicdeclarationcomputer")); Assert.False(WordSimilarityChecker.AreSimilar("variabledeclaratorsyntax", "ilineseparatorservice")); Assert.False(WordSimilarityChecker.AreSimilar("expressionsyntax", "awaitexpressioninfo")); }
private async Task CreateSpellCheckCodeIssueAsync(CodeFixContext context, TSimpleName nameNode, string nameText, CancellationToken cancellationToken) { var document = context.Document; var service = CompletionService.GetService(document); var completionList = await service.GetCompletionsAsync( document, nameNode.SpanStart, cancellationToken : cancellationToken).ConfigureAwait(false); if (completionList == null) { return; } var onlyConsiderGenerics = IsGeneric(nameNode); var results = new MultiDictionary <double, string>(); using (var similarityChecker = new WordSimilarityChecker(nameText, substringsAreSimilar: true)) { foreach (var item in completionList.Items) { if (onlyConsiderGenerics && !IsGeneric(item)) { continue; } var candidateText = item.FilterText; double matchCost; if (!similarityChecker.AreSimilar(candidateText, out matchCost)) { continue; } var insertionText = await GetInsertionTextAsync(document, item, cancellationToken : cancellationToken).ConfigureAwait(false); results.Add(matchCost, insertionText); } } var matches = results.OrderBy(kvp => kvp.Key) .SelectMany(kvp => kvp.Value.Order()) .Where(t => t != nameText) .Take(3) .Select(n => CreateCodeAction(nameNode, nameText, n, document)); context.RegisterFixes(matches, context.Diagnostics); }