diff --git a/Docs/Content/VersionHistory/VersionHistory.aml b/Docs/Content/VersionHistory/VersionHistory.aml index fac43dd..f022969 100644 --- a/Docs/Content/VersionHistory/VersionHistory.aml +++ b/Docs/Content/VersionHistory/VersionHistory.aml @@ -9,6 +9,12 @@ project. Select a version below to see a description of its changes.
+ + + + + + diff --git a/Docs/Content/VersionHistory/v2023.12.29.0.aml b/Docs/Content/VersionHistory/v2023.12.29.0.aml new file mode 100644 index 0000000..9ac2c9c --- /dev/null +++ b/Docs/Content/VersionHistory/v2023.12.29.0.aml @@ -0,0 +1,43 @@ + + + + + Changes made in this release: + + +
+ + + + Moved generation of suggestions for code analyzer misspellings to the code fix provider which +should reduce excessive CPU usage by the code analyzer. + + + + Fixed an issue with the classifier factory throwing an exception with ASPX files. + + + + Fixed a couple of issues when adding and removing sections in the configuration editor. + + + + Fixed handling of all uppercase words in the word splitter when they are being spell checked. + + + + Changed type identifier renaming to only rename the containing file if the filename matches the +misspelled identifier name. + + + + + +
+ + + + + +
+
diff --git a/Docs/ContentLayout.content b/Docs/ContentLayout.content index d5acc80..c57652e 100644 --- a/Docs/ContentLayout.content +++ b/Docs/ContentLayout.content @@ -164,7 +164,12 @@ - + + + + + + diff --git a/Docs/VSSpellCheckerDocs.shfbproj b/Docs/VSSpellCheckerDocs.shfbproj index 89575c7..f3a17bb 100644 --- a/Docs/VSSpellCheckerDocs.shfbproj +++ b/Docs/VSSpellCheckerDocs.shfbproj @@ -65,7 +65,7 @@ - 2023.5.15.0 + 2023.12.29.0 0 ..\Source\ True @@ -219,6 +219,7 @@ + diff --git a/Source/SpellCheckCodeAnalyzer.CodeFixes2022AndLater/SpellCheckCodeFixProvider.cs b/Source/SpellCheckCodeAnalyzer.CodeFixes2022AndLater/SpellCheckCodeFixProvider.cs index 97f4d61..662baea 100644 --- a/Source/SpellCheckCodeAnalyzer.CodeFixes2022AndLater/SpellCheckCodeFixProvider.cs +++ b/Source/SpellCheckCodeAnalyzer.CodeFixes2022AndLater/SpellCheckCodeFixProvider.cs @@ -2,7 +2,7 @@ // System : Visual Studio Spell Checker Package // File : SpellCheckCodeFixProvider.cs // Author : Eric Woodruff (Eric@EWoodruff.us) -// 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(); + 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(), 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 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) } } + /// + /// This is used to filter and adjust the suggestions used to fix a misspelling in an identifier + /// + /// The suggestions that should replace the misspelling + /// An enumerable list of valid suggestions, if any. + /// Some suggestions include spaces or punctuation. Those are altered to remove the punctuation + /// and return the suggestion in camel case. + private static HashSet CheckSuggestions(IEnumerable suggestions) + { + var validSuggestions = new HashSet(); + + 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; + } + + /// /// Create the solution used to correct a spelling error /// diff --git a/Source/SpellCheckCodeAnalyzer2022AndLater/CSharpSpellCheckCodeAnalyzer.cs b/Source/SpellCheckCodeAnalyzer2022AndLater/CSharpSpellCheckCodeAnalyzer.cs index 04b04be..7668ef6 100644 --- a/Source/SpellCheckCodeAnalyzer2022AndLater/CSharpSpellCheckCodeAnalyzer.cs +++ b/Source/SpellCheckCodeAnalyzer2022AndLater/CSharpSpellCheckCodeAnalyzer.cs @@ -2,7 +2,7 @@ // System : Visual Studio Spell Checker Package // File : CSharpSpellCheckCodeAnalyzer.cs // Author : Eric Woodruff (Eric@EWoodruff.us) -// Updated : 10/27/2023 +// Updated : 12/29/2023 // Note : Copyright 2023, Eric Woodruff, All rights reserved // // This file contains the class used to implement the C# spell check code analyzer @@ -17,8 +17,6 @@ // 02/26/2023 EFW Created the code //=============================================================================================================== -// Ignore Spelling: welldone - using System; using System.Collections.Generic; using System.Collections.Immutable; @@ -251,7 +249,7 @@ public static void AnalyzeSyntaxTree(SyntaxTreeAnalysisContext context) context.CancellationToken.ThrowIfCancellationRequested(); - if(globalDictionaries.Any()) + if(globalDictionaries.Count != 0) { var dictionary = new SpellingDictionary(globalDictionaries, configuration.IgnoredWords); var handler = new CSharpSpellCheckHandler(configuration); @@ -262,12 +260,7 @@ public static void AnalyzeSyntaxTree(SyntaxTreeAnalysisContext context) var ignoreSpellingWords = handler.FindIgnoreSpellingDirectives(); var rangeExclusions = new List(); - var diagnosticProperties = new Dictionary - { - { "Suggestions", null }, - { "Prefix", null }, - { "Suffix", null } - }; + var diagnosticProperties = new Dictionary(); // Just handle identifiers and leave the rest to the existing tagger which already handles // all the other elements. The inability to add words to the ignored words files and the @@ -311,13 +304,19 @@ public static void AnalyzeSyntaxTree(SyntaxTreeAnalysisContext context) if(configuration.ShouldIgnoreWord(textToCheck) || dictionary.ShouldIgnoreWord(textToCheck)) continue; + if(s.Text.Length != word.Length) + { + diagnosticProperties["Prefix"] = s.Text.Substring(0, word.Start); + diagnosticProperties["Suffix"] = s.Text.Substring(word.Start + word.Length); + } + // Handle code analysis dictionary checks first as they may be not be // recognized as correctly spelled words but have alternate handling. if(configuration.CadOptions.TreatDeprecatedTermsAsMisspelled && configuration.DeprecatedTerms.TryGetValue(textToCheck, out string preferredTerm)) { - diagnosticProperties["Prefix"] = diagnosticProperties["Suffix"] = null; - diagnosticProperties["Suggestions"] = preferredTerm; + diagnosticProperties["Suggestions"] = preferredTerm.CapitalizeIfNecessary( + Char.IsUpper(textToCheck[0])); context.ReportDiagnostic(Diagnostic.Create(SpellingRule, Location.Create(context.Tree, s.TextSpan), @@ -328,8 +327,8 @@ public static void AnalyzeSyntaxTree(SyntaxTreeAnalysisContext context) if(configuration.CadOptions.TreatCompoundTermsAsMisspelled && configuration.CompoundTerms.TryGetValue(textToCheck, out preferredTerm)) { - diagnosticProperties["Prefix"] = diagnosticProperties["Suffix"] = null; - diagnosticProperties["Suggestions"] = preferredTerm; + diagnosticProperties["Suggestions"] = preferredTerm.CapitalizeIfNecessary( + Char.IsUpper(textToCheck[0])); context.ReportDiagnostic(Diagnostic.Create(SpellingRule, Location.Create(context.Tree, s.TextSpan), @@ -340,8 +339,8 @@ public static void AnalyzeSyntaxTree(SyntaxTreeAnalysisContext context) if(configuration.CadOptions.TreatUnrecognizedWordsAsMisspelled && configuration.UnrecognizedWords.TryGetValue(textToCheck, out IList spellingAlternates)) { - diagnosticProperties["Prefix"] = diagnosticProperties["Suffix"] = null; - diagnosticProperties["Suggestions"] = String.Join(",", spellingAlternates); + diagnosticProperties["Suggestions"] = String.Join(",", spellingAlternates.Select( + sa => sa.CapitalizeIfNecessary(Char.IsUpper(textToCheck[0])))); context.ReportDiagnostic(Diagnostic.Create(SpellingRule, Location.Create(context.Tree, s.TextSpan), @@ -353,21 +352,11 @@ public static void AnalyzeSyntaxTree(SyntaxTreeAnalysisContext context) // prevents a lot of false reports for things we don't care about. if(!dictionary.IsSpelledCorrectlyIgnoreCase(textToCheck)) { - // Ignore the dictionary the suggestions come from for now. I may add this - // later but we can't add words to dictionaries from the code fix so it may - // serve no purpose. - var suggestions = CheckSuggestions( - dictionary.SuggestCorrections(textToCheck).Select(ss => ss.Suggestion)); - - if(s.Text.Length != word.Length) - { - diagnosticProperties["Prefix"] = s.Text.Substring(0, word.Start); - diagnosticProperties["Suffix"] = s.Text.Substring(word.Start + word.Length); - } - else - diagnosticProperties["Prefix"] = diagnosticProperties["Suffix"] = null; - - diagnosticProperties["Suggestions"] = String.Join(",", suggestions); + // Getting suggestions is expensive so it is deferred until it's really + // needed when the code fix is invoked. + diagnosticProperties["TextToCheck"] = textToCheck; + diagnosticProperties["Languages"] = String.Join(",", + dictionary.Dictionaries.Select(d => d.Culture.Name)); context.ReportDiagnostic(Diagnostic.Create(SpellingRule, Location.Create(context.Tree, TextSpan.FromBounds( @@ -385,53 +374,6 @@ public static void AnalyzeSyntaxTree(SyntaxTreeAnalysisContext context) } } } - - /// - /// This is used to filter and adjust the suggestions used to fix a misspelling in an identifier - /// - /// The suggestions that should replace the misspelling - /// An enumerable list of valid suggestions, if any. - /// Some suggestions include spaces or punctuation. Those are altered to remove the punctuation - /// and return the suggestion in camel case. - private static IEnumerable CheckSuggestions(IEnumerable suggestions) - { - var validSuggestions = new HashSet(); - - 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; - } #endregion } } diff --git a/Source/VSSpellChecker2017And2019/source.extension.vsixmanifest b/Source/VSSpellChecker2017And2019/source.extension.vsixmanifest index b00a6c1..a4fb5dd 100644 --- a/Source/VSSpellChecker2017And2019/source.extension.vsixmanifest +++ b/Source/VSSpellChecker2017And2019/source.extension.vsixmanifest @@ -1,7 +1,7 @@  - + Visual Studio Spell Checker (VS2017 and VS2019) An editor extension that checks the spelling of comments, strings, and plain text as you type or interactively with a tool window. It can also spell check an entire solution, project, or selected items. Options are available to define multiple languages to spell check against, define ignored words, control how elements and attributes in XML and MAML files are spell checked, and much more. https://ewsoftware.github.io/VSSpellChecker diff --git a/Source/VSSpellChecker2022AndLater/source.extension.vsixmanifest b/Source/VSSpellChecker2022AndLater/source.extension.vsixmanifest index 85516e3..e73bd26 100644 --- a/Source/VSSpellChecker2022AndLater/source.extension.vsixmanifest +++ b/Source/VSSpellChecker2022AndLater/source.extension.vsixmanifest @@ -1,7 +1,7 @@  - + Visual Studio Spell Checker (VS2022 and Later) An editor extension that checks the spelling of comments, strings, and plain text as you type or interactively with a tool window. It can also spell check an entire solution, project, or selected items. Options are available to define multiple languages to spell check against, define ignored words, control how elements and attributes in XML and MAML files are spell checked, and much more. https://ewsoftware.github.io/VSSpellChecker diff --git a/Source/VSSpellCheckerCommon/CommonUtilities.cs b/Source/VSSpellCheckerCommon/CommonUtilities.cs index 6889f42..2f5c1d7 100644 --- a/Source/VSSpellCheckerCommon/CommonUtilities.cs +++ b/Source/VSSpellCheckerCommon/CommonUtilities.cs @@ -2,7 +2,7 @@ // System : Visual Studio Spell Checker Package // File : CommonUtilities.cs // Author : Eric Woodruff (Eric@EWoodruff.us) -// Updated : 04/27/2023 +// Updated : 12/29/2023 // Note : Copyright 2013-2023, Eric Woodruff, All rights reserved // // This file contains a utility class with extension and utility methods. @@ -39,6 +39,9 @@ public static class CommonUtilities private static readonly Regex reUppercase = new Regex("([A-Z])"); private static readonly Regex reRegexSeparator = new Regex(@"\(\?\#/Options/(.*?)\)"); + private static readonly char[] spaceSeparator = new[] { ' ' }, backslashSeparator = new[] { '\\' }, + digits = new[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }; + /// /// This defines the minimum identifier length /// @@ -157,6 +160,27 @@ public static string ToRelativePath(this string absolutePath, string basePath) return (hasBackslash) ? relPath + "\\" : relPath; } + /// + /// Capitalize a word if necessary + /// + /// The word to capitalize + /// True to capitalize it, false if not + /// The word capitalized if necessary + public static string CapitalizeIfNecessary(this string word, bool capitalize) + { + if(!capitalize || String.IsNullOrWhiteSpace(word) || word.Length == 0 || !Char.IsLetter(word[0]) || + Char.IsUpper(word[0])) + { + return word; + } + + var chars = word.ToCharArray(); + + chars[0] = Char.ToUpperInvariant(chars[0]); + + return new string(chars); + } + /// /// Convert a camel cased term to one or more space-separated words /// @@ -315,7 +339,7 @@ public static IEnumerable LoadUserDictionary(string filename, bool onlyA // If it doesn't look like an XML file, assume it's text of some sort. Convert anything that // isn't a letter or a digit to a space and get each word. var wordList = (new String(File.ReadAllText(filename).ToCharArray().Select( - c => (c == '\\' || Char.IsLetterOrDigit(c)) ? c : ' ').ToArray())).Split(new[] { ' ' }, + c => (c == '\\' || Char.IsLetterOrDigit(c)) ? c : ' ').ToArray())).Split(spaceSeparator, StringSplitOptions.RemoveEmptyEntries).ToList(); // Handle escaped words and split words containing the escape anywhere other than the start @@ -326,7 +350,7 @@ public static IEnumerable LoadUserDictionary(string filename, bool onlyA if(w.Length > 2) { if(w.IndexOf('\\', 1) > 0) - wordList.AddRange(w.Split(new[] { '\\' }, StringSplitOptions.RemoveEmptyEntries)); + wordList.AddRange(w.Split(backslashSeparator, StringSplitOptions.RemoveEmptyEntries)); else { if(!onlyAddedWords) @@ -346,8 +370,7 @@ public static IEnumerable LoadUserDictionary(string filename, bool onlyA if(allWords) return words; - return words.Distinct().Where(w => w.Length > 1 && w.IndexOfAny( - new[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }) == -1).ToList(); + return words.Distinct().Where(w => w.Length > 1 && w.IndexOfAny(digits) == -1).ToList(); } /// diff --git a/Source/VSSpellCheckerCommon/GlobalSuppressions.cs b/Source/VSSpellCheckerCommon/GlobalSuppressions.cs index d361d99..d921733 100644 --- a/Source/VSSpellCheckerCommon/GlobalSuppressions.cs +++ b/Source/VSSpellCheckerCommon/GlobalSuppressions.cs @@ -36,3 +36,4 @@ [assembly: SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "", Scope = "member", Target = "~M:VisualStudio.SpellChecker.Common.GlobalDictionary.AddSuggestions")] [assembly: SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "", Scope = "member", Target = "~M:VisualStudio.SpellChecker.Common.GlobalDictionary.RemoveWord(System.String)")] [assembly: SuppressMessage("Globalization", "CA1308:Normalize strings to uppercase", Justification = "", Scope = "member", Target = "~M:VisualStudio.SpellChecker.Common.Configuration.Legacy.SpellCheckerLegacyConfiguration.ConvertFromLegacyConfiguration~System.ValueTuple{System.Collections.Generic.IEnumerable{System.ValueTuple{System.String,System.String}},System.Collections.Generic.IEnumerable{System.String}}")] +[assembly: SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "", Scope = "member", Target = "~M:VisualStudio.SpellChecker.Common.GlobalDictionary.IsSpelledCorrectlyIgnoreCase(System.String)~System.Boolean")] diff --git a/Source/VSSpellCheckerDefinitionsShared/Properties/AssemblyInfoShared.cs b/Source/VSSpellCheckerDefinitionsShared/Properties/AssemblyInfoShared.cs index f03e8eb..458ec6c 100644 --- a/Source/VSSpellCheckerDefinitionsShared/Properties/AssemblyInfoShared.cs +++ b/Source/VSSpellCheckerDefinitionsShared/Properties/AssemblyInfoShared.cs @@ -2,7 +2,7 @@ // System : Visual Studio Spell Checker // File : AssemblyInfoShared.cs // Author : Eric Woodruff (Eric@EWoodruff.us) -// Updated : 05/15/2023 +// Updated : 12/29/2023 // Note : Copyright 2013-2023, Eric Woodruff, All rights reserved // // Visual Studio spell checker common assembly attributes @@ -75,13 +75,13 @@ internal static partial class AssemblyInfo // // This is used to set the assembly file version. This will change with each new release. MSIs only // support a Major value between 0 and 255 so we drop the century from the year on this one. - public const string FileVersion = "23.5.15.1"; + public const string FileVersion = "23.12.29.0"; // Common product version // // This may contain additional text to indicate Alpha or Beta states. The version number will always match // the file version above but includes the century on the year. - public const string ProductVersion = "2023.5.15.1"; + public const string ProductVersion = "2023.12.29.0"; // Assembly copyright information public const string Copyright = "Copyright \xA9 2013-2023, Eric Woodruff, All Rights Reserved.\r\n" + diff --git a/Source/VSSpellCheckerShared/SpellingTagger.cs b/Source/VSSpellCheckerShared/SpellingTagger.cs index 6c4a36c..81d194b 100644 --- a/Source/VSSpellCheckerShared/SpellingTagger.cs +++ b/Source/VSSpellCheckerShared/SpellingTagger.cs @@ -2,7 +2,7 @@ // System : Visual Studio Spell Checker Package // File : SpellingTagger.cs // Authors : Noah Richards, Roman Golovin, Michael Lehenbauer, Eric Woodruff -// Updated : 03/22/2023 +// Updated : 12/29/2023 // Note : Copyright 2010-2023, Microsoft Corporation, All rights reserved // Portions Copyright 2013-2023, Eric Woodruff, All rights reserved // @@ -784,7 +784,8 @@ private IEnumerable GetMisspellingsInSpans(NormalizedSnapshotSpa configuration.DeprecatedTerms.TryGetValue(textToCheck, out string preferredTerm)) { yield return new MisspellingTag(MisspellingType.DeprecatedTerm, errorSpan, - new[] { new SpellingSuggestion(null, preferredTerm) }); + new[] { new SpellingSuggestion(null, preferredTerm.CapitalizeIfNecessary( + Char.IsUpper(textToCheck[0]))) }); continue; } @@ -792,7 +793,8 @@ private IEnumerable GetMisspellingsInSpans(NormalizedSnapshotSpa configuration.CompoundTerms.TryGetValue(textToCheck, out preferredTerm)) { yield return new MisspellingTag(MisspellingType.CompoundTerm, errorSpan, - new[] { new SpellingSuggestion(null, preferredTerm) }); + new[] { new SpellingSuggestion(null, preferredTerm.CapitalizeIfNecessary( + Char.IsUpper(textToCheck[0]))) }); continue; } @@ -800,7 +802,8 @@ private IEnumerable GetMisspellingsInSpans(NormalizedSnapshotSpa configuration.UnrecognizedWords.TryGetValue(textToCheck, out IList spellingAlternates)) { yield return new MisspellingTag(MisspellingType.UnrecognizedWord, errorSpan, - spellingAlternates.Select(a => new SpellingSuggestion(null, a))); + spellingAlternates.Select(a => new SpellingSuggestion(null, a.CapitalizeIfNecessary( + Char.IsUpper(textToCheck[0]))))); continue; } diff --git a/Source/VSSpellCheckerShared/SuggestedActions/SpellSuggestedActionSource.cs b/Source/VSSpellCheckerShared/SuggestedActions/SpellSuggestedActionSource.cs index 3c4cdaf..84f0a0e 100644 --- a/Source/VSSpellCheckerShared/SuggestedActions/SpellSuggestedActionSource.cs +++ b/Source/VSSpellCheckerShared/SuggestedActions/SpellSuggestedActionSource.cs @@ -2,7 +2,7 @@ // System : Visual Studio Spell Checker Package // File : SpellSuggestedActionSource.cs // Author : Eric Woodruff (Eric@EWoodruff.us) -// Updated : 04/23/2023 +// Updated : 12/29/2023 // Note : Copyright 2016-2023, Eric Woodruff, All rights reserved // // This file contains a class used to implement the suggestion source for spelling light bulbs @@ -328,9 +328,12 @@ private IEnumerable GetMisspellingSuggestedActions(SnapshotS actions.Add(new IgnoredWordsSuggestedAction(trackingSpan, dictionary, iwf, String.Empty)); } - actionSets.Add(new SuggestedActionSet(null, new[] { new SuggestedActionSubmenu( - "Add to Ignored Words File", new[] { new SuggestedActionSet(null, actions) }) }, null, - SuggestedActionSetPriority.Low)); + if(actions.Count != 0) + { + actionSets.Add(new SuggestedActionSet(null, new[] { new SuggestedActionSubmenu( + "Add to Ignored Words File", new[] { new SuggestedActionSet(null, actions) }) }, null, + SuggestedActionSetPriority.Low)); + } } }