-
Notifications
You must be signed in to change notification settings - Fork 65
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Moved generation of suggestions from the code analyzer to the code fix to alleviate excessive CPU usage by the code analyzer. Fixes #305.
- Loading branch information
1 parent
745ebe1
commit a7be263
Showing
13 changed files
with
215 additions
and
106 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<topic id="9b0ee624-54c6-4020-b546-03e6f5c96b0e" revisionNumber="1"> | ||
<developerConceptualDocument xmlns="http://ddue.schemas.microsoft.com/authoring/2003/5" xmlns:xlink="http://www.w3.org/1999/xlink"> | ||
<introduction> | ||
<para>Changes made in this release:</para> | ||
</introduction> | ||
|
||
<section> | ||
<content> | ||
<list class="bullet"> | ||
<listItem> | ||
<para>Moved generation of suggestions for code analyzer misspellings to the code fix provider which | ||
should reduce excessive CPU usage by the code analyzer.</para> | ||
</listItem> | ||
|
||
<listItem> | ||
<para>Fixed an issue with the classifier factory throwing an exception with ASPX files.</para> | ||
</listItem> | ||
|
||
<listItem> | ||
<para>Fixed a couple of issues when adding and removing sections in the configuration editor.</para> | ||
</listItem> | ||
|
||
<listItem> | ||
<para>Fixed handling of all uppercase words in the word splitter when they are being spell checked.</para> | ||
</listItem> | ||
|
||
<listItem> | ||
<para>Changed type identifier renaming to only rename the containing file if the filename matches the | ||
misspelled identifier name.</para> | ||
</listItem> | ||
|
||
</list> | ||
|
||
</content> | ||
</section> | ||
|
||
<relatedTopics> | ||
<link xlink:href="548dc6d7-6d08-4006-82b3-d5830be96f04" /> | ||
</relatedTopics> | ||
|
||
</developerConceptualDocument> | ||
</topic> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,7 +2,7 @@ | |
// System : Visual Studio Spell Checker Package | ||
// File : SpellCheckCodeFixProvider.cs | ||
// Author : Eric Woodruff ([email protected]) | ||
// Updated : 10/27/2023 | ||
// Updated : 12/29/2023 | ||
// Note : Copyright 2023, Eric Woodruff, All rights reserved | ||
// | ||
// This file contains a class used to provide the spell check code fixes | ||
|
@@ -17,9 +17,13 @@ | |
// 01/29/2023 EFW Created the code | ||
//=============================================================================================================== | ||
|
||
// Ignore Spelling: welldone | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.Collections.Immutable; | ||
using System.Composition; | ||
using System.Globalization; | ||
using System.IO; | ||
using System.Linq; | ||
using System.Threading; | ||
|
@@ -34,6 +38,7 @@ | |
using Microsoft.CodeAnalysis.Rename; | ||
|
||
using VisualStudio.SpellChecker.CodeAnalyzer; | ||
using VisualStudio.SpellChecker.Common; | ||
|
||
namespace VisualStudio.SpellChecker.CodeFixes | ||
{ | ||
|
@@ -65,6 +70,8 @@ public sealed override FixAllProvider GetFixAllProvider() | |
public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) | ||
{ | ||
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); | ||
var suggestions = new List<string>(); | ||
var separators = new[] { ',' }; | ||
|
||
foreach(var diagnostic in context.Diagnostics) | ||
{ | ||
|
@@ -75,18 +82,45 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) | |
// Find the identifier for the diagnostic | ||
var syntaxToken = root.FindToken(diagnosticSpan.Start); | ||
|
||
if(diagnostic.Properties.TryGetValue("Suggestions", out string suggestions)) | ||
suggestions.Clear(); | ||
|
||
if(!diagnostic.Properties.TryGetValue("Suggestions", out string suggestedReplacements)) | ||
{ | ||
// If the misspelling is a sub-span, the prefix and suffix will contain the surrounding text | ||
// used to create the full identifier. | ||
_ = diagnostic.Properties.TryGetValue("Prefix", out string prefix); | ||
_ = diagnostic.Properties.TryGetValue("Suffix", out string suffix); | ||
if(diagnostic.Properties.TryGetValue("Languages", out string languages) && | ||
diagnostic.Properties.TryGetValue("TextToCheck", out suggestedReplacements)) | ||
{ | ||
// Getting suggestions is expensive so it's done here when actually needed rather | ||
// than in the code analyzer. Dictionaries should exist at this point since the | ||
// code analyzer will have created them. | ||
var globalDictionaries = languages.Split(separators, | ||
StringSplitOptions.RemoveEmptyEntries).Select(l => | ||
GlobalDictionary.CreateGlobalDictionary(new CultureInfo(l), null, | ||
Enumerable.Empty<string>(), false)).Where(d => d != null).Distinct().ToList(); | ||
|
||
if(globalDictionaries.Count != 0) | ||
{ | ||
var dictionary = new SpellingDictionary(globalDictionaries, null); | ||
|
||
suggestions.AddRange(CheckSuggestions( | ||
dictionary.SuggestCorrections(suggestedReplacements).Select(ss => ss.Suggestion))); | ||
} | ||
} | ||
} | ||
else | ||
suggestions.AddRange(suggestedReplacements.Split(separators, StringSplitOptions.RemoveEmptyEntries)); | ||
|
||
if(suggestedReplacements != null) | ||
{ | ||
ImmutableArray<CodeAction> replacements; | ||
|
||
if(!String.IsNullOrWhiteSpace(suggestions)) | ||
if(suggestions.Count != 0) | ||
{ | ||
replacements = suggestions.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select( | ||
// If the misspelling is a sub-span, the prefix and suffix will contain the surrounding text | ||
// used to create the full identifier. | ||
_ = diagnostic.Properties.TryGetValue("Prefix", out string prefix); | ||
_ = diagnostic.Properties.TryGetValue("Suffix", out string suffix); | ||
|
||
replacements = suggestions.Select( | ||
s => | ||
{ | ||
string replacement = (String.IsNullOrWhiteSpace(prefix) && | ||
|
@@ -124,6 +158,54 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) | |
} | ||
} | ||
|
||
/// <summary> | ||
/// This is used to filter and adjust the suggestions used to fix a misspelling in an identifier | ||
/// </summary> | ||
/// <param name="suggestions">The suggestions that should replace the misspelling</param> | ||
/// <returns>An enumerable list of valid suggestions, if any.</returns> | ||
/// <remarks>Some suggestions include spaces or punctuation. Those are altered to remove the punctuation | ||
/// and return the suggestion in camel case.</remarks> | ||
private static HashSet<string> CheckSuggestions(IEnumerable<string> suggestions) | ||
{ | ||
var validSuggestions = new HashSet<string>(); | ||
|
||
foreach(string s in suggestions) | ||
{ | ||
var wordChars = s.ToArray(); | ||
|
||
if(wordChars.All(c => Char.IsLetter(c))) | ||
validSuggestions.Add(s); | ||
else | ||
{ | ||
// Certain misspellings may return suggestions with spaces or punctuation. For example: | ||
// welldone suggests "well done" and "well-done". Return those as a camel case suggestion: | ||
// wellDone. | ||
bool caseChanged = false; | ||
|
||
for(int idx = 0; idx < wordChars.Length; idx++) | ||
{ | ||
if(!Char.IsLetter(wordChars[idx])) | ||
{ | ||
while(idx < wordChars.Length && !Char.IsLetter(wordChars[idx])) | ||
idx++; | ||
|
||
if(idx < wordChars.Length) | ||
{ | ||
wordChars[idx] = Char.ToUpperInvariant(wordChars[idx]); | ||
caseChanged = true; | ||
} | ||
} | ||
} | ||
|
||
if(caseChanged) | ||
validSuggestions.Add(new String(wordChars.Where(c => Char.IsLetter(c)).ToArray())); | ||
} | ||
} | ||
|
||
return validSuggestions; | ||
} | ||
|
||
|
||
/// <summary> | ||
/// Create the solution used to correct a spelling error | ||
/// </summary> | ||
|
Oops, something went wrong.