Skip to content

Commit 745ebe1

Browse files
committed
Changes made:
- Changed the type identifier renaming to only rename the containing file if the filename matches the misspelled identifier name. Fixes #304. - Added a new helper method to spell check identifiers case insensitively. This is the first step to moving the suggestion generation to the code fix provider to try to alleviate CPU usage in the analyzer.
1 parent 8d0651a commit 745ebe1

File tree

4 files changed

+68
-46
lines changed

4 files changed

+68
-46
lines changed

Source/SpellCheckCodeAnalyzer.CodeFixes2022AndLater/SpellCheckCodeFixProvider.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// System : Visual Studio Spell Checker Package
33
// File : SpellCheckCodeFixProvider.cs
44
// Author : Eric Woodruff (Eric@EWoodruff.us)
5-
// Updated : 05/06/2023
5+
// Updated : 10/27/2023
66
// Note : Copyright 2023, Eric Woodruff, All rights reserved
77
//
88
// This file contains a class used to provide the spell check code fixes
@@ -20,6 +20,7 @@
2020
using System;
2121
using System.Collections.Immutable;
2222
using System.Composition;
23+
using System.IO;
2324
using System.Linq;
2425
using System.Threading;
2526
using System.Threading.Tasks;
@@ -161,8 +162,11 @@ private static async Task<Solution> CorrectSpellingAsync(Document document, Synt
161162
case BaseTypeDeclarationSyntax btds:
162163
symbol = semanticModel.GetDeclaredSymbol(btds, cancellationToken);
163164

164-
// Rename the file only if the type is not nested within another type
165-
renameFile = !(token.Parent?.Parent is ClassDeclarationSyntax);
165+
// Rename the file only if the type is not nested within another type and the filename
166+
// matches the token text. GitHub issue #304.
167+
renameFile = !(token.Parent?.Parent is ClassDeclarationSyntax) &&
168+
Path.GetFileNameWithoutExtension(token.SyntaxTree.FilePath ?? String.Empty).Equals(
169+
token.Text, StringComparison.OrdinalIgnoreCase);
166170
break;
167171

168172
case DelegateDeclarationSyntax dds:

Source/SpellCheckCodeAnalyzer2022AndLater/CSharpSpellCheckCodeAnalyzer.cs

Lines changed: 27 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// System : Visual Studio Spell Checker Package
33
// File : CSharpSpellCheckCodeAnalyzer.cs
44
// Author : Eric Woodruff (Eric@EWoodruff.us)
5-
// Updated : 05/06/2023
5+
// Updated : 10/27/2023
66
// Note : Copyright 2023, Eric Woodruff, All rights reserved
77
//
88
// This file contains the class used to implement the C# spell check code analyzer
@@ -349,36 +349,35 @@ public static void AnalyzeSyntaxTree(SyntaxTreeAnalysisContext context)
349349
continue;
350350
}
351351

