From b2118cc3de3290947869dabf6a059699b81250d3 Mon Sep 17 00:00:00 2001 From: EWSoftware Date: Mon, 8 May 2023 11:20:57 -0700 Subject: [PATCH] Final updates for initial release of the code analyzer - Added an Ignore Word option to the code analyzer to add the ignored word to an Ignore Spelling directive comment. It's the best I can do for now as it doesn't appear to be possible to add them to the ignored words file or user dictionary. - Updated the documentation for the initial release of the code analyzer. Closes #111. --- .editorconfig | 205 +++++++++++------ Docs/Content/CodeAnalyzer.aml | 45 ++++ .../CodeAnalysisDictionaries.aml | 24 ++ .../ConfigOptions/CodeAnalyzerOptions.aml | 66 ++++++ Docs/Content/ConfigOptions/ConfigOptions.aml | 43 ++++ .../ConfigOptions/DictionarySettings.aml | 22 ++ .../ConfigOptions/ExclusionExpressions.aml | 14 ++ .../Content/ConfigOptions/GeneralSettings.aml | 54 ++++- .../ConfigOptions/IgnoredClassifications.aml | 18 ++ Docs/Content/ConfigOptions/IgnoredWords.aml | 20 +- Docs/Content/ConfigOptions/ImportSettings.aml | 6 + .../VisualStudioWPFTextBoxes.aml | 7 + Docs/Content/ConfigOptions/XMLFiles.aml | 18 ++ Docs/Content/ConvertingToEditorConfig.aml | 2 +- Docs/Content/KnownIssues.aml | 55 +++-- Docs/Content/SystemRequirements.aml | 13 +- Docs/Content/VSSpell001.aml | 97 ++++++++ Docs/Content/VSSpell002.aml | 94 ++++++++ .../{vNext.aml => v2023.5.8.0.aml} | 9 +- Docs/ContentLayout.content | 21 +- Docs/VSSpellCheckerDocs.shfbproj | 5 +- IgnoredWords.dic | 4 + README.md | 8 +- .../CodeFixResources.Designer.cs | 22 +- .../CodeFixResources.resx | 12 +- .../SpellCheckCodeFixProvider.cs | 208 ++++++++++++++---- .../AnalyzerReleases.Unshipped.md | 3 +- .../CSharpSpellCheckCodeAnalyzer.cs | 61 +++-- .../Resources.Designer.cs | 29 ++- .../Resources.resx | 11 +- .../GlobalSuppressions.cs | Bin 47002 -> 47586 bytes .../source.extension.vsixmanifest | 2 +- .../GlobalSuppressions.cs | Bin 47670 -> 48254 bytes .../source.extension.vsixmanifest | 2 +- .../SpellCheckerConfiguration.cs | 4 +- .../VSSpellCheckerCommon/GlobalDictionary.cs | 61 ++++- .../Properties/AssemblyInfoShared.cs | 6 +- .../Pages/DictionarySettingsUserControl.xaml | 9 +- .../DictionarySettingsUserControl.xaml.cs | 62 +++++- 39 files changed, 1151 insertions(+), 191 deletions(-) create mode 100644 Docs/Content/CodeAnalyzer.aml create mode 100644 Docs/Content/VSSpell001.aml create mode 100644 Docs/Content/VSSpell002.aml rename Docs/Content/VersionHistory/{vNext.aml => v2023.5.8.0.aml} (87%) diff --git a/.editorconfig b/.editorconfig index 6088e8b..b39dfc0 100644 --- a/.editorconfig +++ b/.editorconfig @@ -23,63 +23,162 @@ vsspell_exclusion_expressions_9f07c577adcd4fd7a93a42a503828225 = [a-z]{2}-([A-Z] vsspell_spell_check_as_you_type = false vsspell_include_in_project_spell_check = false -# XML files of various types [*.{aml,asax,ascx,asmx,asp,aspx,axml,config,content,cshtml,csproj,database,datasource,dbml,disco,discomap,dtsx,edmx,exclude,fxcop,htm,html,items,layout,maml,manifest,master,msha,myapp,nunit,nuspec,proj,projitems,props,publishproj,pubxml,rdl,rdlc,resx,ruleset,settings,shfbproj,shproj,sitemap,snippets,soap,svc,svcinfo,svcmap,targets,tasks,tokens,vbhtml,vbproj,vcxproj,vcxproj.filters,vsct,vsixmanifest,vstemplate,webinfo,wsdl,xaml,xamlcfg,xml,xsd,xsl,xslt,xsx}] +# XML files of various types indent_style = tab +indent_size = 2 +tab_width = 2 -# C#/VB settings [*.{cs,vb}] +# C#/VB settings indent_size = 4 +tab_width = 4 dotnet_sort_system_directives_first = true +# Naming rules +dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i + +dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.types_should_be_pascal_case.symbols = types +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case + +# Symbol specifications +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.interface.required_modifiers = + +dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.types.required_modifiers = + +dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method +dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.non_field_members.required_modifiers = + +# Naming styles +dotnet_naming_style.begins_with_i.required_prefix = I +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.capitalization = pascal_case + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case + +# Code style settings +dotnet_style_predefined_type_for_locals_parameters_members = true:none +dotnet_style_predefined_type_for_member_access = false:error +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_explicit_tuple_names = true:error +dotnet_style_null_propagation = true:suggestion +dotnet_style_object_initializer = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion +dotnet_style_prefer_auto_properties = true:silent +dotnet_style_prefer_simplified_boolean_expressions = true:suggestion +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +dotnet_style_prefer_conditional_expression_over_return = true:silent +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_compound_assignment = true:suggestion +dotnet_style_prefer_simplified_interpolation = true:suggestion +dotnet_style_namespace_match_folder = true:suggestion +dotnet_style_operator_placement_when_wrapping = beginning_of_line + # "This." and "Me." qualification. Use is preferred where true but the lightbulbs tend to show up in places # they aren't wanted (within static methods and nameof parameters etc.) so no suggestions are enabled. -dotnet_style_qualification_for_event = true : none -dotnet_style_qualification_for_field = false : none -dotnet_style_qualification_for_method = true : none -dotnet_style_qualification_for_property = true : none - -# Language keywords vs framework type names -dotnet_style_predefined_type_for_locals_parameters_members = true : none -dotnet_style_predefined_type_for_member_access = false : error - -# Modern language feature settings -dotnet_style_coalesce_expression = true : suggestion -dotnet_style_collection_initializer = true : suggestion -dotnet_style_explicit_tuple_names = true : error -dotnet_style_null_propagation = true : suggestion -dotnet_style_object_initializer = true : suggestion +dotnet_style_qualification_for_event = true:none +dotnet_style_qualification_for_field = false:none +dotnet_style_qualification_for_method = true:none +dotnet_style_qualification_for_property = true:none -# CSharp code style settings -[*.cs] -csharp_style_var_elsewhere = false : none -csharp_style_var_for_built_in_types = false : none -csharp_style_var_when_type_is_apparent = false : none - -csharp_style_expression_bodied_accessors = true : suggestion -csharp_style_expression_bodied_constructors = false : none -csharp_style_expression_bodied_indexers = true : suggestion -csharp_style_expression_bodied_methods = false : none -csharp_style_expression_bodied_operators = false : none -csharp_style_expression_bodied_properties = true : suggestion - -csharp_style_conditional_delegate_call = true : suggestion -csharp_style_deconstructed_variable_declaration = false : none -csharp_style_inlined_variable_declaration = true : suggestion -csharp_style_pattern_local_over_anonymous_function = true : suggestion -csharp_style_pattern_matching_over_as_with_null_check = true : suggestion -csharp_style_pattern_matching_over_is_with_cast_check = true : suggestion -csharp_style_throw_expression = true : suggestion - -csharp_prefer_simple_default_expression = true : suggestion +# Code analyzer settings +# CA1303: Do not pass literals as localized parameters +dotnet_diagnostic.CA1303.severity = none + +# IDE0010: Add missing cases +dotnet_diagnostic.IDE0010.severity = none + +# IDE0032: Use auto property +dotnet_diagnostic.IDE0032.severity = none + +# IDE0045: Convert to conditional expression +dotnet_diagnostic.IDE0045.severity = none + +# IDE0046: Convert to conditional expression +dotnet_diagnostic.IDE0046.severity = none + +# IDE0047: Remove unnecessary parentheses +dotnet_diagnostic.IDE0047.severity = none +# IDE0055: Fix formatting +dotnet_diagnostic.IDE0055.severity = none + +# IDE0058: Expression value is never used +dotnet_diagnostic.IDE0058.severity = none + +# IDE1006: Naming Styles +dotnet_diagnostic.IDE1006.severity = none + +[*.cs] +# CSharp code style settings +csharp_style_var_elsewhere = false:none +csharp_style_var_for_built_in_types = false:none +csharp_style_var_when_type_is_apparent = false:none + +csharp_style_expression_bodied_accessors = true:suggestion +csharp_style_expression_bodied_constructors = false:none +csharp_style_expression_bodied_indexers = true:suggestion +csharp_style_expression_bodied_methods = false:none +csharp_style_expression_bodied_operators = false:none +csharp_style_expression_bodied_properties = true:suggestion + +csharp_style_conditional_delegate_call = true:suggestion +csharp_style_deconstructed_variable_declaration = false:none +csharp_style_inlined_variable_declaration = true:suggestion +csharp_style_pattern_local_over_anonymous_function = true:suggestion +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_throw_expression = true:suggestion +csharp_style_namespace_declarations = block_scoped:silent +csharp_style_prefer_method_group_conversion = true:silent +csharp_style_prefer_top_level_statements = true:silent +csharp_style_expression_bodied_lambdas = true:silent +csharp_style_expression_bodied_local_functions = false:silent +csharp_style_prefer_null_check_over_type_check = true:suggestion +csharp_style_prefer_local_over_anonymous_function = true:suggestion +csharp_style_prefer_index_operator = true:suggestion +csharp_style_prefer_range_operator = true:suggestion +csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion +csharp_style_prefer_tuple_swap = true:suggestion +csharp_style_prefer_utf8_string_literals = true:suggestion +csharp_style_unused_value_assignment_preference = discard_variable:suggestion +csharp_style_unused_value_expression_statement_preference = discard_variable:silent + +csharp_prefer_braces = when_multiline:none +csharp_prefer_simple_default_expression = true:suggestion +csharp_prefer_simple_using_statement = true:suggestion csharp_preserve_single_line_blocks = true +csharp_using_directive_placement = outside_namespace:silent # Indentation options csharp_indent_block_contents = true csharp_indent_braces = false csharp_indent_case_contents = true +csharp_indent_labels = no_change # New line options csharp_new_line_before_catch = true @@ -109,31 +208,3 @@ csharp_space_between_method_call_parameter_list_parentheses = false csharp_space_between_method_declaration_empty_parameter_list_parentheses = false csharp_space_between_method_declaration_name_and_open_parenthesis = false csharp_space_between_method_declaration_parameter_list_parentheses = false - -# CA1303: Do not pass literals as localized parameters -dotnet_diagnostic.CA1303.severity = none - -# IDE0010: Add missing cases -dotnet_diagnostic.IDE0010.severity = none - -# IDE0032: Use auto property -dotnet_diagnostic.IDE0032.severity = none - -# IDE0045: Convert to conditional expression -dotnet_diagnostic.IDE0045.severity = none - -# IDE0046: Convert to conditional expression -dotnet_diagnostic.IDE0046.severity = none - -# IDE0047: Remove unnecessary parentheses -dotnet_diagnostic.IDE0047.severity = none - -# IDE0055: Fix formatting -dotnet_diagnostic.IDE0055.severity = none - -# IDE0058: Expression value is never used -dotnet_diagnostic.IDE0058.severity = none - -# IDE1006: Naming Styles -dotnet_diagnostic.IDE1006.severity = none - diff --git a/Docs/Content/CodeAnalyzer.aml b/Docs/Content/CodeAnalyzer.aml new file mode 100644 index 0000000..dd6ff46 --- /dev/null +++ b/Docs/Content/CodeAnalyzer.aml @@ -0,0 +1,45 @@ + + + + + The code analyzer in the spell checker extension is used to spell check identifiers in source code. +Only C# is supported right now but future support for Visual Basic and, if possible, F# are planned. Code +analyzer spell checking differs from the string and comments spell checker implemented using the tagger in the +editor. Identifiers are always split up into separate words on capital letters and other separators such as the +underscore. In addition, there are some extra options related to visibility and placement that can control when +spell checking of identifiers occurs. See the +configuration category topic for details. Due to the way code analyzers work, there are some limitations. See +the topic for more information. + + +
+ + The code analyzer contains two rules: VSSpell001 used to flag misspelled +words in identifiers and offer suggested fixes and VSSpell002 used to offer an option +to add the misspelling to an Ignore Spelling directive comment in the source code file. The first rule defaults +to a warning and will show the usual warning underline on the misspellings. The second rule is hidden by default +and only appears on the smart tag when it is opened. + + Place the mouse over the misspelled identifier and click the down arrow on the smart tag to see the +available options. Alternately, you can place the cursor anywhere in the identifier and press Ctrl+. or +Shift+Alt+F10 depending on your version of Visual Studio to show the smart tag options. Note that if an +identifier consists of multiple words and more than one is misspelled, each misspelled part is flagged and +corrected separately. + + Select the "Correct spelling of 'XXX'" option to see the suggested replacements. If the +misspelling represents only a part of the identifier, you will see the rest of the identifier in each suggestion. +Select a suggestion to get a preview of the changes that will occur if it is selected. To add the misspelling to +an Ignore spelling directive comment in the file, select the "Ignore word 'XXX'" option and a preview of the +directive will be displayed. The directive comments is placed at the top of the file below any header comments +and above the first directive, using statement, or namespace declaration. If a directive comment already exists +in that location, the new ignored word will be added to it. + +
+ + + + + + +
+
diff --git a/Docs/Content/ConfigOptions/CodeAnalysisDictionaries.aml b/Docs/Content/ConfigOptions/CodeAnalysisDictionaries.aml index adafbd3..ba8fba5 100644 --- a/Docs/Content/ConfigOptions/CodeAnalysisDictionaries.aml +++ b/Docs/Content/ConfigOptions/CodeAnalysisDictionaries.aml @@ -33,6 +33,10 @@ as shown in the example below. Import project code analysis dictionaries if present - This option is enabled by default and controls whether or not the code analysis dictionaries are imported. If disabled, all of the following options are ignored. + + +vsspell_cad_import_code_analysis_dictionaries = [true|false] + @@ -40,6 +44,10 @@ following options are ignored. will cause unrecognized words in the dictionary to be treated as misspelled words. Adding a SpellingAlternates attribute to the Word element allows you to specify a list of one or more comma-separated words to offer as suggested replacements. + + +vsspell_cad_treat_unrecognized_words_as_misspelled = [true|false] + @@ -47,6 +55,10 @@ specify a list of one or more comma-separated words to offer as suggested replac will cause deprecated terms in the dictionary to be treated as misspelled words. The preferred alternate is offered as the suggested replacement. If the preferred alternate is camel cased, spaces are inserted before each capital letter. + + +vsspell_cad_treat_deprecated_terms_as_misspelled = [true|false] + @@ -54,6 +66,10 @@ capital letter. cause compound terms in the dictionary to be treated as misspelled words. The compound alternate is offered as the suggested replacement. If the compound alternate is camel cased, spaces are inserted before each capital letter. + + +vsspell_cad_treat_compound_terms_as_misspelled = [true|false] + @@ -63,6 +79,10 @@ in all uppercase or camel case. Camel cased words are always ignored. All uppe Ignore words in all uppercase option in the General Settings category is enabled. This option may be of use if that option is disabled so that acronyms in all uppercase within this category are not spell checked. + + +vsspell_cad_treat_casing_exceptions_as_ignored_words = [true|false] + @@ -94,6 +114,10 @@ the word is not imported and will be spell checked in the normal manner. + + +vsspell_cad_recognized_word_handling = [None|IgnoreAllWords|AddAllWords|AttributeDeterminesUsage] + diff --git a/Docs/Content/ConfigOptions/CodeAnalyzerOptions.aml b/Docs/Content/ConfigOptions/CodeAnalyzerOptions.aml index 2c5ce7a..ce65410 100644 --- a/Docs/Content/ConfigOptions/CodeAnalyzerOptions.aml +++ b/Docs/Content/ConfigOptions/CodeAnalyzerOptions.aml @@ -17,17 +17,29 @@ into individual words and each word in the identifier will be spell checked. Ignore identifiers if private - If enabled, all identifiers with a visibility of private are ignored. + + +vsspell_code_analyzer_ignore_identifier_if_private = [true|false] + Ignore identifier if internal - If enabled, all identifiers with a visibility of internal are ignored. Note that protected internal identifiers will still be spell checked as the protected visibility will take precedence. + + +vsspell_code_analyzer_ignore_identifier_if_internal = [true|false] + Ignore identifier if all uppercase - If enabled, any identifier that consists only of uppercase letters will be ignored. + + +vsspell_code_analyzer_ignore_identifier_if_all_uppercase = [true|false] + @@ -36,6 +48,10 @@ of a member will be ignored (local variables in properties, methods, lambda expr useful in reducing the number of spelling errors reported since method bodies tend to contain more abbreviations and other shortened identifiers. Since these will never be publicly visible, it may be preferable to skip spell checking them. + + +vsspell_code_analyzer_ignore_identifiers_within_member_bodies = [true|false] + @@ -43,11 +59,19 @@ checking them. TKey and TValue will be ignored. If spell checked, the leading uppercase "T" in the examples would be skipped so it would only flag a spelling error if the remainder of the type parameter was misspelled. + + +vsspell_code_analyzer_ignore_type_parameters = [true|false] + Ignore compiler generated code - If enabled, all compiler generated code is skipped and will not be spell checked. + + +vsspell_code_analyzer_ignore_if_compiler_generated = [true|false] + @@ -60,34 +84,58 @@ code as applicable. Ignore XML documentation comments (/// ... and /** ... */) - If enabled, XML documentation comments will be excluded from spell checking. Unless disabled with the option below, standard delimited comments will still be included for spell checking. + + +vsspell_code_analyzer_ignore_xml_doc_comments = [true|false] + Ignore delimited comments (/* ... */) - This is useful for excluding private comments that nobody will see except for developers and commented out code. Unless disabled with the option above, delimited XML comments will still be included for spell checking. + + +vsspell_code_analyzer_ignore_delimited_comments = [true|false] + Ignore standard single line comments (// ...) - This is useful for excluding private comments that nobody will see except for developers and commented out code. + + +vsspell_code_analyzer_ignore_standard_single_line_comments = [true|false] + Ignore quadruple slash single line comments (//// ...) - This option is useful for ignoring commented out code using the method recommended by StyleCop so that it does not produce style warnings. + + +vsspell_code_analyzer_ignore_quadruple_slash_comments = [true|false] + Ignore normal string literals ("...") - This is useful for ignoring string literals in code if you use resource files for localized text. In such cases, the string literals are usually not valid words and enabling this option prevents the resource keys from showing up as spelling errors. + + +vsspell_code_analyzer_ignore_normal_strings = [true|false] + Ignore verbatim string literals (@"...") - Verbatim string literals typically contain such things as filenames or multi-line text that is not usually considered text that needs to be spell checked. Enabling this option prevents their content from being checked for spelling errors. + + +vsspell_code_analyzer_ignore_verbatim_strings = [true|false] + @@ -95,12 +143,30 @@ Enabling this option prevents their content from being checked for spelling erro skipping interpolated strings which may only contain property format specifiers. Enabling this option prevents their content from being checked for spelling errors. If left disabled, all string content except text within braces which denote format specifiers will be spell checked. + + +vsspell_code_analyzer_ignore_interpolated_strings = [true|false] + + + + + Ignore raw string literals ("""...""") - Verbatim string literals typically contain +such things as filenames or multi-line text that is not usually considered text that needs to be spell checked. +Enabling this option prevents their content from being checked for spelling errors. + + +vsspell_code_analyzer_ignore_raw_strings = [true|false] + Apply the above options to all C-style languages as applicable - If this option is enabled, the above options are applied to all C-style languages as applicable. For example, all of the comments options and the normal string literal option will be applied to C, C++, JavaScript, etc. + + +vsspell_code_analyzer_apply_to_all_c_style_languages = [true|false] + diff --git a/Docs/Content/ConfigOptions/ConfigOptions.aml b/Docs/Content/ConfigOptions/ConfigOptions.aml index 6b5ebaf..5aab739 100644 --- a/Docs/Content/ConfigOptions/ConfigOptions.aml +++ b/Docs/Content/ConfigOptions/ConfigOptions.aml @@ -1,6 +1,7 @@ + Configuration options for the spell checker are stored in .editorconfig files. There is one global configuration that contains the default settings. Spell checker options can also be stored in .editorconfig @@ -157,6 +158,48 @@ category to remove words from the user dictionary. +
+ Section IDs + + Several spell checker properties can be inherited across multiple sections and/or .editorconfig +files. To allow those settings to be inherited rather than replaced, their property names are given a unique +suffix within each section. The section_id property is used to define the unique ID for +each .editorconfig section when such properties appear in it. A GUID is used to guarantee unique values and one +will be generated by the configuration editor when needed. + + +[*] +# VSSPELL: Spell checker settings for all files +vsspell_section_id = 9f07c577adcd4fd7a93a42a503828225 +vsspell_ignored_words_9f07c577adcd4fd7a93a42a503828225 = File:IgnoredWords.dic +vsspell_exclusion_expressions_9f07c577adcd4fd7a93a42a503828225 = [a-z]{2}-([A-Z]{2}|Cyrl|Latn)(?@@PND@@/Options/None)\\\\\w+(?@@PND@@/Options/None) + +[*.resx] +# VSSPELL: Ignored resource file specific keywords +vsspell_section_id = 1C663502B9244D4DB52510C55DF2AB99 +vsspell_ignored_keywords_1C663502B9244D4DB52510C55DF2AB99 = microsoft|mimetype|mscorlib|resheader|resx|utf + +[*.{c,cc,cpp,cu,cuh,cxx,h,hh,hpp,hxx,ii,ipp,inl,rc,xpp}] +# VSSPELL: Ignored C/C++ language-specific keywords +vsspell_section_id = 2ED8EEE5E7BB4650A5DF3D504B15A4E5 +vsspell_ignored_keywords_2ED8EEE5E7BB4650A5DF3D504B15A4E5 = alignas|alignof|asm|assert|atomic|auto|... + + + A comment starting with # VSSPELL: can be added to the section to provide +comments on the spell checker settings. This comment is used by and can be updated using the configuration +editor. + + + A bug in Visual Studio 2022 and earlier and the code analyzers within them truncates property +values containing a pound sign (#) or semi-colon (;) as it incorrectly interprets them as a comment at the end +of the line which is no longer allowed per the .editorconfig specification. To work around this, the +configuration editor encodes those characters as @@PND@@ and @@SEMI@@ +if they occur within the value of an affected spell checker configuration property. An example can be seen +above in the exclusion expressions property. + + +
+ diff --git a/Docs/Content/ConfigOptions/DictionarySettings.aml b/Docs/Content/ConfigOptions/DictionarySettings.aml index c8b3e14..0886d8e 100644 --- a/Docs/Content/ConfigOptions/DictionarySettings.aml +++ b/Docs/Content/ConfigOptions/DictionarySettings.aml @@ -55,6 +55,15 @@ configurations are ignored. Likewise, if a solution configuration contains a us contains a user dictionary, the project user dictionary would override the solution-level one rather than being additive to it. + + +vsspell_additional_dictionary_folders_[sectionId] = [clear_inherited]|[path1|path2|...] + +sectionId = The unique ID for the section. +clear_inherited = If specified, clear all prior values and use only the settings in this property. If omitted, +prior values from other property instances are inherited. +path1|path2|... = A pipe-separated list of additional paths to use in finding dictionaries. + @@ -87,6 +96,15 @@ the selected languages will be used (French alone in the example above). cause additional memory usage. Only add extra languages when necessary. Consider using solution or project configurations rather than specifying them in the global configuration. + + +vsspell_dictionary_languages_[sectionId] = [langId1],[langId2]...,inherited,[langIdN]... + +sectionId = The unique ID for the section. +langId1,langId2...,langIdN = A comma-separated list of languages to use for spell checking (en-US, fr-FR, etc.) +inherited = If present, this indicates the point at which inherited languages are inserted into the list. +If omitted, languages from prior configurations are ignored. + @@ -102,6 +120,10 @@ available regardless of any other languages that have been selected. If this option is enabled, it will override all other dictionary languages. The determined language will be the only one used for spell checking the resource file. + + +vsspell_determine_resource_file_language_from_name = [true|false] + diff --git a/Docs/Content/ConfigOptions/ExclusionExpressions.aml b/Docs/Content/ConfigOptions/ExclusionExpressions.aml index 7a74436..86429f3 100644 --- a/Docs/Content/ConfigOptions/ExclusionExpressions.aml +++ b/Docs/Content/ConfigOptions/ExclusionExpressions.aml @@ -60,6 +60,20 @@ checking process, especially for spell checking as you type. It may be better t particular type of project within that project's solution or project spell checking configuration file rather than putting all of them in the global configuration. + + +vsspell_exclusion_expressions_[sectionId] = [clear_inherited(?@@PND@@/Options/)][regEx1(?@@PND@@/Options/)]... + +Note: Because expressions may contain any given character, the regular expressions are separated by their +options information which is included in the expression as a comment. Due to a Visual Studio bug, # and ; are +encoded as @@PND@@ and @@SEMI@@. + +sectionId = The unique ID for the section. +clear_inherited = If specified, clear all prior values and use only the settings in this property. If omitted, +prior values from other property instances are inherited. +regEx1(?@@PND@@/Options/)... = One or more regular expressions to use and their options. + + diff --git a/Docs/Content/ConfigOptions/GeneralSettings.aml b/Docs/Content/ConfigOptions/GeneralSettings.aml index 4510614..5768ccd 100644 --- a/Docs/Content/ConfigOptions/GeneralSettings.aml +++ b/Docs/Content/ConfigOptions/GeneralSettings.aml @@ -13,6 +13,10 @@ Spell check as you type - This enables and disables the spell checker. Naturally, it is enabled by default. The spell checker tool window relies on this being enabled. If disabled, spell checking will be unavailable until it is turned back on. + + +vsspell_spell_check_as_you_type = [true|false] + @@ -22,6 +26,10 @@ it will include the related files in the solution/project spell checking process files will be excluded from the process. For example, this offers a convenient way of excluding an entire folder of files from being spell checked as part of the solution/project as it does with the option above for interactive spell checking. + + +vsspell_include_in_project_spell_check = [true|false] + @@ -30,22 +38,38 @@ spell checking code analyzer and is enabled by default. It will spell check ide languages (currently only C# but support for Visual Basic is planned for later). Unlike comment and string misspellings, identifier misspellings will be highlighted using the same colored underline as other code fixes. Fixing a misspelling in an identifier will automatically correct it in all other references to it. + + +vsspell_code_analyzers_enabled = [true|false] + Detect doubled words - This will cause the spell checker to report instances of doubled words. An option to remove the duplicate word or ignore it will be offered as the options to correct any instances that are found. The default is enabled. + + +vsspell_detect_doubled_words = [true|false] + Ignore words with digits - This will cause the spell checker to ignore any words containing digits. The default is enabled. + + +vsspell_ignore_words_with_digits = [true|false] + Ignore words in all uppercase - This will cause the spell checker to ignore any words consisting of all uppercase letters. The default is enabled. + + +vsspell_ignore_words_in_all_uppercase = [true|false] + @@ -53,13 +77,17 @@ consisting of all uppercase letters. The default is enabled. consisting of mixed/camel case letters. The default is enabled. Note that if disabled, it may result in a large number of false reports of misspellings especially on control prefixes such as btn and txt. These can be added as ignored words to suppress them. Disabling this option may -also result in false reports of doubled words where the one of the words is part of a mixed case word preceded +also result in false reports of doubled words where one of the words is part of a mixed case word preceded or followed by another occurrence of the same word. Mixed case words that also include an underscore, period, or at sign will not be included for spell checking even if this option is disabled based on the option related to the special word break character's setting (treat underscores as separators is disabled, ignore words that look like filenames/e-mail addresses is enabled) + + +vsspell_ignore_words_in_mixed_case = [true|false] + @@ -67,6 +95,10 @@ is enabled) to skip text in .NET and C-style format specifiers (i.e. {0:MM/dd/yyyy} and %ld). This prevents the text within them from showing up as misspelled words. The default is enabled. + + +vsspell_ignore_format_specifiers = [true|false] + @@ -75,6 +107,10 @@ checker to ignore words that contain periods and at signs with no intervening wh Userinfo.config or auser@mydomain.com). This option can occasionally cause a misspelled word to be missed such as when a space is missing following the period in a sentence. However, it excludes far more false reports and is enabled by default. + + +vsspell_ignore_filenames_and_email_addresses = [true|false] + @@ -83,12 +119,20 @@ spell checker to ignore words within angle brackets in spell checked text (i.e. element creates a paragraph"). This option can occasionally cause a misspelled word to be missed such as when a space is missing following the opening angle bracket. However, it excludes far more false reports and is enabled by default. + + +vsspell_ignore_xml_elements_in_text = [true|false] + Treat underscores as separators - This option is disabled by default and all words containing underscores will be ignored. Enabling this option will treat the underscore as a word separator and each word separated by the underscores will be spell checked along with all the other text. + + +vsspell_treat_underscore_as_separator = [true|false] + @@ -100,6 +144,10 @@ reporting them as misspellings. If a misspelled word contains a mnemonic, it will be reported with the mnemonic and each suggested replacement will have a matching mnemonic if it contains a matching letter. This allows you to see where the mnemonic is placed or if it will be lost in the replacement. + + +vsspell_ignore_mnemonics = [true|false] + @@ -115,6 +163,10 @@ default is Include all words so that all words are included regardless It can be set to Ignore words containing non-Latin characters to ignore words containing characters above 0xFF or it can be set to Ignore words containing non-ASCII characters to ignore words containing characters above 0x7F. + + +vsspell_ignored_character_class = [None|NonLatin|NonAscii] + diff --git a/Docs/Content/ConfigOptions/IgnoredClassifications.aml b/Docs/Content/ConfigOptions/IgnoredClassifications.aml index 08dd733..128bb18 100644 --- a/Docs/Content/ConfigOptions/IgnoredClassifications.aml +++ b/Docs/Content/ConfigOptions/IgnoredClassifications.aml @@ -45,6 +45,24 @@ next to each classification that you want to exclude or uncheck them to include shown are the internal ones from the language classifiers and vary from language to language. Many are self-explanatory but others may not be. You may need to experiment with the classifications to determine which ones to use to exclude the elements you are interested in ignoring. + + +vsspell_ignored_classifications_[sectionId] = [clear_inherited], +[classificationType|classification1|classification2|...], +[File Type: fileType|classification1|classification2|...], +[Extension: extension|classification1|classification2|...] + +sectionId = The unique ID for the section +clear_inherited = If specified, clear all prior values and use only the settings in this property. If omitted, +prior values from other property instances are inherited. +classificationType|classification1|classification2|... = A classifier type and the classifications to ignore. +The values are separated by pipes. +File Type: fileType|classification1|classification2|... = A set of classifications for a specific file type to +ignore. The values are separated by pipes. +Extension: extension|classification1|classification2|... = A set of classifications for a specific file extension +to ignore. The values are separated by pipes. + + diff --git a/Docs/Content/ConfigOptions/IgnoredWords.aml b/Docs/Content/ConfigOptions/IgnoredWords.aml index bb2f4cc..cbb5c4c 100644 --- a/Docs/Content/ConfigOptions/IgnoredWords.aml +++ b/Docs/Content/ConfigOptions/IgnoredWords.aml @@ -63,6 +63,17 @@ importing or exporting words, you will be asked whether you want to replace the the existing words. Escaped words (those starting with a backslash) will not be exported to XML user dictionary files as they do not support escaped words. + +vsspell_ignored_words_[sectionId] = [clear_inherited]|[File:ignoredWordsFilePath]|[word1|word2|...] + +sectionId = The unique ID for the section. +clear_inherited = If specified, clear all prior values and use only the settings in this property. If omitted, +prior values from other property instances are inherited. +File:ignoredWordsFilePath = If a value is prefixed with "File:", the value after the colon is assumed to be a +path to the ignored words file. +word1|word2|... = A pipe-separated list of words to ignore. + + @@ -74,7 +85,14 @@ ignored keywords file counterpart though. As noted above, ignored keywords are configuration files and cannot be cleared by a later configuration file. They are typically defined in the global configuration file for the file set to which they apply. The default configuration contains several sets of ignored keywords for various languages. - + + +vsspell_ignored_keywords_[sectionID] = [keyword1|keyword2|...] + +sectionId = The unique ID for the section. +keyword1|keyword2|... = A pipe-separated list of keywords to ignore. + + diff --git a/Docs/Content/ConfigOptions/ImportSettings.aml b/Docs/Content/ConfigOptions/ImportSettings.aml index acf2f99..bb7862d 100644 --- a/Docs/Content/ConfigOptions/ImportSettings.aml +++ b/Docs/Content/ConfigOptions/ImportSettings.aml @@ -33,6 +33,12 @@ the containing file will override settings from the imported file. This allows base configuration settings and then selectively override them in the containing configuration file. + +vsspell_import_settings_file_[sectionId] = [pathToImportedSettingsFile] + +sectionId = The unique ID for the section. +pathToImportedSettingsFile = The path to the settings file to import. + diff --git a/Docs/Content/ConfigOptions/VisualStudioWPFTextBoxes.aml b/Docs/Content/ConfigOptions/VisualStudioWPFTextBoxes.aml index 9d3b5b4..e899e10 100644 --- a/Docs/Content/ConfigOptions/VisualStudioWPFTextBoxes.aml +++ b/Docs/Content/ConfigOptions/VisualStudioWPFTextBoxes.aml @@ -32,6 +32,13 @@ button or double click it. The same dialog box will appear in which you can mod remove an expression, select it in the list and click the Remove button. To reset the list to the default set of ignored Visual Studio control IDs, click the Default button. + +vsspell_enable_wpf_text_box_spell_checking = [true|false] +vsspell_visual_studio_id_exclusions = [regEx1(?@@PND@@/Options/)]... + +These option only have an effect in the global spell checker configuration file. +regEx1(?@@PND@@/Options/)... = One or more regular expressions to use and their options. + diff --git a/Docs/Content/ConfigOptions/XMLFiles.aml b/Docs/Content/ConfigOptions/XMLFiles.aml index 5b6692a..c19dedd 100644 --- a/Docs/Content/ConfigOptions/XMLFiles.aml +++ b/Docs/Content/ConfigOptions/XMLFiles.aml @@ -14,12 +14,30 @@ false reports when working with XML files. The options are divided into two sec Ignored XML Elements - This section lets you manage the list of ignored XML elements. If an element name appears in this list, its inner text will be ignored when spell checking is performed. All other elements not in the list will have their inner text spell checked. + + +vsspell_ignored_xml_elements_[sectionId] = [clear_inherited],[element1,element2,...] + +sectionId = The unique ID for the section +clear_inherited = If specified, clear all prior values and use only the settings in this property. If omitted, +prior values from other property instances are inherited. +element1,element2,... = A comma-separated list of XML element names to ignore. + Spell Checked Attributes - This section lets you manage the list of attribute names that will have their values spell checked. The values for all other attributes not in the list will be ignored. + + +vsspell_spell_checked_xml_attributes_[sectionId] = [clear_inherited],[attribute1,attribute2,...] + +sectionId = The unique ID for the section +clear_inherited = If specified, clear all prior values and use only the settings in this property. If omitted, +prior values from other property instances are inherited. +attribute1,attribute2,... = A comma-separated list of XML attribute names to spell check. + diff --git a/Docs/Content/ConvertingToEditorConfig.aml b/Docs/Content/ConvertingToEditorConfig.aml index 29e306b..66d1627 100644 --- a/Docs/Content/ConvertingToEditorConfig.aml +++ b/Docs/Content/ConvertingToEditorConfig.aml @@ -2,7 +2,7 @@ - Starting with version YYYY.MM.DD.0, configuration settings have been moved from the old XML + Starting with version 2023.5.8.0, configuration settings have been moved from the old XML .vsspell configuration files to .editorconfig settings. This is a significant change from prior versions but has the following benefits: diff --git a/Docs/Content/KnownIssues.aml b/Docs/Content/KnownIssues.aml index 7c05d97..e1f66f6 100644 --- a/Docs/Content/KnownIssues.aml +++ b/Docs/Content/KnownIssues.aml @@ -6,41 +6,60 @@
+ Code Analyzers - - - Spell check as you type and solution/project spell checking only apply to non-source code files -and only to strings and comments within source code files. If enabled, identifier spell checking is handled by -the code analyzer. Such misspellings will not appear in the Spell Check Active Document or Solution/Project -Spell Check tool windows. - + The code analyzers only apply to identifiers in supported languages. Identifier misspellings +will not appear in the Spell Check Active Document or Solution/Project Spell Check tool windows. + The code analyzer runs in a separate process. As such, it may not always pick up new words -added to the dictionaries. It may be necessary to close and reopen the solution in order to get it to pick up -new dictionary words. Ignored words will typically be detected as the configuration used by the code analyzer -is refreshed quite often. However, changes made to the global configuration may not be reflected until a change -is made to the source code of any open editors. +added to the dictionaries or ignored words files by the spell check as you type option and solution/project +spell checking until a change is made to the source code of an open editor. Likewise, changes made to the global +and/or local project configurations may not be reflected until a change is made to the source code of an open +editor. The code analyzer is only able to correct the spelling on single part namespaces (e.g. namespace SinglePartNamespace) or the last part of multi-part namespaces (e.g. namespace FirstPart.MiddlePart.LastPart). For parts prior to the last part, -misspellings will be flagged and suggestions offered by you will need to manually correct the spelling and apply +misspellings will be flagged and suggestions offered but you will need to manually correct the spelling and apply the refactoring code fix to change the name throughout the code base if necessary. This appears to be a limitation with symbol renaming and I was unable to find a workaround. If anyone with more knowledge wants to fix it, feel free to do so and submit a pull request with the changes. - Code analyzers are designed to work with source code. Unfortunately, the do not work well for -updating non-code files or files outside of the project. As such, there are no fixes to allow adding a word -in a misspelled identifier to ignored words files in the solution or project or the global configuration nor to -the dictionary itself. If you want a word flagged by the code analyzer added to either of those files, you must -do so manually or through the spell checker configuration editor. + Correcting a misspelling on an identifier will correct it throughout the code base wherever the +containing identifier is used. However, there are no provisions for correcting a common misspelling in multiple, +different identifiers (replace all). Each must be corrected individually. + + + + Code analyzers are, naturally, designed to work with source code. Unfortunately, they do not +work well for updating non-code files or files outside of the project. There are some provisions for updating +non-code files as long as they are added to the additional file set used by code analyzers but that requires some +extra steps to set up properly. Files in the solution items or outside of the solution entirely such as those +specified in the global spell checker configuration are inaccessible. As such, there are no fixes offered to +allow adding a word in a misspelled identifier to ignored words files in the solution or project or the global +configuration nor to the dictionary itself. If you want a word flagged by the code analyzer added to either of +those files, you must do so manually or through the spell checker configuration editor. Again, this limitation +may be due to my lack of knowledge on code analyzers so feel free to offer an update with this functionality if +you can provide one. + + +
+ +
+ Spell Check as You Type and Solution/Project Spell Checking + + Spell check as you type and solution/project spell checking applies to comments and strings within +source code and for the text in all non-source code files. + + The Spell Check Active Document tool window relies on the Spell check as you type configuration option being enabled. If disabled, spell checking will be unavailable until it is turned back on. @@ -76,7 +95,7 @@ line can be ignored due to the way the tagger is implemented. - On a similar note to the item above, in other languages such as VB.NET, the content of + On a similar note to the item above, in other languages such as Visual Basic, the content of code elements will typically not be spell checked unless you make a change on a particular line within it. Moving the cursor away from the edited line will usually clear any issues on it. Again, this is due to the way the tagger is implemented. As noted, such elements are always ignored as expected diff --git a/Docs/Content/SystemRequirements.aml b/Docs/Content/SystemRequirements.aml index 4238318..e232f08 100644 --- a/Docs/Content/SystemRequirements.aml +++ b/Docs/Content/SystemRequirements.aml @@ -45,16 +45,17 @@ and all related files) generated by building the projects. Building and Debugging the Projects To build the projects, open the solution file (*.sln) found in the -Source\ folder and build it. You can also run the MasterBuild.bat -script from a command prompt to build the projects. The projects should be built at least once in Visual -Studio so that all of the necessary NuGet packages are restored and available for the command line build. +Source\ folder, set the proper configuration based on the version of Visual Studio being +used and the configuration you want to build (Debug2019, Release2019, +Debug2022, or Release2022). In order to debug the projects: - Set the VSSpellChecker project as the default project and then open its -project properties. + Set the VSSpellCheckerDefinitions2017And2019 project (Visual Studio 2019) +or the VSSpellCheckerDefinitions2022AndLater project (Visual Studio 2022) as the default +project and then open its project properties. @@ -65,7 +66,7 @@ project properties. For the Start Action option, set it to Start external program, click the "..." button after the text box, navigate to the installation folder for your version of Visual Studio and select the devenv.exe file in that folder (i.e. -C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\Common7\IDE\devenv.exe). +C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\IDE\devenv.exe). diff --git a/Docs/Content/VSSpell001.aml b/Docs/Content/VSSpell001.aml new file mode 100644 index 0000000..d9a16f2 --- /dev/null +++ b/Docs/Content/VSSpell001.aml @@ -0,0 +1,97 @@ + + + + + + + + + Item + + + Value + + + + + + Rule ID + + + VSSpell001 + + + + + Category + + + Naming + + + + + Fix is breaking or non-breaking + + + Correcting the spelling in the identifier of a namespace or a public or protected type or +member is considered a breaking change. For internal and private types and members, the change is non-breaking. + + +
+
+ +
+ Cause + + One or more parts of an identifier are possibly misspelled. + +
+ +
+ Rule Description + + Code element spell check. + +
+ +
+ How to Fix Violations + + Select a suggested replacement to correct the spelling. + +
+ +
+ When to Suppress Warnings + + The warning can be suppressed if the word is correctly spelled but not in the dictionary by adding +the word to the dictionary. If misspelled but you do not want it flagged, it can be added to an ignored words +file defined in the spell checker configuration settings or to an Ignore Spelling directive comment in the source +code file. + +
+ +
+ Configure Code to Analyze + + See the configuration category topic for +information on the available options. + +
+ +
+ Related Rules + + + + + +
+ + + + + +
+
diff --git a/Docs/Content/VSSpell002.aml b/Docs/Content/VSSpell002.aml new file mode 100644 index 0000000..8561408 --- /dev/null +++ b/Docs/Content/VSSpell002.aml @@ -0,0 +1,94 @@ + + + + + + + + + Item + + + Value + + + + + + Rule ID + + + VSSpell002 + + + + + Category + + + Naming + + + + + Fix is breaking or non-breaking + + + Non-breaking + + +
+
+ +
+ Cause + + One or more parts of an identifier are possibly misspelled. + +
+ +
+ Rule Description + + Code element spell check. + +
+ +
+ How to Fix Violations + + Select the option to add the word to an Ignore Spelling directive comment in the source code file. +As an alternative, it can be added to an ignored words file defined in the spell checker configuration settings. + +
+ +
+ When to Suppress Warnings + + Not applicable. + +
+ +
+ Configure Code to Analyze + + See the configuration category topic for +information on the available options. + +
+ +
+ Related Rules + + + + + +
+ + + + + +
+
diff --git a/Docs/Content/VersionHistory/vNext.aml b/Docs/Content/VersionHistory/v2023.5.8.0.aml similarity index 87% rename from Docs/Content/VersionHistory/vNext.aml rename to Docs/Content/VersionHistory/v2023.5.8.0.aml index 5a2472d..524365e 100644 --- a/Docs/Content/VersionHistory/vNext.aml +++ b/Docs/Content/VersionHistory/v2023.5.8.0.aml @@ -51,10 +51,11 @@ etc.) The options above only apply to the code analyzer for fixing identifiers and only in C# code right now. Strings and comments are still handled via the taggers as in prior releases and will work across all -languages. The code fix will offer suggestions to correct the spelling of identifiers. Options to ignore an -identifier misspelling or add it to the dictionary are planned for a future release if I can figure out how to do -it. For now, they can be added to the ignored words file directly by editing it or to the dictionary through the -.editorconfig file on the Dictionary Settings page. +languages. The code fix will offer suggestions to correct the spelling of identifiers or add misspellings to an +Ignore Spelling directive comment in the source code file. Options to add it to the dictionary or an ignored +words file are planned for a future release if I can figure out how to do it. For now, they can be added to the +ignored words file or dictionary file directly by editing them. The location of the user dictionary can be +found via the settings file on the Dictionary Settings page. See the topic for some information on issues and limitations with the code analyzer. diff --git a/Docs/ContentLayout.content b/Docs/ContentLayout.content index dc9b373..745df12 100644 --- a/Docs/ContentLayout.content +++ b/Docs/ContentLayout.content @@ -51,6 +51,21 @@ + + + + + + + + + + + + + + + @@ -149,9 +164,9 @@ - + - + @@ -174,7 +189,7 @@ - + diff --git a/Docs/VSSpellCheckerDocs.shfbproj b/Docs/VSSpellCheckerDocs.shfbproj index 7913912..c2a4d5d 100644 --- a/Docs/VSSpellCheckerDocs.shfbproj +++ b/Docs/VSSpellCheckerDocs.shfbproj @@ -159,6 +159,7 @@ + @@ -220,7 +221,9 @@ - + + + diff --git a/IgnoredWords.dic b/IgnoredWords.dic index 21a5f6c..89facc9 100644 --- a/IgnoredWords.dic +++ b/IgnoredWords.dic @@ -3,6 +3,7 @@ arn Beňo blackspikeltd csharp +Cyrl Essilfie Gaisie Golovin @@ -14,14 +15,17 @@ hunspells jquery Kamecke Kristensen +Latn Lehenbauer microsoft Miloslav mimetype +mscorlib php plaintext Resharper resheader resx Ruhmann +utf xml diff --git a/README.md b/README.md index 4bb4c13..7857993 100644 --- a/README.md +++ b/README.md @@ -7,9 +7,11 @@ Welcome to the **Visual Studio Spell Checker** project. * [Contributing](https://ewsoftware.github.io/VSSpellChecker/html/847a2b53-6583-4198-80ef-0e537346e4a3.htm) This project is a Visual Studio editor extension that checks the spelling of comments, strings, and plain text -as you type or interactively with a tool window. It is based largely on the spell checker extension originally -created by Noah Richards, Roman Golovin, and Michael Lehenbauer. This version has been extended significantly to -include many new features all of which are configurable. +as you type or interactively with a tool window. It also contains a code analyzer that will spell check +identifiers in C# source code. It can spell check an entire solution, project, or selected items. It is based +largely on the spell checker extension originally created by Noah Richards, Roman Golovin, and Michael +Lehenbauer. This version has been extended significantly to include many new features all of which are +configurable. _**NOTE: The master branch is for Visual Studio 2017 and later. For Visual Studio 2013/2015, use the VSLegacy branch.**_ diff --git a/Source/SpellCheckCodeAnalyzer.CodeFixes2022AndLater/CodeFixResources.Designer.cs b/Source/SpellCheckCodeAnalyzer.CodeFixes2022AndLater/CodeFixResources.Designer.cs index c1bfea5..38fb040 100644 --- a/Source/SpellCheckCodeAnalyzer.CodeFixes2022AndLater/CodeFixResources.Designer.cs +++ b/Source/SpellCheckCodeAnalyzer.CodeFixes2022AndLater/CodeFixResources.Designer.cs @@ -60,12 +60,30 @@ internal CodeFixResources() { } } + /// + /// Looks up a localized string similar to Ignore Word. + /// + internal static string IgnoreWordCodeFixTitle { + get { + return ResourceManager.GetString("IgnoreWordCodeFixTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to (No Suggestions). + /// + internal static string NoSuggestions { + get { + return ResourceManager.GetString("NoSuggestions", resourceCulture); + } + } + /// /// Looks up a localized string similar to Spell Check. /// - internal static string CodeFixTitle { + internal static string SpellCheckCodeFixTitle { get { - return ResourceManager.GetString("CodeFixTitle", resourceCulture); + return ResourceManager.GetString("SpellCheckCodeFixTitle", resourceCulture); } } } diff --git a/Source/SpellCheckCodeAnalyzer.CodeFixes2022AndLater/CodeFixResources.resx b/Source/SpellCheckCodeAnalyzer.CodeFixes2022AndLater/CodeFixResources.resx index d2248f9..c85db7b 100644 --- a/Source/SpellCheckCodeAnalyzer.CodeFixes2022AndLater/CodeFixResources.resx +++ b/Source/SpellCheckCodeAnalyzer.CodeFixes2022AndLater/CodeFixResources.resx @@ -117,8 +117,16 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + + Ignore Word + The title of the code fix. + + + (No Suggestions) + No suggestions title. + + Spell Check The title of the code fix. - \ No newline at end of file + diff --git a/Source/SpellCheckCodeAnalyzer.CodeFixes2022AndLater/SpellCheckCodeFixProvider.cs b/Source/SpellCheckCodeAnalyzer.CodeFixes2022AndLater/SpellCheckCodeFixProvider.cs index ca05961..a55cc4f 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 : 04/30/2023 +// Updated : 05/06/2023 // Note : Copyright 2023, Eric Woodruff, All rights reserved // // This file contains a class used to provide the spell check code fixes @@ -29,23 +29,26 @@ using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Rename; using VisualStudio.SpellChecker.CodeAnalyzer; namespace VisualStudio.SpellChecker.CodeFixes { + // TODO: Separate code fixes for VB and F# are likely needed since they uses different SyntaxNode types among + // other things. + /// /// This is used to provide the spell check code fixes /// - // TODO: Separate code fixes for VB and F# are likely needed since they uses different SyntaxNode types among - // other things. [ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(SpellCheckCodeFixProvider)), Shared] public class SpellCheckCodeFixProvider : CodeFixProvider { /// public sealed override ImmutableArray FixableDiagnosticIds => ImmutableArray.Create( - CSharpSpellCheckCodeAnalyzer.SpellingDiagnosticId); + CSharpSpellCheckCodeAnalyzer.SpellingDiagnosticId, + CSharpSpellCheckCodeAnalyzer.IgnoreWordDiagnosticId); /// /// This fix provider does not use a fix all provider @@ -66,45 +69,56 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var diagnosticSpan = diagnostic.Location.SourceSpan; - // Find the identifier for the diagnostic - var syntaxToken = root.FindToken(diagnosticSpan.Start); - - if(diagnostic.Properties.TryGetValue("Suggestions", out string suggestions)) + if(diagnostic.Id == CSharpSpellCheckCodeAnalyzer.SpellingDiagnosticId) { - // 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); + // Find the identifier for the diagnostic + var syntaxToken = root.FindToken(diagnosticSpan.Start); - ImmutableArray replacements; - - if(!String.IsNullOrWhiteSpace(suggestions)) - { - replacements = suggestions.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select( - s => - { - string replacement = s; - - if(!String.IsNullOrWhiteSpace(prefix) || !String.IsNullOrWhiteSpace(suffix)) - replacement = prefix + replacement + suffix; - - return CodeAction.Create(replacement, c => CorrectSpellingAsync(context.Document, syntaxToken, - replacement, c), nameof(CodeFixResources.CodeFixTitle)); - - }).ToImmutableArray(); - } - else + if(diagnostic.Properties.TryGetValue("Suggestions", out string suggestions)) { - replacements = new[] { CodeAction.Create("(No Suggestions)", - c => CorrectSpellingAsync(context.Document, syntaxToken, null, c), - nameof(CodeFixResources.CodeFixTitle)) }.ToImmutableArray(); + // 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); + + ImmutableArray replacements; + + if(!String.IsNullOrWhiteSpace(suggestions)) + { + replacements = suggestions.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select( + s => + { + string replacement = (String.IsNullOrWhiteSpace(prefix) && + String.IsNullOrWhiteSpace(suffix)) ? s : prefix + s + suffix; + + return CodeAction.Create(replacement, + c => CorrectSpellingAsync(context.Document, syntaxToken, replacement, c), + replacement); + + }).ToImmutableArray(); + } + else + { + replacements = new[] { CodeAction.Create(CodeFixResources.NoSuggestions, + c => CorrectSpellingAsync(context.Document, syntaxToken, null, c), + nameof(CodeFixResources.NoSuggestions)) }.ToImmutableArray(); + } +#pragma warning disable RS1010 + // Register a code action that will invoke the fix and offer the various suggested replacements + context.RegisterCodeFix(CodeAction.Create(diagnostic.Descriptor.MessageFormat.ToString(null), + replacements, false), diagnostic); +#pragma warning restore RS1010 } + } + else + { + // Offer the option to ignore the word by adding it to an Ignore Spelling directive comment. + // Support for modifying non-code files is limited and nonexistent for files outside of the + // project as far as I can tell so this is the best we can do. + string ignoredWord = root.GetText().GetSubText(diagnosticSpan).ToString(); - // Register a code action that will invoke the fix and offer the various suggested replacements -#pragma warning disable RS1010 context.RegisterCodeFix(CodeAction.Create(diagnostic.Descriptor.MessageFormat.ToString(null), - replacements, false), diagnostic); -#pragma warning restore RS1010 + c => IgnoreWordAsync(context.Document, ignoredWord, c), ignoredWord), diagnostic); } } } @@ -260,13 +274,14 @@ private static async Task CorrectSpellingAsync(Document document, Synt break; case IdentifierNameSyntax _: - // TODO: Renaming namespace parts needs some work. It's fine for the last part but renaming - // an earlier part puts the change on the end (e.g. If renaming "Namespace1" in - // Root.Namespace1.SubNamespace it becomes Root.Namespace1.NewNamespace rather than - // Root.NewNamespace.SubNamespace). Not sure how to fix that yet. As such, we'll limit it - // to only allowing fixing the misspelling on single namespaces or the last part of - // multi-part namespaces. For others, it will show suggestions but won't allow a change - // and it will have to be fixed manually. + // Renaming namespace parts needs some work. It's fine for the last part but renaming + // an earlier part puts the change on the end (e.g. If renaming "OldNamespace" in + // Root.OldNamespace.SubNamespace it becomes Root.OldNamespace.NewNamespace rather than + // Root.NewNamespace.SubNamespace). I'm not sure if this is a limitation of the code fix + // API or my limited understanding of how they work. As such, for now we'll limit it to + // only allowing fixing the misspelling on single namespaces or the last part of multi-part + // namespaces. For others, it will show suggestions but won't allow a change and it will + // have to be fixed manually. if(token.Parent?.Parent is NamespaceDeclarationSyntax singleNamespace) symbol = semanticModel.GetDeclaredSymbol(singleNamespace, cancellationToken); else @@ -314,5 +329,110 @@ private static async Task CorrectSpellingAsync(Document document, Synt #endif return null; } + + /// + /// Create the solution used to ignore a misspelled word + /// + /// The document containing the misspelling + /// The ignored word syntax token + /// A cancellation token + /// The solution task used to add the ignored word to the Ignore Spelling directive + private static async Task IgnoreWordAsync(Document document, string ignoredWord, + CancellationToken cancellationToken) + { + if(document == null || ignoredWord == null) + return null; + + var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + + // TODO: This will vary by language + string newLineText = document.Project.Solution.Workspace.Options.GetOption(FormattingOptions.NewLine, + LanguageNames.CSharp); + char commentChar = '/'; + + var newLineTrivia = SyntaxFactory.EndOfLine(newLineText); + var newLeadingTrivia = SyntaxFactory.ParseLeadingTrivia(String.Empty); + + // We'll try to insert the directive after any leading comments and whitespace so add any existing + // leading trivia around the directive comment. + SyntaxTriviaList leadingTrivia = root.GetLeadingTrivia(); + string existingComment = null; + int triviaIdx = 0, existingCommentIdx = -1, lineCount = 0, blankLineCount = 0; + bool done = false; + + while(triviaIdx < leadingTrivia.Count && !done) + { + switch(leadingTrivia[triviaIdx].Kind()) + { + case SyntaxKind.EndOfLineTrivia: + triviaIdx++; + lineCount++; + break; + + case SyntaxKind.WhitespaceTrivia: + case SyntaxKind.MultiLineCommentTrivia: + triviaIdx++; + lineCount = 0; + break; + + case SyntaxKind.SingleLineCommentTrivia: + existingComment = leadingTrivia[triviaIdx].ToString(); + blankLineCount = lineCount; + + int skipIdx = 0; + + while(skipIdx < existingComment.Length && (existingComment[skipIdx] == commentChar || + Char.IsWhiteSpace(existingComment[skipIdx]))) + { + skipIdx++; + } + + if(skipIdx < existingComment.Length && existingComment.Substring(skipIdx).StartsWith( + "Ignore Spelling:", StringComparison.OrdinalIgnoreCase)) + { + existingCommentIdx = triviaIdx; + triviaIdx--; + done = true; + } + else + triviaIdx++; + break; + + default: + triviaIdx--; + done = true; + break; + } + } + + if(triviaIdx > 0) + { + newLeadingTrivia = newLeadingTrivia.AddRange(leadingTrivia.Take(triviaIdx + 1)); + + if(blankLineCount == 0) + newLeadingTrivia = newLeadingTrivia.Add(newLineTrivia); + } + + if(existingCommentIdx != -1) + { + // No need for a new line here as there should already be new line trivia after it + newLeadingTrivia = newLeadingTrivia.AddRange(SyntaxFactory.ParseLeadingTrivia( + existingComment + " " + ignoredWord)); + triviaIdx++; + } + else + { + newLeadingTrivia = newLeadingTrivia.AddRange(SyntaxFactory.ParseLeadingTrivia( + $"// Ignore Spelling: {ignoredWord}{newLineText}")); + + if(triviaIdx + 1 >= leadingTrivia.Count || !leadingTrivia[triviaIdx + 1].IsKind(SyntaxKind.EndOfLineTrivia)) + newLeadingTrivia = newLeadingTrivia.Add(newLineTrivia); + } + + if(triviaIdx < leadingTrivia.Count) + newLeadingTrivia = newLeadingTrivia.AddRange(leadingTrivia.Skip(triviaIdx + 1)); + + return document.WithSyntaxRoot(root.WithLeadingTrivia(newLeadingTrivia)); + } } } diff --git a/Source/SpellCheckCodeAnalyzer2022AndLater/AnalyzerReleases.Unshipped.md b/Source/SpellCheckCodeAnalyzer2022AndLater/AnalyzerReleases.Unshipped.md index 482c441..5769e30 100644 --- a/Source/SpellCheckCodeAnalyzer2022AndLater/AnalyzerReleases.Unshipped.md +++ b/Source/SpellCheckCodeAnalyzer2022AndLater/AnalyzerReleases.Unshipped.md @@ -2,7 +2,8 @@ Rule ID | Category | Severity | Notes --------|----------|----------|-------------------- -VSSpell001 | Naming | Warning | SpellCheckCodeAnalyzer, [Documentation](VSSpell001_Documentation_Link) +VSSpell001 | Naming | Warning | CSharpSpellCheckCodeAnalyzer, [Documentation](https://ewsoftware.github.io/VSSpellChecker/html/a7120f4c-5191-4442-b366-c3e792060569.htm) +VSSpell002 | Naming | Hidden | CSharpSpellCheckCodeAnalyzer, [Documentation](https://ewsoftware.github.io/VSSpellChecker/html/83ff9063-294f-4a18-b765-1510c86ad0d4.htm) ; Unshipped analyzer release diff --git a/Source/SpellCheckCodeAnalyzer2022AndLater/CSharpSpellCheckCodeAnalyzer.cs b/Source/SpellCheckCodeAnalyzer2022AndLater/CSharpSpellCheckCodeAnalyzer.cs index 55a4443..21d6d97 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 : 05/02/2023 +// Updated : 05/06/2023 // Note : Copyright 2023, Eric Woodruff, All rights reserved // // This file contains the class used to implement the C# spell check code analyzer @@ -56,29 +56,45 @@ public class CSharpSpellCheckCodeAnalyzer : DiagnosticAnalyzer #region Private data members and constants //===================================================================== + private static readonly Dictionary referenceAssemblies = new Dictionary(); + /// /// This constant represents the diagnostic ID for spelling errors /// public const string SpellingDiagnosticId = "VSSpell001"; + private const string SpellingCategory = "Naming"; + // You can change these strings in the Resources.resx file. If you do not want your analyzer to be // localize-able, you can use regular strings for Title and MessageFormat. // See https://github.com/dotnet/roslyn/blob/main/docs/analyzers/Localizing%20Analyzers.md for more on // localization. - private static readonly LocalizableString SpellingTitle = new LocalizableResourceString(nameof(Resources.SpellingAnalyzerTitle), - Resources.ResourceManager, typeof(Resources)); + private static readonly LocalizableString SpellingTitle = new LocalizableResourceString( + nameof(Resources.SpellingAnalyzerTitle), Resources.ResourceManager, typeof(Resources)); private static readonly LocalizableString SpellingMessageFormat = new LocalizableResourceString( nameof(Resources.SpellingAnalyzerMessageFormat), Resources.ResourceManager, typeof(Resources)); private static readonly LocalizableString SpellingDescription = new LocalizableResourceString( nameof(Resources.SpellingAnalyzerDescription), Resources.ResourceManager, typeof(Resources)); - private const string SpellingCategory = "Naming"; + private static readonly DiagnosticDescriptor SpellingRule = new DiagnosticDescriptor( + SpellingDiagnosticId, SpellingTitle, SpellingMessageFormat, SpellingCategory, + DiagnosticSeverity.Warning, true, SpellingDescription, + "https://ewsoftware.github.io/VSSpellChecker/html/a7120f4c-5191-4442-b366-c3e792060569.htm"); - // TODO: Provide help link URL? - private static readonly DiagnosticDescriptor SpellingRule = new DiagnosticDescriptor(SpellingDiagnosticId, - SpellingTitle, SpellingMessageFormat, SpellingCategory, DiagnosticSeverity.Warning, true, - SpellingDescription); - - private static readonly Dictionary referenceAssemblies = new Dictionary(); + /// + /// This constant represents the diagnostic ID for ignoring a misspelled word + /// + public const string IgnoreWordDiagnosticId = "VSSpell002"; + + private static readonly LocalizableString IgnoreWordTitle = new LocalizableResourceString( + nameof(Resources.IgnoreWordAnalyzerTitle), Resources.ResourceManager, typeof(Resources)); + private static readonly LocalizableString IgnoreWordMessageFormat = new LocalizableResourceString( + nameof(Resources.IgnoreWordAnalyzerMessageFormat), Resources.ResourceManager, typeof(Resources)); + private static readonly LocalizableString IgnoreWordDescription = new LocalizableResourceString( + nameof(Resources.IgnoreWordAnalyzerDescription), Resources.ResourceManager, typeof(Resources)); + private static readonly DiagnosticDescriptor IgnoreWordRule = new DiagnosticDescriptor(IgnoreWordDiagnosticId, + IgnoreWordTitle, IgnoreWordMessageFormat, SpellingCategory, DiagnosticSeverity.Hidden, true, + IgnoreWordDescription, + "https://ewsoftware.github.io/VSSpellChecker/html/83ff9063-294f-4a18-b765-1510c86ad0d4.htm"); #endregion @@ -127,7 +143,8 @@ static CSharpSpellCheckCodeAnalyzer() //===================================================================== /// - public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(SpellingRule); + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create( + SpellingRule, IgnoreWordRule); /// public override void Initialize(AnalysisContext context) @@ -252,9 +269,12 @@ public static void AnalyzeSyntaxTree(SyntaxTreeAnalysisContext context) { "Suffix", null } }; - // TODO: For the first release, just handle identifiers and leave the rest to the existing - // tagger which already handles all the other elements. This would let me get this out - // quicker and figure out how to handle the other stuff later if at all with an analyzer. + // 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 + // dictionary from the code analyzer is a limitation the tagger doesn't have so, for now, + // they'll continue to handle strings and comments. The code's already written so I'll + // leave it in here to parse the other elements. If the above issue is ever resolved, I + // could move that functionality in here later on. foreach(var s in handler.Spans.Where(s => s.SpanType == SpellCheckType.Identifier)) { context.CancellationToken.ThrowIfCancellationRequested(); @@ -331,7 +351,9 @@ public static void AnalyzeSyntaxTree(SyntaxTreeAnalysisContext context) if(!dictionary.IsSpelledCorrectly(textToCheck)) { - // TODO: Ignore the dictionary the suggestions come from for now. + // 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 (skipMisspelling, suggestions) = CheckSuggestions(textToCheck, dictionary.SuggestCorrections(textToCheck).Select(ss => ss.Suggestion)); @@ -349,8 +371,13 @@ public static void AnalyzeSyntaxTree(SyntaxTreeAnalysisContext context) context.ReportDiagnostic(Diagnostic.Create(SpellingRule, Location.Create(context.Tree, TextSpan.FromBounds( - s.TextSpan.Start + word.Start, s.TextSpan.Start + word.Start + word.Length)), - diagnosticProperties.ToImmutableDictionary(), s.Text)); + s.TextSpan.Start + word.Start, + s.TextSpan.Start + word.Start + word.Length)), + diagnosticProperties.ToImmutableDictionary(), textToCheck)); + context.ReportDiagnostic(Diagnostic.Create(IgnoreWordRule, + Location.Create(context.Tree, TextSpan.FromBounds( + s.TextSpan.Start + word.Start, + s.TextSpan.Start + word.Start + word.Length)), textToCheck)); } } } diff --git a/Source/SpellCheckCodeAnalyzer2022AndLater/Resources.Designer.cs b/Source/SpellCheckCodeAnalyzer2022AndLater/Resources.Designer.cs index 033ff44..66c86d3 100644 --- a/Source/SpellCheckCodeAnalyzer2022AndLater/Resources.Designer.cs +++ b/Source/SpellCheckCodeAnalyzer2022AndLater/Resources.Designer.cs @@ -61,7 +61,34 @@ internal Resources() { } /// - /// Looks up a localized string similar to Code element spell checker.. + /// Looks up a localized string similar to Ignore misspelled word.. + /// + internal static string IgnoreWordAnalyzerDescription { + get { + return ResourceManager.GetString("IgnoreWordAnalyzerDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Ignore word '{0}'. + /// + internal static string IgnoreWordAnalyzerMessageFormat { + get { + return ResourceManager.GetString("IgnoreWordAnalyzerMessageFormat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Ignore Word. + /// + internal static string IgnoreWordAnalyzerTitle { + get { + return ResourceManager.GetString("IgnoreWordAnalyzerTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Code element spell check.. /// internal static string SpellingAnalyzerDescription { get { diff --git a/Source/SpellCheckCodeAnalyzer2022AndLater/Resources.resx b/Source/SpellCheckCodeAnalyzer2022AndLater/Resources.resx index c5963da..285f549 100644 --- a/Source/SpellCheckCodeAnalyzer2022AndLater/Resources.resx +++ b/Source/SpellCheckCodeAnalyzer2022AndLater/Resources.resx @@ -117,8 +117,17 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + Ignore misspelled word. + + + Ignore word '{0}' + + + Ignore Word + - Code element spell checker. + Code element spell check. An optional longer localizable description of the diagnostic. diff --git a/Source/VSSpellChecker2017And2019/GlobalSuppressions.cs b/Source/VSSpellChecker2017And2019/GlobalSuppressions.cs index fefd87c46c78c7f9fb1d43c594c8a659407a5b44..73aca1bf0f4e710b67da70ab66c641d826e3569d 100644 GIT binary patch delta 40 wcmbRBp6StNrVX>UZ+^g2z&N>KiwkcGLncEBLnuQrLn=ejWJU$i$w`t$0B9NxwEzGB delta 9 RcmaF#nQ7L0rVX>U0{|QA1!MpK diff --git a/Source/VSSpellChecker2017And2019/source.extension.vsixmanifest b/Source/VSSpellChecker2017And2019/source.extension.vsixmanifest index fd18a5e..b2b6d5f 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/GlobalSuppressions.cs b/Source/VSSpellChecker2022AndLater/GlobalSuppressions.cs index 5cacd0d8488c8c7e12659389a871b0387ed4ac9c..175131e3352d731cbfeed89e947a9512900b4e83 100644 GIT binary patch delta 42 ycmdn?h3Vf9rVVDhHs=_mFit)o=Ev{KkiwA3P{I((P|T3ZP&AoQL3DDGWDx*r*bXfK delta 9 QcmezOgK66rrVVDh038bjMF0Q* diff --git a/Source/VSSpellChecker2022AndLater/source.extension.vsixmanifest b/Source/VSSpellChecker2022AndLater/source.extension.vsixmanifest index 82d4ce0..4a4adf8 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/Configuration/SpellCheckerConfiguration.cs b/Source/VSSpellCheckerCommon/Configuration/SpellCheckerConfiguration.cs index 223bffb..d827cde 100644 --- a/Source/VSSpellCheckerCommon/Configuration/SpellCheckerConfiguration.cs +++ b/Source/VSSpellCheckerCommon/Configuration/SpellCheckerConfiguration.cs @@ -2,7 +2,7 @@ // System : Visual Studio Spell Checker Package // File : SpellCheckerConfiguration.cs // Author : Eric Woodruff (Eric@EWoodruff.us) -// Updated : 04/23/2023 +// Updated : 05/06/2023 // Note : Copyright 2015-2023, Eric Woodruff, All rights reserved // // This file contains the class used to contain the spell checker's configuration settings @@ -1081,7 +1081,7 @@ private void ApplyProperties(string basePath, IEnumerable<(string PropertyName, ignoredWords.Add(word); else { - string ignoredWordsFile = ResolveFilePath(word.Substring(5), basePath); + string ignoredWordsFile = ResolveFilePath(word.Substring(5).Trim(), basePath); if(ignoredWordsFile != null) { diff --git a/Source/VSSpellCheckerCommon/GlobalDictionary.cs b/Source/VSSpellCheckerCommon/GlobalDictionary.cs index 7274838..146dfa5 100644 --- a/Source/VSSpellCheckerCommon/GlobalDictionary.cs +++ b/Source/VSSpellCheckerCommon/GlobalDictionary.cs @@ -2,7 +2,7 @@ // System : Visual Studio Spell Checker Package // File : GlobalDictionary.cs // Author : Eric Woodruff (Eric@EWoodruff.us) -// Updated : 03/20/2023 +// Updated : 05/06/2023 // Note : Copyright 2013-2023, Eric Woodruff, All rights reserved // // This file contains a class that implements the global dictionary @@ -68,6 +68,11 @@ public sealed class GlobalDictionary : IDisposable /// public bool IsInitialized { get; private set; } + /// + /// This read-only property returns the name of the user dictionary file + /// + public string UserDictionaryFile => dictionaryWordsFile; + /// /// This can be used to assign a function that checks to see if the user words file is writable /// @@ -363,8 +368,30 @@ public static GlobalDictionary CreateGlobalDictionary(CultureInfo culture, } else { - // Add recognized words that are not already there - globalDictionary.AddRecognizedWords(recognizedWords); + // Add recognized words that are not already there and include the user dictionary in case it + // was modified by another process. + var dw = new HashSet(); + + try + { + if(File.Exists(globalDictionary.UserDictionaryFile)) + { + foreach(string word in File.ReadLines(globalDictionary.UserDictionaryFile)) + { + if(!String.IsNullOrWhiteSpace(word)) + dw.Add(word.Trim()); + } + } + } + catch(Exception ex) + { + // Ignore exceptions. Not much we can do, we'll just not include those words for now. + System.Diagnostics.Debug.WriteLine(ex); + } + + dw.UnionWith(recognizedWords); + + globalDictionary.AddRecognizedWords(dw); } } catch(Exception ex) @@ -548,8 +575,10 @@ public static bool IsReadyForUse(CultureInfo language) public static void NotifyChangeOfStatus() { if(globalDictionaries != null) + { foreach(var g in globalDictionaries.Values) g.NotifySpellingServicesOfChange(null); + } } /// @@ -564,14 +593,16 @@ private void LoadUserDictionaryFile() try { foreach(string word in File.ReadLines(dictionaryWordsFile)) + { if(!String.IsNullOrWhiteSpace(word)) dictionaryWords.Add(word.Trim()); + } this.AddSuggestions(); } catch(Exception ex) { - // Ignore exceptions. Not much we can do, we'll just not ignore anything by default. + // Ignore exceptions. Not much we can do, we'll just not include those words for now. System.Diagnostics.Debug.WriteLine(ex); } } @@ -617,11 +648,13 @@ public void AddRecognizedWords(IEnumerable words) lock(recognizedWords) { foreach(string word in words) + { if(!recognizedWords.Contains(word)) { recognizedWords.Add(word); addSuggestions = true; } + } if(addSuggestions && this.IsInitialized) this.AddSuggestions(); @@ -652,6 +685,7 @@ private void AddSuggestion(string word) Stack hunspells = (Stack)fi.GetValue(spellFactory); if(hunspellSemaphore != null && hunspells != null) + { try { // Make sure we get all semaphores since we will be touching all spellers @@ -666,9 +700,13 @@ private void AddSuggestion(string word) } if(releaseCount == processors) + { foreach(var hs in hunspells.ToArray()) + { if(!hs.Spell(word)) hs.Add(word.ToLower(this.Culture)); + } + } } catch(Exception ex) { @@ -680,6 +718,7 @@ private void AddSuggestion(string word) if(releaseCount != 0) hunspellSemaphore.Release(releaseCount); } + } } } @@ -707,6 +746,7 @@ private void AddSuggestions() Stack hunspells = (Stack)fi.GetValue(spellFactory); if(hunspellSemaphore != null && hunspells != null) + { try { // Make sure we get all semaphores since we will be touching all spellers @@ -723,10 +763,16 @@ private void AddSuggestions() lock(recognizedWords) { if(releaseCount == processors) + { foreach(var hs in hunspells.ToArray()) + { foreach(string word in dictionaryWords.Concat(recognizedWords)) + { if(!hs.Spell(word)) hs.Add(word.ToLower(this.Culture)); + } + } + } } } catch(Exception ex) @@ -739,6 +785,7 @@ private void AddSuggestions() if(releaseCount != 0) hunspellSemaphore.Release(releaseCount); } + } } } @@ -784,6 +831,7 @@ private void RemoveWord(string word) Stack hunspells = (Stack)fi.GetValue(spellFactory); if(hunspellSemaphore != null && hunspells != null) + { try { // Make sure we get all semaphores since we will be touching all spellers @@ -798,9 +846,13 @@ private void RemoveWord(string word) } if(releaseCount == processors) + { foreach(var hs in hunspells.ToArray()) + { if(hs.Spell(word)) hs.Remove(word.ToLower(this.Culture)); + } + } } catch(Exception ex) { @@ -812,6 +864,7 @@ private void RemoveWord(string word) if(releaseCount != 0) hunspellSemaphore.Release(releaseCount); } + } } } #endregion diff --git a/Source/VSSpellCheckerDefinitionsShared/Properties/AssemblyInfoShared.cs b/Source/VSSpellCheckerDefinitionsShared/Properties/AssemblyInfoShared.cs index 0862fb9..587002c 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 : 04/22/2023 +// Updated : 05/08/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.4.22.0"; + public const string FileVersion = "23.5.8.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.4.22.0"; + public const string ProductVersion = "2023.5.8.0"; // Assembly copyright information public const string Copyright = "Copyright \xA9 2013-2023, Eric Woodruff, All Rights Reserved.\r\n" + diff --git a/Source/VSSpellCheckerShared/Editors/Pages/DictionarySettingsUserControl.xaml b/Source/VSSpellCheckerShared/Editors/Pages/DictionarySettingsUserControl.xaml index 614b39c..5d02268 100644 --- a/Source/VSSpellCheckerShared/Editors/Pages/DictionarySettingsUserControl.xaml +++ b/Source/VSSpellCheckerShared/Editors/Pages/DictionarySettingsUserControl.xaml @@ -3,7 +3,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" - mc:Ignorable="d" d:DesignHeight="400" d:DesignWidth="650" + mc:Ignorable="d" d:DesignHeight="400" d:DesignWidth="750" Background="{DynamicResource ToolWindowBackgroundBrushKey}"> @@ -118,13 +118,16 @@ the configuration file. +