352-
if(!dictionary.IsSpelledCorrectly(textToCheck))
352+
// Ignore case as variable names may not match the case and that's okay. This
353+
// prevents a lot of false reports for things we don't care about.
354+
if(!dictionary.IsSpelledCorrectlyIgnoreCase(textToCheck))
353355
{
354356
// Ignore the dictionary the suggestions come from for now. I may add this
355357
// later but we can't add words to dictionaries from the code fix so it may
356358
// serve no purpose.
357-
var (skipMisspelling, suggestions) = CheckSuggestions(textToCheck,
359+
var suggestions = CheckSuggestions(
358360
dictionary.SuggestCorrections(textToCheck).Select(ss => ss.Suggestion));
359361

360-
if(!skipMisspelling)
362+
if(s.Text.Length != word.Length)
361363
{
362-
if(s.Text.Length != word.Length)
363-
{
364-
diagnosticProperties["Prefix"] = s.Text.Substring(0, word.Start);
365-
diagnosticProperties["Suffix"] = s.Text.Substring(word.Start + word.Length);
366-
}
367-
else
368-
diagnosticProperties["Prefix"] = diagnosticProperties["Suffix"] = null;
369-
370-
diagnosticProperties["Suggestions"] = String.Join(",", suggestions);
371-
372-
context.ReportDiagnostic(Diagnostic.Create(SpellingRule,
373-
Location.Create(context.Tree, TextSpan.FromBounds(
374-
s.TextSpan.Start + word.Start,
375-
s.TextSpan.Start + word.Start + word.Length)),
376-
diagnosticProperties.ToImmutableDictionary(), textToCheck));
377-
context.ReportDiagnostic(Diagnostic.Create(IgnoreWordRule,
378-
Location.Create(context.Tree, TextSpan.FromBounds(
379-
s.TextSpan.Start + word.Start,
380-
s.TextSpan.Start + word.Start + word.Length)), textToCheck));
364+
diagnosticProperties["Prefix"] = s.Text.Substring(0, word.Start);
365+
diagnosticProperties["Suffix"] = s.Text.Substring(word.Start + word.Length);
381366
}
367+
else
368+
diagnosticProperties["Prefix"] = diagnosticProperties["Suffix"] = null;
369+
370+
diagnosticProperties["Suggestions"] = String.Join(",", suggestions);
371+
372+
context.ReportDiagnostic(Diagnostic.Create(SpellingRule,
373+
Location.Create(context.Tree, TextSpan.FromBounds(
374+
s.TextSpan.Start + word.Start,
375+
s.TextSpan.Start + word.Start + word.Length)),
376+
diagnosticProperties.ToImmutableDictionary(), textToCheck));
377+
context.ReportDiagnostic(Diagnostic.Create(IgnoreWordRule,
378+
Location.Create(context.Tree, TextSpan.FromBounds(
379+
s.TextSpan.Start + word.Start,
380+
s.TextSpan.Start + word.Start + word.Length)), textToCheck));
382381
}
383382
}
384383
}
@@ -390,34 +389,20 @@ public static void AnalyzeSyntaxTree(SyntaxTreeAnalysisContext context)
390389
/// <summary>
391390
/// This is used to filter and adjust the suggestions used to fix a misspelling in an identifier
392391
/// </summary>
393-
/// <param name="misspelling">The misspelling</param>
394392
/// <param name="suggestions">The suggestions that should replace the misspelling</param>
395-
/// <returns>A flag indicating whether or not the misspelling should be skipped and an enumerable list of
396-
/// valid suggestions, if any.</returns>
397-
/// <remarks>Some suggestions include spaces or punctuation. Others only differ from the misspelling in
398-
/// casing. Misspellings in which there is a suggestion that only differs in case are ignored. Those
399-
/// with spaces or punctuation are altered to remove the punctuation and return the suggestion in camel
400-
/// case.</remarks>
401-
private static (bool SkipMisspelling, IEnumerable<string> Suggestions) CheckSuggestions(string misspelling,
402-
IEnumerable<string> suggestions)
393+
/// <returns>An enumerable list of valid suggestions, if any.</returns>
394+
/// <remarks>Some suggestions include spaces or punctuation. Those are altered to remove the punctuation
395+
/// and return the suggestion in camel case.</remarks>
396+
private static IEnumerable<string> CheckSuggestions(IEnumerable<string> suggestions)
403397
{
404398
var validSuggestions = new HashSet<string>();
405-
bool skipMisspelling = false;
406399

407400
foreach(string s in suggestions)
408401
{
409402
var wordChars = s.ToArray();
410403

411404
if(wordChars.All(c => Char.IsLetter(c)))
412-
{
413-
if(s.Equals(misspelling, StringComparison.OrdinalIgnoreCase))
414-
{
415-
skipMisspelling = true;
416-
break;
417-
}
418-
419405
validSuggestions.Add(s);
420-
}
421406
else
422407
{
423408
// Certain misspellings may return suggestions with spaces or punctuation. For example:
@@ -445,7 +430,7 @@ private static (bool SkipMisspelling, IEnumerable<string> Suggestions) CheckSugg
445430
}
446431
}
447432

448-
return (skipMisspelling, validSuggestions);
433+
return validSuggestions;
449434
}
450435
#endregion
451436
}

Source/VSSpellCheckerCommon/GlobalDictionary.cs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// System : Visual Studio Spell Checker Package
33
// File : GlobalDictionary.cs
44
// Author : Eric Woodruff (Eric@EWoodruff.us)
5-
// Updated : 05/06/2023
5+
// Updated : 10/27/2023
66
// Note : Copyright 2013-2023, Eric Woodruff, All rights reserved
77
//
88
// This file contains a class that implements the global dictionary
@@ -151,6 +151,28 @@ public bool IsSpelledCorrectly(string word)
151151
return true;
152152
}
153153

154+
/// <summary>
155+
/// This is used to spell check a word case insensitively
156+
/// </summary>
157+
/// <param name="word">The word to spell check</param>
158+
/// <returns>True if spelled correctly, false if not</returns>
159+
/// <remarks>The word is converted to uppercase to bypass the case sensitive setting in the dictionary</remarks>
160+
public bool IsSpelledCorrectlyIgnoreCase(string word)
161+
{
162+
try
163+
{
164+
if(this.IsInitialized && spellFactory != null && !spellFactory.IsDisposed && !String.IsNullOrWhiteSpace(word))
165+
return spellFactory.Spell(word.ToUpper(this.Culture));
166+
}
167+
catch(Exception ex)
168+
{
169+
// Ignore exceptions, there's not much we can do
170+
System.Diagnostics.Debug.WriteLine(ex);
171+
}
172+
173+
return true;
174+
}
175+
154176
/// <summary>
155177
/// This is used to suggest corrections for a misspelled word
156178
/// </summary>

Source/VSSpellCheckerCommon/SpellingDictionary.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,17 @@ public bool IsSpelledCorrectly(string word)
101101
return this.Dictionaries.Any(d => d.IsSpelledCorrectly(word));
102102
}
103103

104+
/// <summary>
105+
/// This is used to spell check a word case insensitively
106+
/// </summary>
107+
/// <param name="word">The word to spell check</param>
108+
/// <returns>True if spelled correctly, false if not</returns>
109+
/// <remarks>The word is converted to uppercase to bypass the case sensitive setting in the dictionary</remarks>
110+
public bool IsSpelledCorrectlyIgnoreCase(string word)
111+
{
112+
return this.Dictionaries.Any(d => d.IsSpelledCorrectlyIgnoreCase(word));
113+
}
114+
104115
/// <summary>
105116
/// This is used to suggest corrections for a misspelled word
106117
/// </summary>

0 commit comments

Comments
 (0)