diff --git a/CodeMaid.UnitTests/CachedSettingSetTests.cs b/CodeMaid.UnitTests/CachedSettingSetTests.cs index b99d5fd55..635846fdc 100644 --- a/CodeMaid.UnitTests/CachedSettingSetTests.cs +++ b/CodeMaid.UnitTests/CachedSettingSetTests.cs @@ -1,21 +1,19 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using SteveCadwallader.CodeMaid.Helpers; +using SteveCadwallader.CodeMaid.Helpers; using SteveCadwallader.CodeMaid.Properties; using System; using System.Collections.Generic; using System.Linq; +using Xunit; namespace SteveCadwallader.CodeMaid.UnitTests { - [TestClass] public class CachedSettingSetTests { private int _lookupCount; private int _parseCount; private CachedSettingSet _cachedSettingSet; - [TestInitialize] - public void TestInitialize() + public CachedSettingSetTests() { Settings.Default.Reset(); @@ -36,45 +34,45 @@ public void TestInitialize() .ToList(); }); - Assert.AreEqual(0, _lookupCount); - Assert.AreEqual(0, _parseCount); - Assert.IsNotNull(_cachedSettingSet); + Assert.Equal(0, _lookupCount); + Assert.Equal(0, _parseCount); + Assert.NotNull(_cachedSettingSet); } - [TestMethod] + [Fact] public void CachedSettingSetCanLookupAndParse() { var cleanupExclusions = _cachedSettingSet.Value; - Assert.IsNotNull(cleanupExclusions); - Assert.AreEqual(1, _lookupCount); - Assert.AreEqual(1, _parseCount); + Assert.NotNull(cleanupExclusions); + Assert.Equal(1, _lookupCount); + Assert.Equal(1, _parseCount); } - [TestMethod] + [Fact] public void CachedSettingSetUsesCacheOnSecondLookup() { var cleanupExclusions = _cachedSettingSet.Value; - Assert.IsNotNull(cleanupExclusions); - Assert.AreEqual(1, _lookupCount); - Assert.AreEqual(1, _parseCount); + Assert.NotNull(cleanupExclusions); + Assert.Equal(1, _lookupCount); + Assert.Equal(1, _parseCount); var cleanupExclusions2 = _cachedSettingSet.Value; - Assert.IsNotNull(cleanupExclusions2); - Assert.AreEqual(2, _lookupCount); - Assert.AreEqual(1, _parseCount); + Assert.NotNull(cleanupExclusions2); + Assert.Equal(2, _lookupCount); + Assert.Equal(1, _parseCount); } - [TestMethod] + [Fact] public void CachedSettingSetReParsesOnChange() { var cleanupExclusions = _cachedSettingSet.Value; - Assert.IsNotNull(cleanupExclusions); - Assert.AreEqual(1, _lookupCount); - Assert.AreEqual(1, _parseCount); + Assert.NotNull(cleanupExclusions); + Assert.Equal(1, _lookupCount); + Assert.Equal(1, _parseCount); var cleanupExclusion2 = new List(cleanupExclusions) { ".*Test.*" }; var serializedCleanupExclusions = string.Join("||", cleanupExclusion2); @@ -83,9 +81,9 @@ public void CachedSettingSetReParsesOnChange() var memberTypeSetting2 = _cachedSettingSet.Value; - Assert.IsNotNull(memberTypeSetting2); - Assert.AreEqual(2, _lookupCount); - Assert.AreEqual(2, _parseCount); + Assert.NotNull(memberTypeSetting2); + Assert.Equal(2, _lookupCount); + Assert.Equal(2, _parseCount); } } -} \ No newline at end of file +} diff --git a/CodeMaid.UnitTests/CachedSettingTests.cs b/CodeMaid.UnitTests/CachedSettingTests.cs index 285469d66..67e92ee1d 100644 --- a/CodeMaid.UnitTests/CachedSettingTests.cs +++ b/CodeMaid.UnitTests/CachedSettingTests.cs @@ -1,18 +1,16 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using SteveCadwallader.CodeMaid.Helpers; +using SteveCadwallader.CodeMaid.Helpers; using SteveCadwallader.CodeMaid.Properties; +using Xunit; namespace SteveCadwallader.CodeMaid.UnitTests { - [TestClass] public class CachedSettingTests { private int _lookupCount; private int _parseCount; private CachedSetting _cachedSetting; - [TestInitialize] - public void TestInitialize() + public CachedSettingTests() { Settings.Default.Reset(); @@ -30,54 +28,54 @@ public void TestInitialize() return (MemberTypeSetting)x; }); - Assert.AreEqual(0, _lookupCount); - Assert.AreEqual(0, _parseCount); - Assert.IsNotNull(_cachedSetting); + Assert.Equal(0, _lookupCount); + Assert.Equal(0, _parseCount); + Assert.NotNull(_cachedSetting); } - [TestMethod] + [Fact] public void CachedSettingCanLookupAndParse() { var memberTypeSetting = _cachedSetting.Value; - Assert.IsNotNull(memberTypeSetting); - Assert.AreEqual(1, _lookupCount); - Assert.AreEqual(1, _parseCount); + Assert.NotNull(memberTypeSetting); + Assert.Equal(1, _lookupCount); + Assert.Equal(1, _parseCount); } - [TestMethod] + [Fact] public void CachedSettingUsesCacheOnSecondLookup() { var memberTypeSetting = _cachedSetting.Value; - Assert.IsNotNull(memberTypeSetting); - Assert.AreEqual(1, _lookupCount); - Assert.AreEqual(1, _parseCount); + Assert.NotNull(memberTypeSetting); + Assert.Equal(1, _lookupCount); + Assert.Equal(1, _parseCount); var memberTypeSetting2 = _cachedSetting.Value; - Assert.IsNotNull(memberTypeSetting2); - Assert.AreEqual(2, _lookupCount); - Assert.AreEqual(1, _parseCount); + Assert.NotNull(memberTypeSetting2); + Assert.Equal(2, _lookupCount); + Assert.Equal(1, _parseCount); } - [TestMethod] + [Fact] public void CachedSettingReParsesOnChange() { var memberTypeSetting = _cachedSetting.Value; - Assert.IsNotNull(memberTypeSetting); - Assert.AreEqual(1, _lookupCount); - Assert.AreEqual(1, _parseCount); + Assert.NotNull(memberTypeSetting); + Assert.Equal(1, _lookupCount); + Assert.Equal(1, _parseCount); memberTypeSetting.EffectiveName = "Member Variables"; Settings.Default.Reorganizing_MemberTypeFields = (string)memberTypeSetting; var memberTypeSetting2 = _cachedSetting.Value; - Assert.IsNotNull(memberTypeSetting2); - Assert.AreEqual(2, _lookupCount); - Assert.AreEqual(2, _parseCount); + Assert.NotNull(memberTypeSetting2); + Assert.Equal(2, _lookupCount); + Assert.Equal(2, _parseCount); } } -} \ No newline at end of file +} diff --git a/CodeMaid.UnitTests/CodeMaid.UnitTests.csproj b/CodeMaid.UnitTests/CodeMaid.UnitTests.csproj index 23fc90cc9..5e3cca97c 100644 --- a/CodeMaid.UnitTests/CodeMaid.UnitTests.csproj +++ b/CodeMaid.UnitTests/CodeMaid.UnitTests.csproj @@ -1,4 +1,4 @@ - + Debug @@ -6,7 +6,7 @@ 15.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) {27C78B2A-18BC-4CE0-BCB1-DB4C30D805BE} - Library + Exe Properties SteveCadwallader.CodeMaid.UnitTests SteveCadwallader.CodeMaid.UnitTests @@ -21,6 +21,8 @@ latest + + true @@ -62,6 +64,7 @@ + @@ -81,25 +84,19 @@ - 16.10.31321.278 + 17.14.40265 - 17.0.0 - - - 2.2.7 - - - 2.2.7 + 18.6.0 - 4.2.2 + 5.3.0 - - 3.13.2 + + 3.0.2 - - 4.0.0 + + 3.2.2 diff --git a/CodeMaid.UnitTests/Formatting/CommentFormatHelper.cs b/CodeMaid.UnitTests/Formatting/CommentFormatHelper.cs index 7f6d1b9ad..caa584fd7 100644 --- a/CodeMaid.UnitTests/Formatting/CommentFormatHelper.cs +++ b/CodeMaid.UnitTests/Formatting/CommentFormatHelper.cs @@ -1,5 +1,5 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using SteveCadwallader.CodeMaid.Model.Comments; +using SteveCadwallader.CodeMaid.Model.Comments; +using Xunit; using SteveCadwallader.CodeMaid.Model.Comments.Options; using System; @@ -29,7 +29,7 @@ public static string AssertEqualAfterFormat( Action options = null) { var result = CodeComment.Format(text, prefix, options); - Assert.AreEqual(expected ?? text, result); + Assert.Equal(expected ?? text, result); return result; } } diff --git a/CodeMaid.UnitTests/Formatting/FormatWithPrefixTests.cs b/CodeMaid.UnitTests/Formatting/FormatWithPrefixTests.cs index 70fe855c6..e6238e37f 100644 --- a/CodeMaid.UnitTests/Formatting/FormatWithPrefixTests.cs +++ b/CodeMaid.UnitTests/Formatting/FormatWithPrefixTests.cs @@ -1,22 +1,20 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using SteveCadwallader.CodeMaid.Properties; +using SteveCadwallader.CodeMaid.Properties; using System; +using Xunit; namespace SteveCadwallader.CodeMaid.UnitTests.Formatting { /// /// - [TestClass] public class FormatWithPrefixTests { - [TestInitialize] - public void TestInitialize() + public FormatWithPrefixTests() { Settings.Default.Reset(); } - [TestMethod] - [TestCategory("Formatting UnitTests")] + [Fact] + [Trait("Category", "Formatting UnitTests")] public void SimpleFormatWithPrefixTests_KeepsPrefix() { var input = "// Lorem ipsum dolor sit amet, consectetur adipiscing elit."; @@ -26,8 +24,8 @@ public void SimpleFormatWithPrefixTests_KeepsPrefix() CommentFormatHelper.AssertEqualAfterFormat(input, expected, "//", o => o.WrapColumn = 40); } - [TestMethod] - [TestCategory("Formatting UnitTests")] + [Fact] + [Trait("Category", "Formatting UnitTests")] public void SimpleFormatWithPrefixTests_TrimsTrailingSpace() { var input = "// Trailing space "; @@ -35,8 +33,8 @@ public void SimpleFormatWithPrefixTests_TrimsTrailingSpace() CommentFormatHelper.AssertEqualAfterFormat(input, expected, "//"); } - [TestMethod] - [TestCategory("Formatting UnitTests")] + [Fact] + [Trait("Category", "Formatting UnitTests")] public void SimpleFormatWithPrefixTests_TrimsTrailingLines() { var input = @@ -48,8 +46,8 @@ public void SimpleFormatWithPrefixTests_TrimsTrailingLines() CommentFormatHelper.AssertEqualAfterFormat(input, expected, "//"); } - [TestMethod] - [TestCategory("Formatting UnitTests")] + [Fact] + [Trait("Category", "Formatting UnitTests")] public void SimpleFormatWithPrefixTests_TrimsLeadingLines() { var input = @@ -62,16 +60,16 @@ public void SimpleFormatWithPrefixTests_TrimsLeadingLines() CommentFormatHelper.AssertEqualAfterFormat(input, expected, "//"); } - [TestMethod] - [TestCategory("Formatting UnitTests")] + [Fact] + [Trait("Category", "Formatting UnitTests")] public void SimpleFormatWithPrefixTests_KeepsLeadingSpace() { var input = " // Lorem ipsum."; CommentFormatHelper.AssertEqualAfterFormat(input, input, " //"); } - [TestMethod] - [TestCategory("Formatting UnitTests")] + [Fact] + [Trait("Category", "Formatting UnitTests")] public void SimpleFormatWithPrefixTests_AlignsToFirstPrefix() { var input = @@ -83,8 +81,8 @@ public void SimpleFormatWithPrefixTests_AlignsToFirstPrefix() CommentFormatHelper.AssertEqualAfterFormat(input, expected, " //", o => o.WrapColumn = 40); } - [TestMethod] - [TestCategory("Formatting UnitTests")] + [Fact] + [Trait("Category", "Formatting UnitTests")] public void SimpleFormatWithPrefixTests_NoTrailingWhitespaceOnEmptyLine() { var input = diff --git a/CodeMaid.UnitTests/Formatting/HeaderFormattingTests.cs b/CodeMaid.UnitTests/Formatting/HeaderFormattingTests.cs index 613007bc2..0b5499882 100644 --- a/CodeMaid.UnitTests/Formatting/HeaderFormattingTests.cs +++ b/CodeMaid.UnitTests/Formatting/HeaderFormattingTests.cs @@ -1,6 +1,6 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; using SteveCadwallader.CodeMaid.Properties; using System; +using Xunit; namespace SteveCadwallader.CodeMaid.UnitTests.Formatting { @@ -8,11 +8,9 @@ namespace SteveCadwallader.CodeMaid.UnitTests.Formatting /// Class with simple unit tests for formatting header type comments. This calls the formatter /// directly, rather than invoking it through the UI as with the integration tests. /// - [TestClass] public class HeaderFormattingTests { - [TestInitialize] - public void TestInitialize() + public HeaderFormattingTests() { Settings.Default.Reset(); } @@ -20,8 +18,8 @@ public void TestInitialize() /// /// Tests the forced indenting of the XML copyright file header. /// - [TestMethod] - [TestCategory("Formatting UnitTests")] + [Fact] + [Trait("Category", "Formatting UnitTests")] public void HeaderFormattingTests_Copyright_Indenting() { var input = @@ -37,8 +35,8 @@ public void HeaderFormattingTests_Copyright_Indenting() CommentFormatHelper.AssertEqualAfterFormat(input, expected, o => o.Xml.Default.Indent = 0); } - [TestMethod] - [TestCategory("Formatting UnitTests")] + [Fact] + [Trait("Category", "Formatting UnitTests")] public void HeaderFormattingTests_PreservesHyphenLinesWithoutXML() { var input = @@ -51,8 +49,8 @@ public void HeaderFormattingTests_PreservesHyphenLinesWithoutXML() CommentFormatHelper.AssertEqualAfterFormat(input); } - [TestMethod] - [TestCategory("Formatting UnitTests")] + [Fact] + [Trait("Category", "Formatting UnitTests")] public void HeaderFormattingTests_Copyright_PreservesHyphenLinesWithXML() { var input = diff --git a/CodeMaid.UnitTests/Formatting/IgnorePrefixesTests.cs b/CodeMaid.UnitTests/Formatting/IgnorePrefixesTests.cs index 5757a163f..862d46c1e 100644 --- a/CodeMaid.UnitTests/Formatting/IgnorePrefixesTests.cs +++ b/CodeMaid.UnitTests/Formatting/IgnorePrefixesTests.cs @@ -1,30 +1,28 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using SteveCadwallader.CodeMaid.Properties; +using SteveCadwallader.CodeMaid.Properties; using System; +using Xunit; namespace SteveCadwallader.CodeMaid.UnitTests.Formatting { /// /// Test for the ignoring of comments lines starting with certain prefixes. /// - [TestClass] public class IgnorePrefixesTests { - [TestInitialize] - public void TestInitialize() + public IgnorePrefixesTests() { Settings.Default.Reset(); } - [TestMethod] - [TestCategory("Formatting UnitTests")] + [Fact] + [Trait("Category", "Formatting UnitTests")] public void IgnorePrefixesTests_DoesNotWrapSingleLine() { CommentFormatHelper.AssertEqualAfterFormat(@"TODO: Lorem ipsum dolor sit amet, consectetur adipiscing elit.", o => o.WrapColumn = 30); } - [TestMethod] - [TestCategory("Formatting UnitTests")] + [Fact] + [Trait("Category", "Formatting UnitTests")] public void IgnorePrefixesTests_DoesNotWrapLineInsideComment() { var input = @@ -43,8 +41,8 @@ public void IgnorePrefixesTests_DoesNotWrapLineInsideComment() CommentFormatHelper.AssertEqualAfterFormat(input, expected, o => o.WrapColumn = 30); } - [TestMethod] - [TestCategory("Formatting UnitTests")] + [Fact] + [Trait("Category", "Formatting UnitTests")] public void IgnorePrefixesTests_DoesNotCombineSubsequentLines() { var input = diff --git a/CodeMaid.UnitTests/Formatting/ListFormattingTests.cs b/CodeMaid.UnitTests/Formatting/ListFormattingTests.cs index f44c0952d..903788450 100644 --- a/CodeMaid.UnitTests/Formatting/ListFormattingTests.cs +++ b/CodeMaid.UnitTests/Formatting/ListFormattingTests.cs @@ -1,6 +1,6 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using SteveCadwallader.CodeMaid.Properties; +using SteveCadwallader.CodeMaid.Properties; using System; +using Xunit; namespace SteveCadwallader.CodeMaid.UnitTests.Formatting { @@ -8,17 +8,15 @@ namespace SteveCadwallader.CodeMaid.UnitTests.Formatting /// Class with list oriented unit tests for formatting. This calls the formatter directly, rather /// than invoking it through the UI as with the integration tests. /// - [TestClass] public class ListFormattingTests { - [TestInitialize] - public void TestInitialize() + public ListFormattingTests() { Settings.Default.Reset(); } - [TestMethod] - [TestCategory("Formatting UnitTests")] + [Fact] + [Trait("Category", "Formatting UnitTests")] public void ListFormattingTests_DashedList() { var input = @@ -38,8 +36,8 @@ public void ListFormattingTests_DashedList() CommentFormatHelper.AssertEqualAfterFormat(input, expected, o => o.WrapColumn = 30); } - [TestMethod] - [TestCategory("Formatting UnitTests")] + [Fact] + [Trait("Category", "Formatting UnitTests")] public void ListFormattingTests_NumberedList() { var input = @@ -59,8 +57,8 @@ public void ListFormattingTests_NumberedList() CommentFormatHelper.AssertEqualAfterFormat(input, expected, o => o.WrapColumn = 30); } - [TestMethod] - [TestCategory("Formatting UnitTests")] + [Fact] + [Trait("Category", "Formatting UnitTests")] public void ListFormattingTests_WordList() { var input = @@ -80,8 +78,8 @@ public void ListFormattingTests_WordList() CommentFormatHelper.AssertEqualAfterFormat(input, expected, o => o.WrapColumn = 35); } - [TestMethod] - [TestCategory("Formatting UnitTests")] + [Fact] + [Trait("Category", "Formatting UnitTests")] public void ListFormattingTests_XmlListWithHeader() { var input = @@ -115,8 +113,8 @@ public void ListFormattingTests_XmlListWithHeader() CommentFormatHelper.AssertEqualAfterFormat(input, expected); } - [TestMethod] - [TestCategory("Formatting UnitTests")] + [Fact] + [Trait("Category", "Formatting UnitTests")] public void ListFormattingTests_XmlListWithHeaderAndIndent() { var input = diff --git a/CodeMaid.UnitTests/Formatting/SimpleFormattingTests.cs b/CodeMaid.UnitTests/Formatting/SimpleFormattingTests.cs index 8185f56df..ccc145768 100644 --- a/CodeMaid.UnitTests/Formatting/SimpleFormattingTests.cs +++ b/CodeMaid.UnitTests/Formatting/SimpleFormattingTests.cs @@ -1,6 +1,6 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using SteveCadwallader.CodeMaid.Properties; +using SteveCadwallader.CodeMaid.Properties; using System; +using Xunit; namespace SteveCadwallader.CodeMaid.UnitTests.Formatting { @@ -8,24 +8,22 @@ namespace SteveCadwallader.CodeMaid.UnitTests.Formatting /// Class with simple unit tests for formatting. This calls the formatter directly, rather than /// invoking it through the UI as with the integration tests. /// - [TestClass] public class SimpleFormattingTests { - [TestInitialize] - public void TestInitialize() + public SimpleFormattingTests() { Settings.Default.Reset(); } - [TestMethod] - [TestCategory("Formatting UnitTests")] + [Fact] + [Trait("Category", "Formatting UnitTests")] public void SimpleFormattingTests_DoesNotCreateText() { CommentFormatHelper.AssertEqualAfterFormat(string.Empty, string.Empty); } - [TestMethod] - [TestCategory("Formatting UnitTests")] + [Fact] + [Trait("Category", "Formatting UnitTests")] public void SimpleFormattingTests_DoesNotWrapShortLines() { var input = "Lorem ipsum dolor sit amet."; @@ -33,8 +31,8 @@ public void SimpleFormattingTests_DoesNotWrapShortLines() CommentFormatHelper.AssertEqualAfterFormat(input); } - [TestMethod] - [TestCategory("Formatting UnitTests")] + [Fact] + [Trait("Category", "Formatting UnitTests")] public void SimpleFormattingTests_PreservesMultipleBlankLine() { var input = "Lorem ipsum\r\n\r\n\r\ndolor sit amet."; @@ -42,8 +40,8 @@ public void SimpleFormattingTests_PreservesMultipleBlankLine() CommentFormatHelper.AssertEqualAfterFormat(input); } - [TestMethod] - [TestCategory("Formatting UnitTests")] + [Fact] + [Trait("Category", "Formatting UnitTests")] public void SimpleFormattingTests_PreservesSingleBlankLine() { var input = "Lorem ipsum\r\n\r\ndolor sit amet."; @@ -51,8 +49,8 @@ public void SimpleFormattingTests_PreservesSingleBlankLine() CommentFormatHelper.AssertEqualAfterFormat(input); } - [TestMethod] - [TestCategory("Formatting UnitTests")] + [Fact] + [Trait("Category", "Formatting UnitTests")] public void SimpleFormattingTests_NoTrailingWhitespace() { var input = @@ -68,8 +66,8 @@ public void SimpleFormattingTests_NoTrailingWhitespace() CommentFormatHelper.AssertEqualAfterFormat(input, expected); } - [TestMethod] - [TestCategory("Formatting UnitTests")] + [Fact] + [Trait("Category", "Formatting UnitTests")] public void SimpleFormattingTests_RemoveBlankLinesAfter() { var input = "Lorem ipsum dolor sit amet.\r\n\r\n"; @@ -78,8 +76,8 @@ public void SimpleFormattingTests_RemoveBlankLinesAfter() CommentFormatHelper.AssertEqualAfterFormat(input, expected); } - [TestMethod] - [TestCategory("Formatting UnitTests")] + [Fact] + [Trait("Category", "Formatting UnitTests")] public void SimpleFormattingTests_RemoveBlankLinesBefore() { var input = "\r\n\r\nLorem ipsum dolor sit amet."; @@ -88,8 +86,8 @@ public void SimpleFormattingTests_RemoveBlankLinesBefore() CommentFormatHelper.AssertEqualAfterFormat(input, expected); } - [TestMethod] - [TestCategory("Formatting UnitTests")] + [Fact] + [Trait("Category", "Formatting UnitTests")] public void SimpleFormattingTests_RemovesLineBreaks() { var input = "Lorem ipsum\r\ndolor sit amet."; @@ -98,8 +96,8 @@ public void SimpleFormattingTests_RemovesLineBreaks() CommentFormatHelper.AssertEqualAfterFormat(input, expected); } - [TestMethod] - [TestCategory("Formatting UnitTests")] + [Fact] + [Trait("Category", "Formatting UnitTests")] public void SimpleFormattingTests_SkipWrapOnLastWord() { var input = "Lorem ipsum dolor sit amet."; @@ -112,8 +110,8 @@ public void SimpleFormattingTests_SkipWrapOnLastWord() }); } - [TestMethod] - [TestCategory("Formatting UnitTests")] + [Fact] + [Trait("Category", "Formatting UnitTests")] public void SimpleFormattingTests_WrapOnLastWord() { var input = "Lorem ipsum dolor sit amet."; @@ -126,24 +124,24 @@ public void SimpleFormattingTests_WrapOnLastWord() }); } - [TestMethod] - [TestCategory("Formatting UnitTests")] + [Fact] + [Trait("Category", "Formatting UnitTests")] public void SimpleFormattingTests_HyperlinkOnNewLine() { var input = "http://foo"; CommentFormatHelper.AssertEqualAfterFormat(input); } - [TestMethod] - [TestCategory("Formatting UnitTests")] + [Fact] + [Trait("Category", "Formatting UnitTests")] public void SimpleFormattingTests_HyperlinkBetweenWords() { var input = "Look at this http://foo pretty link."; CommentFormatHelper.AssertEqualAfterFormat(input); } - [TestMethod] - [TestCategory("Formatting UnitTests")] + [Fact] + [Trait("Category", "Formatting UnitTests")] public void SimpleFormattingTests_WrapsLinesAsExpected() { var input = "Lorem ipsum dolor sit."; @@ -152,8 +150,8 @@ public void SimpleFormattingTests_WrapsLinesAsExpected() CommentFormatHelper.AssertEqualAfterFormat(input, expected, o => o.WrapColumn = 12); } - [TestMethod] - [TestCategory("Formatting UnitTests")] + [Fact] + [Trait("Category", "Formatting UnitTests")] public void SimpleFormattingTests_MergesHyphenAndNonHyphenLines() { var input = diff --git a/CodeMaid.UnitTests/Formatting/XmlFormattingTests.cs b/CodeMaid.UnitTests/Formatting/XmlFormattingTests.cs index 9936aeaae..625920313 100644 --- a/CodeMaid.UnitTests/Formatting/XmlFormattingTests.cs +++ b/CodeMaid.UnitTests/Formatting/XmlFormattingTests.cs @@ -1,7 +1,7 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using SteveCadwallader.CodeMaid.Model.Comments.Options; +using SteveCadwallader.CodeMaid.Model.Comments.Options; using SteveCadwallader.CodeMaid.Properties; using System; +using Xunit; namespace SteveCadwallader.CodeMaid.UnitTests.Formatting { @@ -9,17 +9,15 @@ namespace SteveCadwallader.CodeMaid.UnitTests.Formatting /// Class with simple unit tests for formatting XML based comments. This calls the formatter /// directly, rather than invoking it through the UI as with the integration tests. /// - [TestClass] public class XmlFormattingTests { - [TestInitialize] - public void TestInitialize() + public XmlFormattingTests() { Settings.Default.Reset(); } - [TestMethod] - [TestCategory("Formatting UnitTests")] + [Fact] + [Trait("Category", "Formatting UnitTests")] public void XmlFormattingTests_AddSpaceToInsideTags() { var input = ""; @@ -31,8 +29,8 @@ public void XmlFormattingTests_AddSpaceToInsideTags() }); } - [TestMethod] - [TestCategory("Formatting UnitTests")] + [Fact] + [Trait("Category", "Formatting UnitTests")] public void XmlFormattingTests_AddSpaceToTagContent() { var input = "test"; @@ -44,8 +42,8 @@ public void XmlFormattingTests_AddSpaceToTagContent() }); } - [TestMethod] - [TestCategory("Formatting UnitTests")] + [Fact] + [Trait("Category", "Formatting UnitTests")] public void XmlFormattingTests_AddSpaceToTagContentWithSelfClosingTag() { var input = ""; @@ -58,8 +56,8 @@ public void XmlFormattingTests_AddSpaceToTagContentWithSelfClosingTag() }); } - [TestMethod] - [TestCategory("Formatting UnitTests")] + [Fact] + [Trait("Category", "Formatting UnitTests")] public void XmlFormattingTests_AddSpaceToTagContentWithSelfClosingTagMultiline() { // Add space to content should not add a space when tag content is on it's own line. @@ -77,8 +75,8 @@ public void XmlFormattingTests_AddSpaceToTagContentWithSelfClosingTagMultiline() }); } - [TestMethod] - [TestCategory("Formatting UnitTests")] + [Fact] + [Trait("Category", "Formatting UnitTests")] public void XmlFormattingTests_AddSpaceToTagContentShouldLeaveNoTrailingWhitespace1() { var input = "Lorem ipsum dolor sit amet, consectetur adipiscing elit."; @@ -96,8 +94,8 @@ public void XmlFormattingTests_AddSpaceToTagContentShouldLeaveNoTrailingWhitespa }); } - [TestMethod] - [TestCategory("Formatting UnitTests")] + [Fact] + [Trait("Category", "Formatting UnitTests")] public void XmlFormattingTests_AddSpaceToTagContentShouldLeaveNoTrailingWhitespace2() { var input = @@ -119,8 +117,8 @@ public void XmlFormattingTests_AddSpaceToTagContentShouldLeaveNoTrailingWhitespa }); } - [TestMethod] - [TestCategory("Formatting UnitTests")] + [Fact] + [Trait("Category", "Formatting UnitTests")] public void XmlFormattingTests_AllRootLevelTagsOnNewLine() { var input = "abcabc"; @@ -131,8 +129,8 @@ public void XmlFormattingTests_AllRootLevelTagsOnNewLine() CommentFormatHelper.AssertEqualAfterFormat(input, expected); } - [TestMethod] - [TestCategory("Formatting UnitTests")] + [Fact] + [Trait("Category", "Formatting UnitTests")] public void XmlFormattingTests_BreakAllTags() { var input = ""; @@ -149,8 +147,8 @@ public void XmlFormattingTests_BreakAllTags() }); } - [TestMethod] - [TestCategory("Formatting UnitTests")] + [Fact] + [Trait("Category", "Formatting UnitTests")] public void XmlFormattingTests_BreakLongParagraphs() { var input = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus nisi neque, placerat sed neque vitae."; @@ -165,8 +163,8 @@ public void XmlFormattingTests_BreakLongParagraphs() CommentFormatHelper.AssertEqualAfterFormat(input, expected, o => o.WrapColumn = 60); } - [TestMethod] - [TestCategory("Formatting UnitTests")] + [Fact] + [Trait("Category", "Formatting UnitTests")] public void XmlFormattingTests_BreakTagsWhenContainsParagraphs() { var input = "test"; @@ -182,8 +180,8 @@ public void XmlFormattingTests_BreakTagsWhenContainsParagraphs() /// If XML tag indenting is set, this should not affect any literal content. However, content /// after the literal should be indented as normal. /// - [TestMethod] - [TestCategory("Formatting UnitTests")] + [Fact] + [Trait("Category", "Formatting UnitTests")] public void XmlFormattingTests_DoesIndentAfterLiteralContent() { var input = @@ -215,8 +213,8 @@ public void XmlFormattingTests_DoesIndentAfterLiteralContent() CommentFormatHelper.AssertEqualAfterFormat(result, expected); } - [TestMethod] - [TestCategory("Formatting UnitTests")] + [Fact] + [Trait("Category", "Formatting UnitTests")] public void XmlFormattingTests_DoesNotIndentCloseTag() { var input = ""; @@ -239,8 +237,8 @@ public void XmlFormattingTests_DoesNotIndentCloseTag() /// If XML tag indenting is set, this should not affect any literal content. Since whitespace /// is preserved on literals, this would increase the indenting with every pass. /// - [TestMethod] - [TestCategory("Formatting UnitTests")] + [Fact] + [Trait("Category", "Formatting UnitTests")] public void XmlFormattingTests_DoesNotIndentLiteralContent() { var input = @@ -274,38 +272,38 @@ public void XmlFormattingTests_DoesNotIndentLiteralContent() CommentFormatHelper.AssertEqualAfterFormat(result, expected); } - [TestMethod] - [TestCategory("Formatting UnitTests")] + [Fact] + [Trait("Category", "Formatting UnitTests")] public void XmlFormattingTests_DoNotAutoCollapseTags() { CommentFormatHelper.AssertEqualAfterFormat(""); } - [TestMethod] - [TestCategory("Formatting UnitTests")] + [Fact] + [Trait("Category", "Formatting UnitTests")] public void XmlFormattingTests_DoNotAutoExpandTags() { CommentFormatHelper.AssertEqualAfterFormat("", o => o.Xml.Default.SpaceSelfClosing = false); } - [TestMethod] - [TestCategory("Formatting UnitTests")] + [Fact] + [Trait("Category", "Formatting UnitTests")] public void XmlFormattingTests_HyperlinkBetweenWords() { var input = "" + Environment.NewLine + "Look at this http://foo pretty link." + Environment.NewLine + ""; CommentFormatHelper.AssertEqualAfterFormat(input); } - [TestMethod] - [TestCategory("Formatting UnitTests")] + [Fact] + [Trait("Category", "Formatting UnitTests")] public void XmlFormattingTests_HyperlinkOnNewLine() { var input = "" + Environment.NewLine + "http://foo" + Environment.NewLine + ""; CommentFormatHelper.AssertEqualAfterFormat(input); } - [TestMethod] - [TestCategory("Formatting UnitTests")] + [Fact] + [Trait("Category", "Formatting UnitTests")] public void XmlFormattingTests_IndentsXml() { var input = "Lorem ipsum dolor sit amet."; @@ -320,8 +318,8 @@ public void XmlFormattingTests_IndentsXml() CommentFormatHelper.AssertEqualAfterFormat(input, expected); } - [TestMethod] - [TestCategory("Formatting UnitTests")] + [Fact] + [Trait("Category", "Formatting UnitTests")] public void XmlFormattingTests_IndentsXmlMultiLevel() { var input = "Lorem ipsum dolor Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus nisi neque, placerat sed neque vitae. sit amet."; @@ -342,8 +340,8 @@ public void XmlFormattingTests_IndentsXmlMultiLevel() }); } - [TestMethod] - [TestCategory("Formatting UnitTests")] + [Fact] + [Trait("Category", "Formatting UnitTests")] public void XmlFormattingTests_IndentsXmlSingleLevel() { var input = "Lorem ipsum dolor Lorem ipsum dolor sit amet. sit amet."; @@ -365,8 +363,8 @@ public void XmlFormattingTests_IndentsXmlSingleLevel() /// Test to make sure there is no spacing is added between an inline XML tag directly /// followed by interpunction. /// - [TestMethod] - [TestCategory("Formatting UnitTests")] + [Fact] + [Trait("Category", "Formatting UnitTests")] public void XmlFormattingTests_InterpunctionNoSpacing() { var input = "Line with ."; @@ -374,8 +372,8 @@ public void XmlFormattingTests_InterpunctionNoSpacing() CommentFormatHelper.AssertEqualAfterFormat(input, o => o.Xml.Default.SpaceSelfClosing = false); } - [TestMethod] - [TestCategory("Formatting UnitTests")] + [Fact] + [Trait("Category", "Formatting UnitTests")] public void XmlFormattingTests_KeepShortParagraphs() { var input = @@ -393,8 +391,8 @@ public void XmlFormattingTests_KeepShortParagraphs() CommentFormatHelper.AssertEqualAfterFormat(input, expected); } - [TestMethod] - [TestCategory("Formatting UnitTests")] + [Fact] + [Trait("Category", "Formatting UnitTests")] public void XmlFormattingTests_RemoveSpaceFromInsideTags() { var input = ""; @@ -403,8 +401,8 @@ public void XmlFormattingTests_RemoveSpaceFromInsideTags() CommentFormatHelper.AssertEqualAfterFormat(input, expected, o => o.Xml.Default.SpaceSelfClosing = false); } - [TestMethod] - [TestCategory("Formatting UnitTests")] + [Fact] + [Trait("Category", "Formatting UnitTests")] public void XmlFormattingTests_RemoveSpaceFromTagContent() { var input = " test "; @@ -413,8 +411,8 @@ public void XmlFormattingTests_RemoveSpaceFromTagContent() CommentFormatHelper.AssertEqualAfterFormat(input, expected, o => o.Xml.Default.SpaceContent = false); } - [TestMethod] - [TestCategory("Formatting UnitTests")] + [Fact] + [Trait("Category", "Formatting UnitTests")] public void XmlFormattingTests_SplitAlwaysOnSingleTag() { var input = ""; @@ -431,8 +429,8 @@ public void XmlFormattingTests_SplitAlwaysOnSingleTag() }); } - [TestMethod] - [TestCategory("Formatting UnitTests")] + [Fact] + [Trait("Category", "Formatting UnitTests")] public void XmlFormattingTests_SplitsTagsWhenLineDoesNotFit() { var input = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus nisi neque, placerat sed neque vitae"; @@ -448,8 +446,8 @@ public void XmlFormattingTests_SplitsTagsWhenLineDoesNotFit() }); } - [TestMethod] - [TestCategory("Formatting UnitTests")] + [Fact] + [Trait("Category", "Formatting UnitTests")] public void XmlFormattingTests_TagCase_Keep() { var input = ""; @@ -457,8 +455,8 @@ public void XmlFormattingTests_TagCase_Keep() CommentFormatHelper.AssertEqualAfterFormat(input, o => o.Xml.Default.Case = XmlTagCase.Keep); } - [TestMethod] - [TestCategory("Formatting UnitTests")] + [Fact] + [Trait("Category", "Formatting UnitTests")] public void XmlFormattingTests_TagCase_Lower() { var input = ""; @@ -466,8 +464,8 @@ public void XmlFormattingTests_TagCase_Lower() CommentFormatHelper.AssertEqualAfterFormat(input, expected, o => o.Xml.Default.Case = XmlTagCase.LowerCase); } - [TestMethod] - [TestCategory("Formatting UnitTests")] + [Fact] + [Trait("Category", "Formatting UnitTests")] public void XmlFormattingTests_TagCase_Upper() { var input = ""; @@ -479,8 +477,8 @@ public void XmlFormattingTests_TagCase_Upper() /// If XML tag indenting is set, this should not affect any literal content. Since whitespace /// is preserved on literals, this would increase the indenting with every pass. /// - [TestMethod] - [TestCategory("Formatting UnitTests")] + [Fact] + [Trait("Category", "Formatting UnitTests")] public void XmlFormattingTests_Literal_DoesNotIndent() { var input = @@ -516,8 +514,8 @@ public void XmlFormattingTests_Literal_DoesNotIndent() /// If XML tag indenting is set, this should not affect any literal content. however, content /// after the literal should be indented as normal. /// - [TestMethod] - [TestCategory("Formatting UnitTests")] + [Fact] + [Trait("Category", "Formatting UnitTests")] public void XmlFormattingTests_Literal_IndentsAfterContent() { var input = @@ -555,8 +553,8 @@ public void XmlFormattingTests_Literal_IndentsAfterContent() }); } - [TestMethod] - [TestCategory("Formatting UnitTests")] + [Fact] + [Trait("Category", "Formatting UnitTests")] public void XmlFormattingTests_Literal_KeepFormatting() { var input = diff --git a/CodeMaid.UnitTests/Helpers/CodeItemTypeComparerTests.cs b/CodeMaid.UnitTests/Helpers/CodeItemTypeComparerTests.cs index 0fa12a30e..badd41ff1 100644 --- a/CodeMaid.UnitTests/Helpers/CodeItemTypeComparerTests.cs +++ b/CodeMaid.UnitTests/Helpers/CodeItemTypeComparerTests.cs @@ -1,22 +1,20 @@ using EnvDTE80; -using Microsoft.VisualStudio.TestTools.UnitTesting; using NSubstitute; using SteveCadwallader.CodeMaid.Helpers; using SteveCadwallader.CodeMaid.Model.CodeItems; using SteveCadwallader.CodeMaid.Properties; +using Xunit; namespace SteveCadwallader.CodeMaid.UnitTests.Helpers { - [TestClass] public class CodeItemTypeComparerTests { - [TestInitialize] - public void TestInitialize() + public CodeItemTypeComparerTests() { Settings.Default.Reset(); } - [TestMethod] + [Fact] public void ShouldSortItemsOfTheSameTypeByName() { BaseCodeItem itemB = Create("b", 1); @@ -25,10 +23,10 @@ public void ShouldSortItemsOfTheSameTypeByName() int result = comparer.Compare(itemA, itemB); - Assert.IsTrue(result < 0); + Assert.True(result < 0); } - [TestMethod] + [Fact] public void ShouldSortItemsOfTheSameTypeByOffset() { BaseCodeItem itemB = Create("b", 1); @@ -37,10 +35,10 @@ public void ShouldSortItemsOfTheSameTypeByOffset() int result = comparer.Compare(itemA, itemB); - Assert.IsTrue(result > 0); + Assert.True(result > 0); } - [TestMethod] + [Fact] public void ShouldSortByGroupType() { BaseCodeItem method = Create("a", 1); @@ -49,10 +47,10 @@ public void ShouldSortByGroupType() int result = comparer.Compare(field, method); - Assert.IsTrue(result < 0); + Assert.True(result < 0); } - [TestMethod] + [Fact] public void ShouldSortByExplicitInterfaceMemberName() { CodeItemMethod methodZ = CreateExplicitMethod("Interface", "Z", 1); @@ -62,10 +60,10 @@ public void ShouldSortByExplicitInterfaceMemberName() Settings.Default.Reorganizing_ExplicitMembersAtEnd = false; int result = comparer.Compare(methodX, methodZ); - Assert.IsTrue(result < 0); + Assert.True(result < 0); } - [TestMethod] + [Fact] public void ShouldPlaceExplicitInterfaceMembersAtTheEndOfTheGroup() { CodeItemMethod methodA = CreateExplicitMethod("Interface", "A", 1); @@ -75,7 +73,7 @@ public void ShouldPlaceExplicitInterfaceMembersAtTheEndOfTheGroup() Settings.Default.Reorganizing_ExplicitMembersAtEnd = true; int result = comparer.Compare(methodB, methodA); - Assert.IsTrue(result < 0); + Assert.True(result < 0); } private static T Create(string name, int offset) where T : BaseCodeItem, new() diff --git a/CodeMaid.UnitTests/Helpers/CodeReorganizationManagerTests.cs b/CodeMaid.UnitTests/Helpers/CodeReorganizationManagerTests.cs new file mode 100644 index 000000000..ea7762a0a --- /dev/null +++ b/CodeMaid.UnitTests/Helpers/CodeReorganizationManagerTests.cs @@ -0,0 +1,227 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Runtime.Serialization; +using EnvDTE; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Threading; +using NSubstitute; +using SteveCadwallader.CodeMaid.Logic.Reorganizing; +using SteveCadwallader.CodeMaid.Model.CodeItems; +using SteveCadwallader.CodeMaid.Properties; +using Xunit; + +namespace SteveCadwallader.CodeMaid.UnitTests.Helpers +{ + public class CodeReorganizationManagerTests + { + private readonly CodeReorganizationManager _manager; + + public CodeReorganizationManagerTests() + { + Settings.Default.Reset(); + + InitializeUIThread(); + + // Use FormatterServices.GetUninitializedObject to create an uninitialized instance, + // to avoid initializing other VS components when calling the constructor because Package is null. + _manager = (CodeReorganizationManager)FormatterServices.GetUninitializedObject(typeof(CodeReorganizationManager)); + } + + [Fact] + public void ShouldReorganizeChildren_WhenParentIsEnum_ReturnsFalse() + { + var parent = new CodeItemEnum(); + + var result = InvokeShouldReorganizeChildren(parent); + + Assert.False(result); + } + + [Fact] + public void ShouldReorganizeChildren_WhenParentIsClassWithoutAttributes_ReturnsTrue() + { + var parent = new CodeItemClass(); + + var result = InvokeShouldReorganizeChildren(parent); + + Assert.True(result); + } + + [Fact] + public void ShouldReorganizeChildren_WhenParentHasComImportAttribute_ReturnsFalse() + { + var parent = new CodeItemClass(); + SetMockAttributes(parent, new[] { "System.Runtime.InteropServices.ComImportAttribute" }); + + var result = InvokeShouldReorganizeChildren(parent); + + Assert.False(result); + } + + [Fact] + public void ShouldReorganizeChildren_WhenParentHasStructLayoutAttribute_ReturnsFalse() + { + var parent = new CodeItemClass(); + SetMockAttributes(parent, new[] { "System.Runtime.InteropServices.StructLayoutAttribute" }); + + var result = InvokeShouldReorganizeChildren(parent); + + Assert.False(result); + } + + [Fact] + public void ShouldReorganizeChildren_WhenParentHasOtherAttributes_ReturnsTrue() + { + var parent = new CodeItemClass(); + SetMockAttributes(parent, new[] { "System.SerializableAttribute" }); + + var result = InvokeShouldReorganizeChildren(parent); + + Assert.True(result); + } + + [Fact] + public void RegionsFlatten_WhenKeepMembersWithinRegionsIsTrue_ReturnsOriginalList() + { + Settings.Default.Reorganizing_KeepMembersWithinRegions = true; + + var field1 = new CodeItemField { Name = "Field1" }; + var subRegion = new CodeItemRegion { Name = "SubRegion" }; + var codeItems = new List { field1, subRegion }; + + var result = InvokeRegionsFlatten(codeItems).ToList(); + + Assert.Equal(2, result.Count); + Assert.Contains(field1, result); + Assert.Contains(subRegion, result); + } + + [Fact] + public void RegionsFlatten_WhenKeepMembersWithinRegionsIsFalse_FlattensAllRegions() + { + Settings.Default.Reorganizing_KeepMembersWithinRegions = false; + + var field1 = new CodeItemField { Name = "Field1" }; + var field2 = new CodeItemField { Name = "Field2" }; + var method1 = new CodeItemMethod { Name = "Method1" }; + + var subRegion = new CodeItemRegion { Name = "SubRegion" }; + subRegion.Children.Add(method1); + + var mainRegion = new CodeItemRegion { Name = "MainRegion" }; + mainRegion.Children.Add(field2); + mainRegion.Children.Add(subRegion); + + var codeItems = new List { field1, mainRegion }; + + var result = InvokeRegionsFlatten(codeItems).ToList(); + + // 預期僅包含 field1, field2, method1,不應包含 any Region + Assert.Equal(3, result.Count); + Assert.Contains(field1, result); + Assert.Contains(field2, result); + Assert.Contains(method1, result); + Assert.All(result, item => Assert.False(IsRegion(item))); + } + + private static bool IsRegion(BaseCodeItem item) + { + return item is CodeItemRegion; + } + + private static void InitializeUIThread() + { + // 強迫 ThreadHelper.Generic 被 lazy-init 初始化,使其內部實例不為 null + var genericValue = ThreadHelper.Generic; + + // 將當前執行測試方法的執行緒註冊為 UI 執行緒 +#pragma warning disable VSSDK005 + var context = new JoinableTaskContext(System.Threading.Thread.CurrentThread); +#pragma warning restore VSSDK005 + + // 取得當前測試執行緒的 Dispatcher + var dispatcher = System.Windows.Threading.Dispatcher.CurrentDispatcher; + + // 1. 強制透過反射設定 ThreadHelper 的私有 static JoinableTaskContext 與 uiThreadDispatcher 欄位 + var fields = typeof(ThreadHelper).GetFields(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); + foreach (var f in fields) + { + if (f.FieldType == typeof(JoinableTaskContext)) + { + f.SetValue(null, context); + } + else if (f.FieldType == typeof(System.Windows.Threading.Dispatcher)) + { + f.SetValue(null, dispatcher); + } + } + + // 2. 設定 _generic 實例的所有 JoinableTaskContext 與 uiThreadDispatcher 欄位 + if (genericValue != null) + { + var instFields = typeof(ThreadHelper).GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); + foreach (var f in instFields) + { + if (f.FieldType == typeof(JoinableTaskContext)) + { + f.SetValue(genericValue, context); + } + else if (f.FieldType == typeof(System.Windows.Threading.Dispatcher)) + { + f.SetValue(genericValue, dispatcher); + } + } + } + } + + private bool InvokeShouldReorganizeChildren(BaseCodeItemElement parent) + { + InitializeUIThread(); + + var method = typeof(CodeReorganizationManager).GetMethod("ShouldReorganizeChildren", + BindingFlags.NonPublic | BindingFlags.Instance); + if (method == null) + { + throw new InvalidOperationException("Could not find private method ShouldReorganizeChildren"); + } + return (bool)method.Invoke(_manager, new object[] { parent }); + } + + private IEnumerable InvokeRegionsFlatten(IEnumerable codeItems) + { + var method = typeof(CodeReorganizationManager).GetMethod("RegionsFlatten", + BindingFlags.NonPublic | BindingFlags.Instance); + if (method == null) + { + throw new InvalidOperationException("Could not find private method RegionsFlatten"); + } + return (IEnumerable)method.Invoke(_manager, new object[] { codeItems }); + } + + private void SetMockAttributes(BaseCodeItemElement element, string[] attributeFullNames) + { + var mockAttributes = Substitute.For(); + var attributesList = new List(); + + foreach (var fullName in attributeFullNames) + { + var mockAttr = Substitute.For(); + mockAttr.FullName.Returns(fullName); + attributesList.Add(mockAttr); + } + + mockAttributes.GetEnumerator().Returns(attributesList.GetEnumerator()); + ((System.Collections.IEnumerable)mockAttributes).GetEnumerator().Returns(attributesList.GetEnumerator()); + + var field = typeof(BaseCodeItemElement).GetField("_Attributes", + BindingFlags.NonPublic | BindingFlags.Instance); + if (field == null) + { + throw new InvalidOperationException("Could not find field _Attributes in BaseCodeItemElement"); + } + field.SetValue(element, new Lazy(() => mockAttributes)); + } + } +} diff --git a/CodeMaid.UnitTests/Helpers/FileHeaderHelperTests.cs b/CodeMaid.UnitTests/Helpers/FileHeaderHelperTests.cs index d904f2610..a7b4e2f1b 100644 --- a/CodeMaid.UnitTests/Helpers/FileHeaderHelperTests.cs +++ b/CodeMaid.UnitTests/Helpers/FileHeaderHelperTests.cs @@ -1,217 +1,229 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using NUnit.Framework; using SteveCadwallader.CodeMaid.Helpers; using System; using System.Collections.Generic; using System.Text.RegularExpressions; -using Assert = NUnit.Framework.Assert; +using Xunit; namespace SteveCadwallader.CodeMaid.UnitTests.Helpers { // remark: EnvDTE only counts 1 character per newline - [TestClass] public class FileHeaderHelperTests { - [TestCase(CodeLanguage.CSharp, "// some CSharp test header\r\n")] - [TestCase(CodeLanguage.CSharp, "/* some C# \r\n* test\r\n header\r\n *\r\n *\r\n* !!! */\r\n")] - [TestCase(CodeLanguage.JavaScript, "// some JavaScript test header\r\n")] - [TestCase(CodeLanguage.JavaScript, "/* some JS \r\n* test \r\n header !!! */\r\n")] - [TestCase(CodeLanguage.LESS, "// some LESS test header\r\n")] - [TestCase(CodeLanguage.LESS, "/* some LESS \r\n* test \r\n header !!! */\r\n")] - [TestCase(CodeLanguage.SCSS, "// some SCSS test header\r\n")] - [TestCase(CodeLanguage.SCSS, "/* some SCSS\r\n * test\r\n header !!! */\r\n")] - [TestCase(CodeLanguage.TypeScript, "// some TypeScript test header\r\n")] - [TestCase(CodeLanguage.TypeScript, "/* some TypeScript\r\n* test\r\n header !!! */\r\n")] - [TestCase(CodeLanguage.HTML, "\r\n")] - [TestCase(CodeLanguage.HTML, "")] - [TestCase(CodeLanguage.XAML, "\r\n")] - [TestCase(CodeLanguage.XAML, "")] - [TestCase(CodeLanguage.XML, "\r\n")] - [TestCase(CodeLanguage.XML, "")] - [TestCase(CodeLanguage.CSS, "/* some CSS test header */\r\n")] - [TestCase(CodeLanguage.CSS, "/* some CSS \r\ntest \r\nheader */\r\n")] - [TestCase(CodeLanguage.CPlusPlus, "// some CPlusPlus test header\r\n")] - [TestCase(CodeLanguage.CPlusPlus, "/* some C++ \r\ntest\r\n header */\r\n")] - [TestCase(CodeLanguage.PHP, "// some PHP test header\r\n")] - [TestCase(CodeLanguage.PHP, "# some PHP test header\r\n")] - [TestCase(CodeLanguage.PHP, "/* some PHP \r\n test\r\nheader */")] - [TestCase(CodeLanguage.PowerShell, "# some PowerShell test header\r\n")] - [TestCase(CodeLanguage.PowerShell, "<# some PowerShell\r\ntest\r\n header ! #>")] - [TestCase(CodeLanguage.R, "# some R test header\r\n")] - [TestCase(CodeLanguage.FSharp, "// some F# test header\r\n")] - [TestCase(CodeLanguage.FSharp, "(* some F#\r\n test \r\nheader\r\n*)")] - [TestCase(CodeLanguage.VisualBasic, "' some PowerShell test header\r\n")] + [InlineData(CodeLanguage.CSharp, "// some CSharp test header\r\n")] + [InlineData(CodeLanguage.CSharp, "/* some C# \r\n* test\r\n header\r\n *\r\n *\r\n* !!! */\r\n")] + [InlineData(CodeLanguage.JavaScript, "// some JavaScript test header\r\n")] + [InlineData(CodeLanguage.JavaScript, "/* some JS \r\n* test \r\n header !!! */\r\n")] + [InlineData(CodeLanguage.LESS, "// some LESS test header\r\n")] + [InlineData(CodeLanguage.LESS, "/* some LESS \r\n* test \r\n header !!! */\r\n")] + [InlineData(CodeLanguage.SCSS, "// some SCSS test header\r\n")] + [InlineData(CodeLanguage.SCSS, "/* some SCSS\r\n * test\r\n header !!! */\r\n")] + [InlineData(CodeLanguage.TypeScript, "// some TypeScript test header\r\n")] + [InlineData(CodeLanguage.TypeScript, "/* some TypeScript\r\n* test\r\n header !!! */\r\n")] + [InlineData(CodeLanguage.HTML, "\r\n")] + [InlineData(CodeLanguage.HTML, "")] + [InlineData(CodeLanguage.XAML, "\r\n")] + [InlineData(CodeLanguage.XAML, "")] + [InlineData(CodeLanguage.XML, "\r\n")] + [InlineData(CodeLanguage.XML, "")] + [InlineData(CodeLanguage.CSS, "/* some CSS test header */\r\n")] + [InlineData(CodeLanguage.CSS, "/* some CSS \r\ntest \r\nheader */\r\n")] + [InlineData(CodeLanguage.CPlusPlus, "// some CPlusPlus test header\r\n")] + [InlineData(CodeLanguage.CPlusPlus, "/* some C++ \r\ntest\r\n header */\r\n")] + [InlineData(CodeLanguage.PHP, "// some PHP test header\r\n")] + [InlineData(CodeLanguage.PHP, "# some PHP test header\r\n")] + [InlineData(CodeLanguage.PHP, "/* some PHP \r\n test\r\nheader */")] + [InlineData(CodeLanguage.PowerShell, "# some PowerShell test header\r\n")] + [InlineData(CodeLanguage.PowerShell, "<# some PowerShell\r\ntest\r\n header ! #>")] + [InlineData(CodeLanguage.R, "# some R test header\r\n")] + [InlineData(CodeLanguage.FSharp, "// some F# test header\r\n")] + [InlineData(CodeLanguage.FSharp, "(* some F#\r\n test \r\nheader\r\n*)")] + [InlineData(CodeLanguage.VisualBasic, "' some PowerShell test header\r\n")] + [Theory] public void GetHeaderLengthLanguage(CodeLanguage language, string text) { var headerLength = FileHeaderHelper.GetHeaderLength(language, text); - Assert.IsTrue(headerLength > 0, $"Expecting value > 0, found {headerLength}"); + Assert.True(headerLength > 0, $"Expecting value > 0, found {headerLength}"); } - [TestCase(CodeLanguage.CSharp, "# some CSharp test header\r\n")] - [TestCase(CodeLanguage.JavaScript, "\r\n")] - [TestCase(CodeLanguage.LESS, "' some LESS test header\r\n")] - [TestCase(CodeLanguage.SCSS, "# some SCSS test header\r\n")] - [TestCase(CodeLanguage.TypeScript, "\r\n")] - [TestCase(CodeLanguage.HTML, "// some HTML test header\r\n")] - [TestCase(CodeLanguage.XAML, "/* some XAML test header */\r\n")] - [TestCase(CodeLanguage.XML, "' some XML test header\r\n")] - [TestCase(CodeLanguage.CSS, "// some CSS test header\r\n")] - [TestCase(CodeLanguage.CPlusPlus, "# some CPlusPlus test header\r\n")] - [TestCase(CodeLanguage.PHP, "' some PHP test header\r\n")] - [TestCase(CodeLanguage.PowerShell, "/* some PowerShell\r\ntest\r\n header ! */")] - [TestCase(CodeLanguage.R, "// some R test header\r\n")] - [TestCase(CodeLanguage.FSharp, "/* some F#\r\n test \r\nheader\r\n*/")] - [TestCase(CodeLanguage.VisualBasic, "// some PowerShell test header\r\n")] + [InlineData(CodeLanguage.CSharp, "# some CSharp test header\r\n")] + [InlineData(CodeLanguage.JavaScript, "\r\n")] + [InlineData(CodeLanguage.LESS, "' some LESS test header\r\n")] + [InlineData(CodeLanguage.SCSS, "# some SCSS test header\r\n")] + [InlineData(CodeLanguage.TypeScript, "\r\n")] + [InlineData(CodeLanguage.HTML, "// some HTML test header\r\n")] + [InlineData(CodeLanguage.XAML, "/* some XAML test header */\r\n")] + [InlineData(CodeLanguage.XML, "' some XML test header\r\n")] + [InlineData(CodeLanguage.CSS, "// some CSS test header\r\n")] + [InlineData(CodeLanguage.CPlusPlus, "# some CPlusPlus test header\r\n")] + [InlineData(CodeLanguage.PHP, "' some PHP test header\r\n")] + [InlineData(CodeLanguage.PowerShell, "/* some PowerShell\r\ntest\r\n header ! */")] + [InlineData(CodeLanguage.R, "// some R test header\r\n")] + [InlineData(CodeLanguage.FSharp, "/* some F#\r\n test \r\nheader\r\n*/")] + [InlineData(CodeLanguage.VisualBasic, "// some PowerShell test header\r\n")] + [Theory] public void GetHeaderLengthLanguageWithWrongTags(CodeLanguage language, string text) { var headerLength = FileHeaderHelper.GetHeaderLength(language, text); - Assert.IsTrue(headerLength == 0, $"Expecting 0, found {headerLength}"); + Assert.True(headerLength == 0, $"Expecting 0, found {headerLength}"); } - [TestCase("/* */", "/*", "*/")] - [TestCase("/* \r\n Copyright © 2021 \r\n */", "/*", "*/")] - [TestCase("", "")] - [TestCase("", "")] - [TestCase("<# #>", "<#", "#>")] - [TestCase("<# //////////////// \r\n Copyright © 2021 \r\n //////////////// #>", "<#", "#>")] - [TestCase("(* *)", "(*", "*)")] - [TestCase("(* ~~~~ \r\n ~~ \r\n Copyright © 2021 \r\n ~~ \r\n ~~~~ *)", "(*", "*)")] + [InlineData("/* */", "/*", "*/")] + [InlineData("/* \r\n Copyright © 2021 \r\n */", "/*", "*/")] + [InlineData("", "")] + [InlineData("", "")] + [InlineData("<# #>", "<#", "#>")] + [InlineData("<# //////////////// \r\n Copyright © 2021 \r\n //////////////// #>", "<#", "#>")] + [InlineData("(* *)", "(*", "*)")] + [InlineData("(* ~~~~ \r\n ~~ \r\n Copyright © 2021 \r\n ~~ \r\n ~~~~ *)", "(*", "*)")] + [Theory] public void GetHeaderLengthMultiLine(string text, string tagStart, string tagEnd) { var headerLength = FileHeaderHelper.GetHeaderLength(text, tagStart, tagEnd, false); var expectedLength = text.Length - Regex.Matches(text, Environment.NewLine).Count + 1; - Assert.IsTrue(headerLength == expectedLength, $"Expecting {expectedLength}, found {headerLength}"); + Assert.True(headerLength == expectedLength, $"Expecting {expectedLength}, found {headerLength}"); } - [TestCase("using System;\r\n/* */", "/*", "*/", 6)] - [TestCase("using EnvDTE;\r\nusing System;\r\nusing SteveCadwallader.CodeMaid.Helpers;\r\n/* \r\n Copyright © 2021 \r\n */", "/*", "*/", 29)] + [InlineData("using System;\r\n/* */", "/*", "*/", 6)] + [InlineData("using EnvDTE;\r\nusing System;\r\nusing SteveCadwallader.CodeMaid.Helpers;\r\n/* \r\n Copyright © 2021 \r\n */", "/*", "*/", 29)] + [Theory] public void GetHeaderLengthMultiLineSkipUsings(string text, string tagStart, string tagEnd, int expectedLength) { var headerLength = FileHeaderHelper.GetHeaderLength(text, tagStart, tagEnd, true); - Assert.IsTrue(headerLength == expectedLength, $"Expecting {expectedLength}, found {headerLength}"); + Assert.True(headerLength == expectedLength, $"Expecting {expectedLength}, found {headerLength}"); } - [TestCase("\r\n/* */", "/*", "*/")] - [TestCase("\r\n\r\n\r\n/* Copyright © 2021 */", "/*", "*/")] - [TestCase("\r\n\r\n", "")] + [InlineData("\r\n/* */", "/*", "*/")] + [InlineData("\r\n\r\n\r\n/* Copyright © 2021 */", "/*", "*/")] + [InlineData("\r\n\r\n", "")] + [Theory] public void GetHeaderLengthMultiLineWithEmptyLines(string text, string tagStart, string tagEnd) { var headerLength = FileHeaderHelper.GetHeaderLength(text, tagStart, tagEnd, false); var expectedLength = text.Length - Regex.Matches(text, Environment.NewLine).Count + 1; - Assert.IsTrue(headerLength == expectedLength, $"Expecting {expectedLength}, found {headerLength}"); + Assert.True(headerLength == expectedLength, $"Expecting {expectedLength}, found {headerLength}"); } - [TestCase("using EnvDTE;\r\nusing System;\r\nusing SteveCadwallader.CodeMaid.Helpers;\r\n\r\n\r\n/* \r\n Copyright © 2021 \r\n */", "/*", "*/", 29)] - [TestCase("using System;\r\n\r\n", "", 9)] + [InlineData("using EnvDTE;\r\nusing System;\r\nusing SteveCadwallader.CodeMaid.Helpers;\r\n\r\n\r\n/* \r\n Copyright © 2021 \r\n */", "/*", "*/", 29)] + [InlineData("using System;\r\n\r\n", "", 9)] + [Theory] public void GetHeaderLengthMultiLineWithEmptyLinesSkipUsings(string text, string tagStart, string tagEnd, int expectedLength) { var headerLength = FileHeaderHelper.GetHeaderLength(text, tagStart, tagEnd, true); - Assert.IsTrue(headerLength == expectedLength, $"Expecting {expectedLength}, found {headerLength}"); + Assert.True(headerLength == expectedLength, $"Expecting {expectedLength}, found {headerLength}"); } - [TestCase("", "/*", "*/")] - [TestCase("some text", "/*", "*/")] - [TestCase("/* \r\n \r\n ", "/*", "*/")] - [TestCase("/* header */", "/*", "*>")] - [TestCase("*/ ", "/*", "*/")] + [InlineData("", "/*", "*/")] + [InlineData("some text", "/*", "*/")] + [InlineData("/* \r\n \r\n ", "/*", "*/")] + [InlineData("/* header */", "/*", "*>")] + [InlineData("*/ ", "/*", "*/")] + [Theory] public void GetHeaderLengthMultiLineZero(string text, string tagStart, string tagEnd) { var headerLength = FileHeaderHelper.GetHeaderLength(text, tagStart, tagEnd, false); - Assert.IsTrue(headerLength == 0, $"Expecting 0, found {headerLength}"); + Assert.True(headerLength == 0, $"Expecting 0, found {headerLength}"); } - [TestCase("using System;", "/*", "*/")] - [TestCase("using System;\r\n/* \r\n \r\n ", "/*", "*/")] - [TestCase("using System;\r\n/* header */", "/*", "*>")] - [TestCase("using System;\r\n*/ ", "/*", "*/")] + [InlineData("using System;", "/*", "*/")] + [InlineData("using System;\r\n/* \r\n \r\n ", "/*", "*/")] + [InlineData("using System;\r\n/* header */", "/*", "*>")] + [InlineData("using System;\r\n*/ ", "/*", "*/")] + [Theory] public void GetHeaderLengthMultiLineZeroSkipUsings(string text, string tagStart, string tagEnd) { var headerLength = FileHeaderHelper.GetHeaderLength(text, tagStart, tagEnd, true); - Assert.IsTrue(headerLength == 0, $"Expecting 0, found {headerLength}"); + Assert.True(headerLength == 0, $"Expecting 0, found {headerLength}"); } - [TestCase("//", "// header fdsfndksjnfe\r\n")] - [TestCase("#", "# header eropf opekfropze jaqozerijfg uihgsdfidnffdsfg\r\n")] - [TestCase("'", "'header sdvq qsudg bqudgybuydzeau _(àç_ç*ù$^$*ùàyguozadgzao\r\n")] - [TestCase("//", "// ====================\r\n// Copyright\r\n// ====================\r\n")] - [TestCase("#", "# ==============\r\n# Copyright !\r\n# ~~\r\n# ==============\r\n")] - [TestCase("'", "' ==============\r\n' some text\r\n' Copyright !\r\n' ==============\r\n")] + [InlineData("//", "// header fdsfndksjnfe\r\n")] + [InlineData("#", "# header eropf opekfropze jaqozerijfg uihgsdfidnffdsfg\r\n")] + [InlineData("'", "'header sdvq qsudg bqudgybuydzeau _(àç_ç*ù$^$*ùàyguozadgzao\r\n")] + [InlineData("//", "// ====================\r\n// Copyright\r\n// ====================\r\n")] + [InlineData("#", "# ==============\r\n# Copyright !\r\n# ~~\r\n# ==============\r\n")] + [InlineData("'", "' ==============\r\n' some text\r\n' Copyright !\r\n' ==============\r\n")] + [Theory] public void GetHeaderLengthMultiSingleLine(string tag, string text) { var headerLength = FileHeaderHelper.GetHeaderLength(text, tag, false); var expectedLength = text.Length - Regex.Matches(text, Environment.NewLine).Count; - Assert.IsTrue(headerLength == expectedLength, $"Expecting {expectedLength}, found {headerLength}"); + Assert.True(headerLength == expectedLength, $"Expecting {expectedLength}, found {headerLength}"); } - [TestCase("//", "using System;\r\n// header\r\nnamespace ", 11)] - [TestCase("//", "using System;\r\n// header I\r\n// header II\r\nnamespace ", 26)] + [InlineData("//", "using System;\r\n// header\r\nnamespace ", 11)] + [InlineData("//", "using System;\r\n// header I\r\n// header II\r\nnamespace ", 26)] + [Theory] public void GetHeaderLengthMultiSingleLineSkipUsings(string tag, string text, int expectedLength) { var headerLength = FileHeaderHelper.GetHeaderLength(text, tag, true); - Assert.IsTrue(headerLength == expectedLength, $"Expecting {expectedLength}, found {headerLength}"); + Assert.True(headerLength == expectedLength, $"Expecting {expectedLength}, found {headerLength}"); } - [TestCase("//", "// header \r\nnamespace\r\npublic class Test\r\n", 12)] - [TestCase("//", "// header \r\n// more header \r\n some code\r\n", 28)] - [TestCase("#", "# some header\r\nnamespace\r\npublic class Test\r\n", 14)] - [TestCase("'", "' some header\r\nnamespace\r\npublic class Test\r\n", 14)] + [InlineData("//", "// header \r\nnamespace\r\npublic class Test\r\n", 12)] + [InlineData("//", "// header \r\n// more header \r\n some code\r\n", 28)] + [InlineData("#", "# some header\r\nnamespace\r\npublic class Test\r\n", 14)] + [InlineData("'", "' some header\r\nnamespace\r\npublic class Test\r\n", 14)] + [Theory] public void GetHeaderLengthMultiSingleLineWithCode(string tag, string text, int expectedLength) { var headerLength = FileHeaderHelper.GetHeaderLength(text, tag, false); - Assert.IsTrue(headerLength == expectedLength, $"Expecting {expectedLength}, found {headerLength}"); + Assert.True(headerLength == expectedLength, $"Expecting {expectedLength}, found {headerLength}"); } - [TestCase("//", "using System;\r\n\r\n// header \r\nnamespace System.Windows;\r\npublic class Test\r\n", 13)] - [TestCase("//", "using EnvDTE;\r\nusing System;\r\nusing SteveCadwallader.CodeMaid.Helpers;\r\n\r\n\r\n// header \r\n// more header \r\nnamespace SteveCadwallader.CodeMaid;\r\n", 29)] - [TestCase("//", "using System;\r\n\r\n// header \r\n[assembly: AssemblyTitle(\"SteveCadwallader.CodeMaid.UnitTests\")]\r\nnamespace ", 13)] + [InlineData("//", "using System;\r\n\r\n// header \r\nnamespace System.Windows;\r\npublic class Test\r\n", 13)] + [InlineData("//", "using EnvDTE;\r\nusing System;\r\nusing SteveCadwallader.CodeMaid.Helpers;\r\n\r\n\r\n// header \r\n// more header \r\nnamespace SteveCadwallader.CodeMaid;\r\n", 29)] + [InlineData("//", "using System;\r\n\r\n// header \r\n[assembly: AssemblyTitle(\"SteveCadwallader.CodeMaid.UnitTests\")]\r\nnamespace ", 13)] + [Theory] public void GetHeaderLengthMultiSingleLineWithCodeSkipUsings(string tag, string text, int expectedLength) { var headerLength = FileHeaderHelper.GetHeaderLength(text, tag, true); - Assert.IsTrue(headerLength == expectedLength, $"Expecting {expectedLength}, found {headerLength}"); + Assert.True(headerLength == expectedLength, $"Expecting {expectedLength}, found {headerLength}"); } - [TestCase("//", "\r\n// header \r\n// header\r\nnamespace\r\n//not header\r\n public class Test\r\n", 23)] - [TestCase("//", "\r\n\r\n\r\n// header \r\n// more header \r\n some code\r\n", 31)] - [TestCase("#", "\r\n# some header\r\nnamespace\r\npublic class Test\r\n", 15)] - [TestCase("'", "\r\n' some header\r\nnamespace\r\npublic class Test\r\n", 15)] + [InlineData("//", "\r\n// header \r\n// header\r\nnamespace\r\n//not header\r\n public class Test\r\n", 23)] + [InlineData("//", "\r\n\r\n\r\n// header \r\n// more header \r\n some code\r\n", 31)] + [InlineData("#", "\r\n# some header\r\nnamespace\r\npublic class Test\r\n", 15)] + [InlineData("'", "\r\n' some header\r\nnamespace\r\npublic class Test\r\n", 15)] + [Theory] public void GetHeaderLengthMultiSingleLineWithEmptyLines(string tag, string text, int expectedLength) { var headerLength = FileHeaderHelper.GetHeaderLength(text, tag, false); - Assert.IsTrue(headerLength == expectedLength, $"Expecting {expectedLength}, found {headerLength}"); + Assert.True(headerLength == expectedLength, $"Expecting {expectedLength}, found {headerLength}"); } - [TestCase("//", "using System;\r\n\r\n\r\n// header \r\n// header\r\nnamespace \r\n//not header\r\n public class Test\r\n", 23)] - [TestCase("//", "using EnvDTE;\r\nusing System;\r\nusing SteveCadwallader.CodeMaid.Helpers;\r\n// header \r\n// more header \r\n namespace System.Text;\r\n{\r\n", 29)] + [InlineData("//", "using System;\r\n\r\n\r\n// header \r\n// header\r\nnamespace \r\n//not header\r\n public class Test\r\n", 23)] + [InlineData("//", "using EnvDTE;\r\nusing System;\r\nusing SteveCadwallader.CodeMaid.Helpers;\r\n// header \r\n// more header \r\n namespace System.Text;\r\n{\r\n", 29)] + [Theory] public void GetHeaderLengthMultiSingleLineWithEmptyLinesSkipUsings(string tag, string text, int expectedLength) { var headerLength = FileHeaderHelper.GetHeaderLength(text, tag, true); - Assert.IsTrue(headerLength == expectedLength, $"Expecting {expectedLength}, found {headerLength}"); + Assert.True(headerLength == expectedLength, $"Expecting {expectedLength}, found {headerLength}"); } - [TestCase("using ", "", "", 0)] - [TestCase(" using", "using System;\r\nnamespace CodeMaid", "namespace ", 0)] - [TestCase("using ", " using System;\r\nnamespace CodeMaid", "namespace ", 1)] - [TestCase("using ", "using System;\r\nusing System.Collections;\r\n\r\nnamespace CodeMaid", "namespace ", 2)] - [TestCase("using ", "using System;\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\nusing System.Collections;\r\n\r\nnamespace CodeMaid", "namespace ", 10)] + [InlineData("using ", "", "", 0)] + [InlineData(" using", "using System;\r\nnamespace CodeMaid", "namespace ", 0)] + [InlineData("using ", " using System;\r\nnamespace CodeMaid", "namespace ", 1)] + [InlineData("using ", "using System;\r\nusing System.Collections;\r\n\r\nnamespace CodeMaid", "namespace ", 2)] + [InlineData("using ", "using System;\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\nusing System.Collections;\r\n\r\nnamespace CodeMaid", "namespace ", 10)] + [Theory] public void GetNbLinesToSkip(string patternToFind, string text, string limit, int expectedNbLines) { var nbLines = FileHeaderHelper.GetNbLinesToSkip(patternToFind, text, new List() { limit }); - Assert.IsTrue(nbLines == expectedNbLines, $"Expecting {expectedNbLines}, found {expectedNbLines}"); + Assert.True(nbLines == expectedNbLines, $"Expecting {expectedNbLines}, found {expectedNbLines}"); } } } \ No newline at end of file diff --git a/CodeMaid.UnitTests/Helpers/SettingsMonitorTests.cs b/CodeMaid.UnitTests/Helpers/SettingsMonitorTests.cs index e31b17f8d..b18b5d917 100644 --- a/CodeMaid.UnitTests/Helpers/SettingsMonitorTests.cs +++ b/CodeMaid.UnitTests/Helpers/SettingsMonitorTests.cs @@ -1,21 +1,19 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using SteveCadwallader.CodeMaid.Helpers; +using SteveCadwallader.CodeMaid.Helpers; using SteveCadwallader.CodeMaid.Properties; using System.Linq; using System.Threading.Tasks; +using Xunit; namespace SteveCadwallader.CodeMaid.UnitTests.Helpers { - [TestClass] public class SettingsMonitorTests { - [TestInitialize] - public void TestInitialize() + public SettingsMonitorTests() { Settings.Default.Reset(); } - [TestMethod] + [Fact] public async Task CallbackShouldBeCalledAtOnce() { var monitor = new SettingsMonitor(Settings.Default, null); @@ -28,10 +26,10 @@ await monitor.WatchAsync(s => s.Feature_CleanupAllCode, _ => return Task.CompletedTask; }); - Assert.AreEqual(/*Initial Call Times*/1, callbackTimes); + Assert.Equal(/*Initial Call Times*/1, callbackTimes); } - [TestMethod] + [Fact] public async Task CallbackShouldNotBeCalledIfSettingNotChanged() { Settings.Default.Feature_CleanupAllCode = false; @@ -50,11 +48,11 @@ await monitor.WatchAsync(s => s.Feature_CleanupAllCode, v => Settings.Default.Feature_CleanupAllCode = false; Settings.Default.Save(); - Assert.AreEqual(/*Initial Call Times*/1 + 0, callbackTimes); - Assert.AreEqual(Settings.Default.Feature_CleanupAllCode, value); + Assert.Equal(/*Initial Call Times*/1 + 0, callbackTimes); + Assert.Equal(value, Settings.Default.Feature_CleanupAllCode); } - [TestMethod] + [Fact] public async Task CallbackShouldBeCalledOnceSettingChanged() { Settings.Default.Feature_CleanupAllCode = false; @@ -73,11 +71,11 @@ await monitor.WatchAsync(s => s.Feature_CleanupAllCode, v => Settings.Default.Feature_CleanupAllCode = true; Settings.Default.Save(); - Assert.AreEqual(/*Initial Call Times*/ 1 + 1, callbackTimes); - Assert.AreEqual(Settings.Default.Feature_CleanupAllCode, value); + Assert.Equal(/*Initial Call Times*/ 1 + 1, callbackTimes); + Assert.Equal(value, Settings.Default.Feature_CleanupAllCode); } - [TestMethod] + [Fact] public async Task AllCallbacksShouldBeCalledOnceSettingChanged() { Settings.Default.Feature_CleanupAllCode = false; @@ -103,13 +101,13 @@ await monitor.WatchAsync(s => s.Feature_CleanupAllCode, v => Settings.Default.Feature_CleanupAllCode = true; Settings.Default.Save(); - Assert.AreEqual(/*Initial Call Times*/1 + 1, callbackTimes1); - Assert.AreEqual(/*Initial Call Times*/1 + 1, callbackTimes2); - Assert.AreEqual(Settings.Default.Feature_CleanupAllCode, value1); - Assert.AreEqual(Settings.Default.Feature_CleanupAllCode, value2); + Assert.Equal(/*Initial Call Times*/1 + 1, callbackTimes1); + Assert.Equal(/*Initial Call Times*/1 + 1, callbackTimes2); + Assert.Equal(value1, Settings.Default.Feature_CleanupAllCode); + Assert.Equal(value2, Settings.Default.Feature_CleanupAllCode); } - [TestMethod] + [Fact] public async Task CallbackShouldBeCalledOnceAnyWatchedSettingChanged() { Settings.Default.Feature_CleanupAllCode = false; @@ -134,8 +132,8 @@ await monitor.WatchAsync(new[]{ Settings.Default.Feature_CleanupSelectedCode = false; Settings.Default.Save(); - Assert.AreEqual(/*Initial Call Times*/1 + 1, callbackTimes); - Assert.IsTrue(values.All(v => v == false)); + Assert.Equal(/*Initial Call Times*/1 + 1, callbackTimes); + Assert.True(values.All(v => v == false)); } } } \ No newline at end of file diff --git a/CodeMaid.UnitTests/MemberTypeSettingTests.cs b/CodeMaid.UnitTests/MemberTypeSettingTests.cs index b9a1efea4..65a0ebc5d 100644 --- a/CodeMaid.UnitTests/MemberTypeSettingTests.cs +++ b/CodeMaid.UnitTests/MemberTypeSettingTests.cs @@ -1,32 +1,31 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using SteveCadwallader.CodeMaid.Helpers; +using SteveCadwallader.CodeMaid.Helpers; +using Xunit; namespace SteveCadwallader.CodeMaid.UnitTests { - [TestClass] public class MemberTypeSettingTests { - [TestMethod] + [Fact] public void CanSerializeMemberTypeSetting() { var memberTypeSetting = new MemberTypeSetting("Fields", "Member Variables", 1); - Assert.IsNotNull(memberTypeSetting); + Assert.NotNull(memberTypeSetting); var serializedString = (string)memberTypeSetting; - Assert.IsFalse(string.IsNullOrWhiteSpace(serializedString)); + Assert.False(string.IsNullOrWhiteSpace(serializedString)); } - [TestMethod] + [Fact] public void CanDeserializeMemberTypeSetting() { const string serializedString = @"Fields||1||Member Variables"; var memberTypeSetting = (MemberTypeSetting)serializedString; - Assert.IsNotNull(memberTypeSetting); - Assert.AreEqual(memberTypeSetting.DefaultName, "Fields"); - Assert.AreEqual(memberTypeSetting.EffectiveName, "Member Variables"); - Assert.AreEqual(memberTypeSetting.Order, 1); + Assert.NotNull(memberTypeSetting); + Assert.Equal("Fields", memberTypeSetting.DefaultName); + Assert.Equal("Member Variables", memberTypeSetting.EffectiveName); + Assert.Equal(1, memberTypeSetting.Order); } } -} \ No newline at end of file +} diff --git a/CodeMaid.UnitTests/Properties/AssemblyInfo.cs b/CodeMaid.UnitTests/Properties/AssemblyInfo.cs index 96acd2c65..b844a4d5b 100644 --- a/CodeMaid.UnitTests/Properties/AssemblyInfo.cs +++ b/CodeMaid.UnitTests/Properties/AssemblyInfo.cs @@ -1,3 +1,8 @@ using System.Reflection; +using Xunit; -[assembly: AssemblyTitle("SteveCadwallader.CodeMaid.UnitTests")] \ No newline at end of file +[assembly: AssemblyTitle("SteveCadwallader.CodeMaid.UnitTests")] + +// 停用測試並行化:各測試類別共享全域 Settings.Default 靜態狀態, +// 序列執行以避免競態(等同原先 MSTest 的預設行為)。 +[assembly: CollectionBehavior(DisableTestParallelization = true)] \ No newline at end of file diff --git a/CodeMaid.UnitTests/app.config b/CodeMaid.UnitTests/app.config index af636acd8..b53bb5b71 100644 --- a/CodeMaid.UnitTests/app.config +++ b/CodeMaid.UnitTests/app.config @@ -6,18 +6,10 @@ - - - - - - - - diff --git a/CodeMaid.VS2022/CodeMaid.VS2022.csproj b/CodeMaid.VS2022/CodeMaid.VS2022.csproj index 10e8a1cc0..ef3abd5a7 100644 --- a/CodeMaid.VS2022/CodeMaid.VS2022.csproj +++ b/CodeMaid.VS2022/CodeMaid.VS2022.csproj @@ -16,7 +16,7 @@ SteveCadwallader.CodeMaid.VS2022 True ..\CodeMaid.snk - v4.7.2 + v4.8 en-US true true @@ -28,6 +28,7 @@ $(DevEnvDir)devenv.exe /rootsuffix Exp latest + true @@ -51,6 +52,9 @@ false False + + CodeMaid.ico + @@ -85,6 +89,7 @@ + Integration\Images\IDE\progressToolWindow.png @@ -96,6 +101,11 @@ true Always + + zh-Hant\extension.vsixlangpack + true + Always + source.extension.vsixmanifest @@ -603,6 +613,11 @@ Menus.ctmenu CodeMaid.vsct + + CodeMaid.zh-Hant.vsct + Menus.ctmenu + CodeMaid.vsct + CodeMaid.en-US.vsct Menus.ctmenu @@ -704,6 +719,9 @@ VSPackage.en-US.resources source.extension.vsixmanifest + + source.extension.cs + True True @@ -716,18 +734,24 @@ VSPackage.zh-Hans.resources source.extension.vsixmanifest + + true + VSPackage.zh-Hant.resources + source.extension.vsixmanifest + 1.0.2 - 17.0.0-previews-2-31512-422 + 17.14.40265 runtime all + compile; build; native; contentfiles; analyzers; buildtransitive - 17.0.3177-preview3 + 18.5.40034 runtime; build; native; contentfiles; analyzers all diff --git a/CodeMaid.VS2022/CodeMaid.ico b/CodeMaid.VS2022/CodeMaid.ico new file mode 100644 index 000000000..a1aa855d8 Binary files /dev/null and b/CodeMaid.VS2022/CodeMaid.ico differ diff --git a/CodeMaid.VS2022/source.extension.zh-Hant.resx b/CodeMaid.VS2022/source.extension.zh-Hant.resx new file mode 100644 index 000000000..2be126619 --- /dev/null +++ b/CodeMaid.VS2022/source.extension.zh-Hant.resx @@ -0,0 +1,130 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + CodeMaid + + + CodeMaid 是一個開源 Visual Studio 延伸模組,用於清理和簡化我們的 C#、C++、F#、VB、PHP、PowerShell、R、JSON、XAML、XML、ASP、HTML、CSS、LESS、SCSS、JavaScript 和TypeScript 程式碼。 + + + + source.extension.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + \ No newline at end of file diff --git a/CodeMaid.ico b/CodeMaid.ico new file mode 100644 index 000000000..a1aa855d8 Binary files /dev/null and b/CodeMaid.ico differ diff --git a/CodeMaid.png b/CodeMaid.png new file mode 100644 index 000000000..9dc237950 Binary files /dev/null and b/CodeMaid.png differ diff --git a/CodeMaid/CodeMaid.csproj b/CodeMaid/CodeMaid.csproj index e0ea74656..476b24f3d 100644 --- a/CodeMaid/CodeMaid.csproj +++ b/CodeMaid/CodeMaid.csproj @@ -53,6 +53,9 @@ false False + + CodeMaid.ico + @@ -86,8 +89,10 @@ + + true Always @@ -340,12 +345,13 @@ 1.0.2 - 16.10.31321.278 + 17.14.40265 runtime all + compile; build; native; contentfiles; analyzers; buildtransitive - 16.11.35 + 18.5.40034 runtime; build; native; contentfiles; analyzers all diff --git a/CodeMaid/CodeMaid.ico b/CodeMaid/CodeMaid.ico new file mode 100644 index 000000000..a1aa855d8 Binary files /dev/null and b/CodeMaid/CodeMaid.ico differ diff --git a/CodeMaid/CodeMaid.zh-Hant.vsct b/CodeMaid/CodeMaid.zh-Hant.vsct new file mode 100644 index 000000000..7801ae48b --- /dev/null +++ b/CodeMaid/CodeMaid.zh-Hant.vsct @@ -0,0 +1,363 @@ + + + + + + + + + + + + + + + &CodeMaid + &CodeMaid + + + + DefaultDocked + + CodeMaid Spade Toolbar + CodeMaid Spade 工具列 + + + + + CodeMaid Spade Context Menu + CodeMaid Spade 內容選單 + + + + + + CodeMaid + + + + + + CodeMaid + + + + + + CodeMaid + + + + + + CodeMaid + + + + + + CodeMaid + + + + + + CodeMaid + + + + + + CodeMaid + + + + + + CodeMaid + + + + + + CodeMaid + + + + + + CodeMaid + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/CodeMaid/zh-Hant/extension.vsixlangpack b/CodeMaid/zh-Hant/extension.vsixlangpack new file mode 100644 index 000000000..977c0d02b --- /dev/null +++ b/CodeMaid/zh-Hant/extension.vsixlangpack @@ -0,0 +1,7 @@ + + + + CodeMaid + CodeMaid 是一個開源的 Visual Studio 擴充套件,用於清理和簡化我們的 C#、C++、F#、VB、PHP、PowerShell、R、JSON、XAML、XML、ASP、HTML、CSS、LESS、SCSS、JavaScript 和 TypeScript 程式碼。 + + \ No newline at end of file diff --git a/CodeMaidShared/CodeMaidPackage.cs b/CodeMaidShared/CodeMaidPackage.cs index f8a1ef571..8466eca7d 100644 --- a/CodeMaidShared/CodeMaidPackage.cs +++ b/CodeMaidShared/CodeMaidPackage.cs @@ -1,4 +1,12 @@ -using EnvDTE; +using System; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Runtime.InteropServices; +using System.Threading; +using System.Windows; +using System.Windows.Threading; +using EnvDTE; using EnvDTE80; using Microsoft.VisualStudio; using Microsoft.VisualStudio.ComponentModelHost; @@ -11,14 +19,6 @@ using SteveCadwallader.CodeMaid.UI; using SteveCadwallader.CodeMaid.UI.ToolWindows.BuildProgress; using SteveCadwallader.CodeMaid.UI.ToolWindows.Spade; -using System; -using System.Diagnostics; -using System.Globalization; -using System.IO; -using System.Runtime.InteropServices; -using System.Threading; -using System.Windows; -using System.Windows.Threading; using Task = System.Threading.Tasks.Task; using VSColorTheme = Microsoft.VisualStudio.PlatformUI.VSColorTheme; @@ -114,6 +114,7 @@ public Document ActiveDocument { get { + ThreadHelper.ThrowIfNotOnUIThread(); try { return IDE.ActiveDocument; @@ -218,6 +219,7 @@ protected override async Task InitializeAsync(CancellationToken cancellationToke /// private void OnDispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e) { + ThreadHelper.ThrowIfNotOnUIThread(); if (!Settings.Default.General_DiagnosticsMode) return; OutputWindowHelper.ExceptionWriteLine("Diagnostics mode caught and marked as handled the following DispatcherUnhandledException raised in Visual Studio", e.Exception); @@ -229,6 +231,7 @@ private void OnDispatcherUnhandledException(object sender, DispatcherUnhandledEx /// private void OnSolutionClosedShowStartPage() { + ThreadHelper.ThrowIfNotOnUIThread(); if (!Settings.Default.General_ShowStartPageOnSolutionClose) return; IDE.ExecuteCommand("View.StartPage"); @@ -280,6 +283,7 @@ private async Task RegisterCommandsAsync() /// private async Task RegisterEventListenersAsync() { + await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); var codeModelManager = CodeModelManager.GetInstance(this); var settingsContextHelper = SettingsContextHelper.GetInstance(this); @@ -353,4 +357,4 @@ await SettingsMonitor.WatchAsync(s => s.Feature_CollapseAllSolutionExplorer, on } } } -} \ No newline at end of file +} diff --git a/CodeMaidShared/CodeMaidShared.projitems b/CodeMaidShared/CodeMaidShared.projitems index a9033ee27..48ece1d9b 100644 --- a/CodeMaidShared/CodeMaidShared.projitems +++ b/CodeMaidShared/CodeMaidShared.projitems @@ -384,5 +384,8 @@ Resources.resx + + Resources.resx + \ No newline at end of file diff --git a/CodeMaidShared/Helpers/ActiveDocumentRestorer.cs b/CodeMaidShared/Helpers/ActiveDocumentRestorer.cs index 11cb1668e..59a660dc9 100644 --- a/CodeMaidShared/Helpers/ActiveDocumentRestorer.cs +++ b/CodeMaidShared/Helpers/ActiveDocumentRestorer.cs @@ -1,5 +1,6 @@ -using EnvDTE; +using EnvDTE; using System; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Helpers { @@ -17,6 +18,7 @@ internal class ActiveDocumentRestorer : IDisposable /// The hosting package. internal ActiveDocumentRestorer(CodeMaidPackage package) { + ThreadHelper.ThrowIfNotOnUIThread(); Package = package; StartTracking(); @@ -31,6 +33,7 @@ internal ActiveDocumentRestorer(CodeMaidPackage package) /// internal void StartTracking() { + ThreadHelper.ThrowIfNotOnUIThread(); // Cache the active document. TrackedDocument = Package.ActiveDocument; } @@ -40,6 +43,7 @@ internal void StartTracking() /// internal void RestoreTrackedDocument() { + ThreadHelper.ThrowIfNotOnUIThread(); if (TrackedDocument != null && Package.ActiveDocument != TrackedDocument) { TrackedDocument.Activate(); @@ -56,6 +60,7 @@ internal void RestoreTrackedDocument() /// public void Dispose() { + ThreadHelper.ThrowIfNotOnUIThread(); RestoreTrackedDocument(); } @@ -75,4 +80,4 @@ public void Dispose() #endregion Private Properties } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Helpers/CodeCommentHelper.cs b/CodeMaidShared/Helpers/CodeCommentHelper.cs index 3fa641929..9d89cbb83 100644 --- a/CodeMaidShared/Helpers/CodeCommentHelper.cs +++ b/CodeMaidShared/Helpers/CodeCommentHelper.cs @@ -1,8 +1,9 @@ -using EnvDTE; +using EnvDTE; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text.RegularExpressions; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Helpers { @@ -92,6 +93,7 @@ internal static Regex GetCommentRegex(CodeLanguage codeLanguage, bool includePre /// public static IEnumerable GetTaskListTokens(CodeMaidPackage package) { + ThreadHelper.ThrowIfNotOnUIThread(); var settings = package.IDE.Properties["Environment", "TaskList"]; var tokens = settings.Item("CommentTokens").Value as string[]; if (tokens == null || tokens.Length < 1) @@ -109,6 +111,7 @@ internal static bool IsCommentLine(EditPoint point) internal static Match LineMatchesRegex(EditPoint point, Regex regex) { + ThreadHelper.ThrowIfNotOnUIThread(); var line = point.GetLine(); var match = regex.Match(line); return match; @@ -119,4 +122,4 @@ internal static string SpaceToFake(string value) return value.Replace(Spacer, KeepTogetherSpacer); } } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Helpers/CodeElementHelper.cs b/CodeMaidShared/Helpers/CodeElementHelper.cs index 11d6a353b..cff415eff 100644 --- a/CodeMaidShared/Helpers/CodeElementHelper.cs +++ b/CodeMaidShared/Helpers/CodeElementHelper.cs @@ -1,7 +1,8 @@ -using EnvDTE; +using EnvDTE; using EnvDTE80; using System; using System.Text.RegularExpressions; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Helpers { @@ -17,6 +18,7 @@ internal static class CodeElementHelper /// The calculated complexity. internal static int CalculateComplexity(CodeElement element) { + ThreadHelper.ThrowIfNotOnUIThread(); EditPoint startPoint = element.StartPoint.CreateEditPoint(); string functionText = startPoint.GetText(element.EndPoint); @@ -78,6 +80,7 @@ internal static string GetAccessModifierKeyword(vsCMAccess accessModifier) /// The string declaration. internal static string GetClassDeclaration(CodeClass codeClass) { + ThreadHelper.ThrowIfNotOnUIThread(); // Get the start point after the attributes. var startPoint = codeClass.GetStartPoint(vsCMPart.vsCMPartHeader); @@ -91,6 +94,7 @@ internal static string GetClassDeclaration(CodeClass codeClass) /// The string declaration. internal static string GetDelegateDeclaration(CodeDelegate codeDelegate) { + ThreadHelper.ThrowIfNotOnUIThread(); // Get the start point at the end of the attributes if there are any (vsCMPartHeader is // not available for delegates). var startPoint = codeDelegate.Attributes.Count > 0 @@ -107,6 +111,7 @@ internal static string GetDelegateDeclaration(CodeDelegate codeDelegate) /// The string declaration. internal static string GetEnumerationDeclaration(CodeEnum codeEnum) { + ThreadHelper.ThrowIfNotOnUIThread(); // Get the start point after the attributes. var startPoint = codeEnum.GetStartPoint(vsCMPart.vsCMPartHeader); @@ -120,6 +125,7 @@ internal static string GetEnumerationDeclaration(CodeEnum codeEnum) /// The string declaration. internal static string GetEventDeclaration(CodeEvent codeEvent) { + ThreadHelper.ThrowIfNotOnUIThread(); // Get the start point at the end of the attributes if there are any (vsCMPartHeader is // not available for events). var startPoint = codeEvent.Attributes.Count > 0 @@ -136,6 +142,7 @@ internal static string GetEventDeclaration(CodeEvent codeEvent) /// The string declaration. internal static string GetFieldDeclaration(CodeVariable codeField) { + ThreadHelper.ThrowIfNotOnUIThread(); // Get the start point at the end of the attributes if there are any (vsCMPartHeader is // not available for fields). var startPoint = codeField.Attributes.Count > 0 @@ -152,6 +159,7 @@ internal static string GetFieldDeclaration(CodeVariable codeField) /// The string declaration. internal static string GetInterfaceDeclaration(CodeInterface codeInterface) { + ThreadHelper.ThrowIfNotOnUIThread(); // Get the start point after the attributes. var startPoint = codeInterface.GetStartPoint(vsCMPart.vsCMPartHeader); @@ -165,6 +173,7 @@ internal static string GetInterfaceDeclaration(CodeInterface codeInterface) /// The string declaration. internal static string GetMethodDeclaration(CodeFunction codeFunction) { + ThreadHelper.ThrowIfNotOnUIThread(); // Get the start point after the attributes. var startPoint = codeFunction.GetStartPoint(vsCMPart.vsCMPartHeader); @@ -178,6 +187,7 @@ internal static string GetMethodDeclaration(CodeFunction codeFunction) /// The string declaration. internal static string GetPropertyDeclaration(CodeProperty codeProperty) { + ThreadHelper.ThrowIfNotOnUIThread(); // Get the start point at the end of the attributes if there are any (vsCMPartHeader is // not available for properties). var startPoint = codeProperty.Attributes.Count > 0 @@ -194,10 +204,11 @@ internal static string GetPropertyDeclaration(CodeProperty codeProperty) /// The string declaration. internal static string GetStructDeclaration(CodeStruct codeStruct) { + ThreadHelper.ThrowIfNotOnUIThread(); // Get the start point after the attributes. var startPoint = codeStruct.GetStartPoint(vsCMPart.vsCMPartHeader); return TextDocumentHelper.GetTextToFirstMatch(startPoint, @"[\{;]"); } } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Helpers/CodeMaidSettingsProvider.cs b/CodeMaidShared/Helpers/CodeMaidSettingsProvider.cs index e287b0200..e621839bd 100644 --- a/CodeMaidShared/Helpers/CodeMaidSettingsProvider.cs +++ b/CodeMaidShared/Helpers/CodeMaidSettingsProvider.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Configuration; using System.Xml; @@ -286,4 +286,4 @@ private static XmlNode CreateXmlValue(object serializedValue) #endregion Write Methods } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Helpers/CommandHelper.cs b/CodeMaidShared/Helpers/CommandHelper.cs index 962064e7e..c456e1074 100644 --- a/CodeMaidShared/Helpers/CommandHelper.cs +++ b/CodeMaidShared/Helpers/CommandHelper.cs @@ -1,6 +1,7 @@ -using EnvDTE; +using EnvDTE; using System; using System.Linq; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Helpers { @@ -52,6 +53,7 @@ private CommandHelper(CodeMaidPackage package) /// The found command, otherwise null. public Command FindCommand(params string[] commandNames) { + ThreadHelper.ThrowIfNotOnUIThread(); if (commandNames == null || commandNames.Length == 0) return null; return _package.IDE.Commands.OfType().FirstOrDefault(x => commandNames.Contains(x.Name)); @@ -65,6 +67,7 @@ public Command FindCommand(params string[] commandNames) /// The found command, otherwise null. public Command FindCommand(string guid, int id) { + ThreadHelper.ThrowIfNotOnUIThread(); return _package.IDE.Commands.OfType().FirstOrDefault(x => x.Guid == guid && x.ID == id); } @@ -75,6 +78,7 @@ public Command FindCommand(string guid, int id) /// The cleanup command name(s). public void ExecuteCommand(TextDocument textDocument, params string[] commandNames) { + ThreadHelper.ThrowIfNotOnUIThread(); try { var command = FindCommand(commandNames); @@ -95,4 +99,4 @@ public void ExecuteCommand(TextDocument textDocument, params string[] commandNam #endregion Methods } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Helpers/CursorPositionRestorer.cs b/CodeMaidShared/Helpers/CursorPositionRestorer.cs index 718d12a11..c4bc53eab 100644 --- a/CodeMaidShared/Helpers/CursorPositionRestorer.cs +++ b/CodeMaidShared/Helpers/CursorPositionRestorer.cs @@ -1,5 +1,6 @@ -using EnvDTE; +using EnvDTE; using System; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Helpers { @@ -17,6 +18,7 @@ internal class CursorPositionRestorer : IDisposable /// The text document. internal CursorPositionRestorer(TextDocument textDocument) { + ThreadHelper.ThrowIfNotOnUIThread(); TextDocument = textDocument; CaptureCursorPosition(); @@ -31,6 +33,7 @@ internal CursorPositionRestorer(TextDocument textDocument) /// internal void CaptureCursorPosition() { + ThreadHelper.ThrowIfNotOnUIThread(); if (TextDocument != null && TextDocument.Selection != null) { TrackedCursorPosition = new CursorPosition(TextDocument.Selection); @@ -66,6 +69,7 @@ internal void RestoreCursorPosition() /// True if the cursor position was reset, otherwise false. private bool IsCursorPositionReset() { + ThreadHelper.ThrowIfNotOnUIThread(); return TextDocument.Selection.ActivePoint.AtStartOfDocument || TextDocument.Selection.ActivePoint.AtEndOfDocument; } @@ -80,6 +84,7 @@ private bool IsCursorPositionReset() /// public void Dispose() { + ThreadHelper.ThrowIfNotOnUIThread(); RestoreCursorPosition(); } @@ -114,6 +119,7 @@ private struct CursorPosition /// The text selection. public CursorPosition(TextSelection textSelection) { + ThreadHelper.ThrowIfNotOnUIThread(); Line = textSelection.CurrentLine; Column = textSelection.CurrentColumn; } @@ -130,4 +136,4 @@ public CursorPosition(TextSelection textSelection) #endregion Private Structures } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Helpers/DocumentExtensions.cs b/CodeMaidShared/Helpers/DocumentExtensions.cs index 078d207df..70f5a8b82 100644 --- a/CodeMaidShared/Helpers/DocumentExtensions.cs +++ b/CodeMaidShared/Helpers/DocumentExtensions.cs @@ -1,4 +1,5 @@ -using EnvDTE; +using EnvDTE; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Helpers { @@ -14,6 +15,7 @@ internal static class DocumentExtensions /// A . internal static CodeLanguage GetCodeLanguage(this Document document) { + ThreadHelper.ThrowIfNotOnUIThread(); return CodeLanguageHelper.GetCodeLanguage(document.Language); } @@ -24,6 +26,7 @@ internal static CodeLanguage GetCodeLanguage(this Document document) /// The associated text document, otherwise null. internal static TextDocument GetTextDocument(this Document document) { + ThreadHelper.ThrowIfNotOnUIThread(); return document.Object("TextDocument") as TextDocument; } @@ -34,9 +37,10 @@ internal static TextDocument GetTextDocument(this Document document) /// True if the document is external, otherwise false. internal static bool IsExternal(this Document document) { + ThreadHelper.ThrowIfNotOnUIThread(); var projectItem = document.ProjectItem; return projectItem == null || projectItem.IsExternal(); } } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Helpers/EditPointExtensions.cs b/CodeMaidShared/Helpers/EditPointExtensions.cs index 98dd71960..b41bdc95e 100644 --- a/CodeMaidShared/Helpers/EditPointExtensions.cs +++ b/CodeMaidShared/Helpers/EditPointExtensions.cs @@ -1,4 +1,5 @@ -using EnvDTE; +using EnvDTE; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Helpers { @@ -14,6 +15,7 @@ internal static class EditPointExtensions /// A . internal static CodeLanguage GetCodeLanguage(this EditPoint editPoint) { + ThreadHelper.ThrowIfNotOnUIThread(); return editPoint.Parent.GetCodeLanguage(); } @@ -24,7 +26,8 @@ internal static CodeLanguage GetCodeLanguage(this EditPoint editPoint) /// The text of the edit point's line. internal static string GetLine(this EditPoint editPoint) { + ThreadHelper.ThrowIfNotOnUIThread(); return editPoint.GetLines(editPoint.Line, editPoint.Line + 1); } } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Helpers/ExplicitInterfaceImplementationHelper.cs b/CodeMaidShared/Helpers/ExplicitInterfaceImplementationHelper.cs index 243051812..83fe7902c 100644 --- a/CodeMaidShared/Helpers/ExplicitInterfaceImplementationHelper.cs +++ b/CodeMaidShared/Helpers/ExplicitInterfaceImplementationHelper.cs @@ -1,5 +1,6 @@ -using EnvDTE; +using EnvDTE; using EnvDTE80; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Helpers { @@ -26,6 +27,8 @@ public static bool IsExplicitInterfaceImplementation(CodeEvent codeEvent) return true; } + ThreadHelper.ThrowIfNotOnUIThread(); + // Otherwise, look for the element name with a preceding dot. var declaration = CodeElementHelper.GetEventDeclaration(codeEvent); var matchString = @"\." + codeEvent.Name; @@ -46,6 +49,8 @@ public static bool IsExplicitInterfaceImplementation(CodeFunction2 codeFunction) return true; } + ThreadHelper.ThrowIfNotOnUIThread(); + // Otherwise, look for the element name with a preceding dot. var declaration = CodeElementHelper.GetMethodDeclaration(codeFunction); var matchString = @"\." + codeFunction.Name; @@ -66,6 +71,8 @@ public static bool IsExplicitInterfaceImplementation(CodeProperty codeProperty) return true; } + ThreadHelper.ThrowIfNotOnUIThread(); + // Otherwise, look for the element name with a preceding dot. var declaration = CodeElementHelper.GetPropertyDeclaration(codeProperty); var matchString = @"\." + codeProperty.Name; @@ -73,4 +80,4 @@ public static bool IsExplicitInterfaceImplementation(CodeProperty codeProperty) return RegexNullSafe.IsMatch(declaration, matchString); } } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Helpers/FileHeaderHelper.cs b/CodeMaidShared/Helpers/FileHeaderHelper.cs index aa0a57b6d..9c0700e7e 100644 --- a/CodeMaidShared/Helpers/FileHeaderHelper.cs +++ b/CodeMaidShared/Helpers/FileHeaderHelper.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Helpers { @@ -17,6 +18,7 @@ internal static class FileHeaderHelper /// A file header from settings. internal static string GetFileHeaderFromSettings(TextDocument textDocument) { + ThreadHelper.ThrowIfNotOnUIThread(); switch (textDocument.GetCodeLanguage()) { case CodeLanguage.CPlusPlus: return Settings.Default.Cleaning_UpdateFileHeaderCPlusPlus; @@ -41,6 +43,7 @@ internal static string GetFileHeaderFromSettings(TextDocument textDocument) internal static HeaderPosition GetFileHeaderPositionFromSettings(TextDocument textDocument) { + ThreadHelper.ThrowIfNotOnUIThread(); switch (textDocument.GetCodeLanguage()) { case CodeLanguage.CSharp: @@ -353,4 +356,4 @@ private static IEnumerable SplitLines(string text) return text.Split(separator, StringSplitOptions.None); } } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Helpers/MemberTypeSetting.cs b/CodeMaidShared/Helpers/MemberTypeSetting.cs index 6cc8b7543..bea468b52 100644 --- a/CodeMaidShared/Helpers/MemberTypeSetting.cs +++ b/CodeMaidShared/Helpers/MemberTypeSetting.cs @@ -1,4 +1,4 @@ -using SteveCadwallader.CodeMaid.UI; +using SteveCadwallader.CodeMaid.UI; using System; using System.Text.RegularExpressions; @@ -92,4 +92,4 @@ public static explicit operator string(MemberTypeSetting memberTypeSetting) #endregion Methods } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Helpers/OutputWindowHelper.cs b/CodeMaidShared/Helpers/OutputWindowHelper.cs index 377eb369b..0eab1ce4e 100644 --- a/CodeMaidShared/Helpers/OutputWindowHelper.cs +++ b/CodeMaidShared/Helpers/OutputWindowHelper.cs @@ -1,4 +1,4 @@ -using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; using SteveCadwallader.CodeMaid.Properties; using System; @@ -32,14 +32,17 @@ internal static class OutputWindowHelper /// An optional exception that was handled. internal static void DiagnosticWriteLine(string message, Exception ex = null) { - if (!Settings.Default.General_DiagnosticsMode) return; - - if (ex != null) + UIThread.RunOnUIThread(() => { - message += $": {ex}"; - } + if (!Settings.Default.General_DiagnosticsMode) return; + + if (ex != null) + { + message += $": {ex}"; + } - WriteLine(Resources.Diagnostic, message); + WriteLine(Resources.Diagnostic, message); + }); } /// @@ -49,9 +52,11 @@ internal static void DiagnosticWriteLine(string message, Exception ex = null) /// The exception that was handled. internal static void ExceptionWriteLine(string message, Exception ex) { - var exceptionMessage = $"{message}: {ex}"; - - WriteLine(Resources.HandledException, exceptionMessage); + UIThread.RunOnUIThread(() => + { + var exceptionMessage = $"{message}: {ex}"; + WriteLine(Resources.HandledException, exceptionMessage); + }); } /// @@ -60,7 +65,7 @@ internal static void ExceptionWriteLine(string message, Exception ex) /// The message. internal static void WarningWriteLine(string message) { - WriteLine(Resources.Warning, message); + UIThread.RunOnUIThread(() => WriteLine(Resources.Warning, message)); } /// @@ -100,4 +105,4 @@ private static void WriteLine(string category, string message) #endregion Methods } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Helpers/ProjectItemExtensions.cs b/CodeMaidShared/Helpers/ProjectItemExtensions.cs index 4c1cf53c2..8050dde23 100644 --- a/CodeMaidShared/Helpers/ProjectItemExtensions.cs +++ b/CodeMaidShared/Helpers/ProjectItemExtensions.cs @@ -1,6 +1,7 @@ -using EnvDTE; +using EnvDTE; using System; using System.Linq; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Helpers { @@ -16,6 +17,7 @@ internal static class ProjectItemExtensions /// The filename of the project item if available, otherwise null. internal static string GetFileName(this ProjectItem projectItem) { + ThreadHelper.ThrowIfNotOnUIThread(); try { return projectItem.FileNames[1]; @@ -34,6 +36,7 @@ internal static string GetFileName(this ProjectItem projectItem) /// The parent project item, otherwise null. internal static ProjectItem GetParentProjectItem(this ProjectItem projectItem) { + ThreadHelper.ThrowIfNotOnUIThread(); try { var parentProjectItem = projectItem.Collection?.Parent as ProjectItem; @@ -53,6 +56,7 @@ internal static ProjectItem GetParentProjectItem(this ProjectItem projectItem) /// True if the project item is external, otherwise false. internal static bool IsExternal(this ProjectItem projectItem) { + ThreadHelper.ThrowIfNotOnUIThread(); try { if (projectItem.Collection == null || !projectItem.IsPhysicalFile()) @@ -76,6 +80,7 @@ internal static bool IsExternal(this ProjectItem projectItem) /// True if the project item is a physical file, otherwise false. internal static bool IsPhysicalFile(this ProjectItem projectItem) { + ThreadHelper.ThrowIfNotOnUIThread(); try { return string.Equals(projectItem.Kind, Constants.vsProjectItemKindPhysicalFile, StringComparison.OrdinalIgnoreCase); @@ -88,4 +93,4 @@ internal static bool IsPhysicalFile(this ProjectItem projectItem) } } } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Helpers/RegexNullSafe.cs b/CodeMaidShared/Helpers/RegexNullSafe.cs index 43c18f5d5..3e0093944 100644 --- a/CodeMaidShared/Helpers/RegexNullSafe.cs +++ b/CodeMaidShared/Helpers/RegexNullSafe.cs @@ -1,4 +1,4 @@ -using System.Text.RegularExpressions; +using System.Text.RegularExpressions; namespace SteveCadwallader.CodeMaid.Helpers { @@ -25,4 +25,4 @@ public static bool IsMatch(string input, string pattern) return Regex.IsMatch(input, pattern); } } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Helpers/RegionHelper.cs b/CodeMaidShared/Helpers/RegionHelper.cs index f987097b4..e8efd0652 100644 --- a/CodeMaidShared/Helpers/RegionHelper.cs +++ b/CodeMaidShared/Helpers/RegionHelper.cs @@ -1,5 +1,6 @@ -using EnvDTE; +using EnvDTE; using System; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Helpers { @@ -12,6 +13,7 @@ internal static class RegionHelper internal static string GetRegionName(EditPoint editPoint, string regionText) { + ThreadHelper.ThrowIfNotOnUIThread(); var codeLanguage = editPoint.GetCodeLanguage(); switch (codeLanguage) { @@ -31,6 +33,7 @@ internal static string GetRegionName(EditPoint editPoint, string regionText) internal static string GetRegionTagText(EditPoint editPoint, string name = null) { + ThreadHelper.ThrowIfNotOnUIThread(); var codeLanguage = editPoint.GetCodeLanguage(); switch (codeLanguage) { @@ -49,6 +52,7 @@ internal static string GetRegionTagText(EditPoint editPoint, string name = null) internal static string GetEndRegionTagText(EditPoint editPoint) { + ThreadHelper.ThrowIfNotOnUIThread(); var codeLanguage = editPoint.GetCodeLanguage(); switch (codeLanguage) { @@ -65,6 +69,7 @@ internal static string GetEndRegionTagText(EditPoint editPoint) internal static bool LanguageSupportsUpdatingEndRegionDirectives(EditPoint editPoint) { + ThreadHelper.ThrowIfNotOnUIThread(); var codeLanguage = editPoint.GetCodeLanguage(); switch (codeLanguage) @@ -79,4 +84,4 @@ internal static bool LanguageSupportsUpdatingEndRegionDirectives(EditPoint editP #endregion Internal Methods } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Helpers/SettingsContextHelper.cs b/CodeMaidShared/Helpers/SettingsContextHelper.cs index 2a3db90e8..1ee7136b2 100644 --- a/CodeMaidShared/Helpers/SettingsContextHelper.cs +++ b/CodeMaidShared/Helpers/SettingsContextHelper.cs @@ -1,7 +1,8 @@ -using SteveCadwallader.CodeMaid.Properties; +using SteveCadwallader.CodeMaid.Properties; using System; using System.Configuration; using System.IO; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Helpers { @@ -72,6 +73,7 @@ internal static string GetUserSettingsPath() /// True if solution-specific settings were loaded, otherwise false. internal bool LoadSolutionSpecificSettings(Settings settings, bool canCreate = false) { + ThreadHelper.ThrowIfNotOnUIThread(); if (_package.IDE.Solution.IsOpen && !string.IsNullOrWhiteSpace(_package.IDE.Solution.FullName)) { var solutionPath = Path.GetDirectoryName(_package.IDE.Solution.FullName); @@ -115,6 +117,7 @@ internal async void OnSolutionClosed() internal async void OnSolutionOpened() #pragma warning restore VSTHRD100 // Avoid async void methods { + await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); if (LoadSolutionSpecificSettings(Settings.Default)) { await _package.SettingsMonitor.NotifySettingsChangedAsync(); @@ -140,4 +143,4 @@ internal bool UnloadSolutionSpecificSettings(Settings settings) return false; } } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Helpers/SettingsMonitor.cs b/CodeMaidShared/Helpers/SettingsMonitor.cs index 6f5d4c916..8af079bf4 100644 --- a/CodeMaidShared/Helpers/SettingsMonitor.cs +++ b/CodeMaidShared/Helpers/SettingsMonitor.cs @@ -72,7 +72,9 @@ internal async Task NotifySettingsChangedAsync() private object[] FindValues(string[] settings) => Array.ConvertAll(settings, key => _settings[key]); - private async void OnSettingsSaving(object sender, CancelEventArgs e) + private void OnSettingsSaving(object sender, CancelEventArgs e) => OnSettingsSavingAsync().Forget(); + + private async Task OnSettingsSavingAsync() { if (_joinableTaskFactory != null) { diff --git a/CodeMaidShared/Helpers/SolutionHelper.cs b/CodeMaidShared/Helpers/SolutionHelper.cs index 83db122d7..2beee559d 100644 --- a/CodeMaidShared/Helpers/SolutionHelper.cs +++ b/CodeMaidShared/Helpers/SolutionHelper.cs @@ -1,7 +1,8 @@ -using EnvDTE; +using EnvDTE; using System; using System.Collections.Generic; using System.Linq; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Helpers { @@ -41,6 +42,7 @@ internal static IEnumerable GetAllItemsInSolution(Solution solution) internal static IEnumerable GetItemsRecursively(object parentItem) where T : class { + ThreadHelper.ThrowIfNotOnUIThread(); if (parentItem == null) { throw new ArgumentNullException(nameof(parentItem)); @@ -74,6 +76,7 @@ internal static IEnumerable GetItemsRecursively(object parentItem) /// The enumerable set of selected project items. internal static IEnumerable GetSelectedProjectItemsRecursively(CodeMaidPackage package) { + ThreadHelper.ThrowIfNotOnUIThread(); var selectedProjectItems = new List(); var selectedUIHierarchyItems = UIHierarchyHelper.GetSelectedUIHierarchyItems(package); @@ -93,6 +96,7 @@ internal static IEnumerable GetSelectedProjectItemsRecursively(Code /// The enumerable set of similar project items. internal static IEnumerable GetSimilarProjectItems(CodeMaidPackage package, ProjectItem projectItem) { + ThreadHelper.ThrowIfNotOnUIThread(); var allItems = GetAllItemsInSolution(package.IDE.Solution); return allItems.Where(x => x.Name == projectItem.Name && x.Kind == projectItem.Kind && x.Document.FullName == projectItem.Document.FullName); @@ -109,6 +113,7 @@ internal static IEnumerable GetSimilarProjectItems(CodeMaidPackage /// An enumerable set of children, may be empty. private static IEnumerable GetChildren(object parentItem) { + ThreadHelper.ThrowIfNotOnUIThread(); // First check if the item is a solution. var solution = parentItem as Solution; if (solution?.Projects != null) @@ -145,4 +150,4 @@ private static IEnumerable GetChildren(object parentItem) #endregion Private Methods } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Helpers/TextDocumentExtensions.cs b/CodeMaidShared/Helpers/TextDocumentExtensions.cs index b9080b3be..ea94d3d62 100644 --- a/CodeMaidShared/Helpers/TextDocumentExtensions.cs +++ b/CodeMaidShared/Helpers/TextDocumentExtensions.cs @@ -1,4 +1,5 @@ -using EnvDTE; +using EnvDTE; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Helpers { @@ -14,6 +15,7 @@ internal static class TextDocumentExtensions /// A . internal static CodeLanguage GetCodeLanguage(this TextDocument document) { + ThreadHelper.ThrowIfNotOnUIThread(); return CodeLanguageHelper.GetCodeLanguage(document.Language); } @@ -24,10 +26,11 @@ internal static CodeLanguage GetCodeLanguage(this TextDocument document) /// An edit point at the cursor. internal static EditPoint GetEditPointAtCursor(this TextDocument textDocument) { + ThreadHelper.ThrowIfNotOnUIThread(); var cursor = textDocument.CreateEditPoint(); cursor.MoveToPoint(textDocument.Selection.ActivePoint); return cursor; } } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Helpers/TextDocumentHelper.cs b/CodeMaidShared/Helpers/TextDocumentHelper.cs index 03abb05c2..f7513b3a6 100644 --- a/CodeMaidShared/Helpers/TextDocumentHelper.cs +++ b/CodeMaidShared/Helpers/TextDocumentHelper.cs @@ -42,9 +42,8 @@ internal static class TextDocumentHelper internal static IEnumerable FindMatches(TextDocument textDocument, string patternString) { var matches = new List(); - UIThread.Run(() => + UIThread.RunOnUIThread(() => { - ThreadHelper.ThrowIfNotOnUIThread(); if (TryGetTextBufferAt(textDocument.Parent.FullName, out ITextBuffer textBuffer)) { @@ -69,9 +68,8 @@ internal static IEnumerable FindMatches(TextDocument textDocument, st internal static IEnumerable FindMatches(TextSelection textSelection, string patternString) { var matches = new List(); - UIThread.Run(() => + UIThread.RunOnUIThread(() => { - ThreadHelper.ThrowIfNotOnUIThread(); if (TryGetTextBufferAt(textSelection.Parent.Parent.FullName, out ITextBuffer textBuffer)) { IFinder finder = GetFinder(patternString, textBuffer); @@ -95,9 +93,8 @@ internal static IEnumerable FindMatches(TextSelection textSelection, internal static EditPoint FirstOrDefaultMatch(TextDocument textDocument, string patternString) { EditPoint result = null; - UIThread.Run(() => + UIThread.RunOnUIThread(() => { - ThreadHelper.ThrowIfNotOnUIThread(); if (TryGetTextBufferAt(textDocument.Parent.FullName, out ITextBuffer textBuffer)) { @@ -125,9 +122,8 @@ internal static bool TryFindNextMatch(EditPoint startPoint, ref EditPoint endPoi bool result = false; EditPoint resultEndPoint = null; - UIThread.Run(() => + UIThread.RunOnUIThread(() => { - ThreadHelper.ThrowIfNotOnUIThread(); if (TryGetTextBufferAt(startPoint.Parent.Parent.FullName, out ITextBuffer textBuffer)) { @@ -154,9 +150,8 @@ internal static bool TryFindNextMatch(EditPoint startPoint, ref EditPoint endPoi internal static string GetTextToFirstMatch(TextPoint startPoint, string matchString) { string result = null; - UIThread.Run(() => + UIThread.RunOnUIThread(() => { - ThreadHelper.ThrowIfNotOnUIThread(); if (TryGetTextBufferAt(startPoint.Parent.Parent.FullName, out ITextBuffer textBuffer)) { @@ -177,6 +172,8 @@ internal static string GetTextToFirstMatch(TextPoint startPoint, string matchStr /// The point. internal static void InsertBlankLineBeforePoint(EditPoint point) { + UIThread.RunOnUIThread(() => + { if (point.Line <= 1) return; point.LineUp(1); @@ -188,6 +185,7 @@ internal static void InsertBlankLineBeforePoint(EditPoint point) point.EndOfLine(); point.Insert(Environment.NewLine); } + }); } /// @@ -196,6 +194,8 @@ internal static void InsertBlankLineBeforePoint(EditPoint point) /// The point. internal static void InsertBlankLineAfterPoint(EditPoint point) { + UIThread.RunOnUIThread(() => + { if (point.AtEndOfDocument) return; point.LineDown(1); @@ -206,6 +206,7 @@ internal static void InsertBlankLineAfterPoint(EditPoint point) { point.Insert(Environment.NewLine); } + }); } /// @@ -216,6 +217,8 @@ internal static void InsertBlankLineAfterPoint(EditPoint point) /// True if the whole element should be used for centering. internal static void MoveToCodeItem(Document document, BaseCodeItem codeItem, bool centerOnWhole) { + UIThread.RunOnUIThread(() => + { var textDocument = document.GetTextDocument(); if (textDocument == null) return; @@ -259,6 +262,7 @@ internal static void MoveToCodeItem(Document document, BaseCodeItem codeItem, bo // Always set focus within the code editor window. document.Activate(); } + }); } /// @@ -268,6 +272,8 @@ internal static void MoveToCodeItem(Document document, BaseCodeItem codeItem, bo /// The code item. internal static void SelectCodeItem(Document document, BaseCodeItem codeItem) { + UIThread.RunOnUIThread(() => + { var textDocument = document.GetTextDocument(); if (textDocument == null) return; @@ -288,6 +294,7 @@ internal static void SelectCodeItem(Document document, BaseCodeItem codeItem) // Always set focus within the code editor window. document.Activate(); } + }); } /// @@ -299,9 +306,8 @@ internal static void SelectCodeItem(Document document, BaseCodeItem codeItem) /// The replacement string. internal static void SubstituteAllStringMatches(TextDocument textDocument, string patternString, string replacementString) { - UIThread.Run(() => + UIThread.RunOnUIThread(() => { - ThreadHelper.ThrowIfNotOnUIThread(); if (TryGetTextBufferAt(textDocument.Parent.FullName, out ITextBuffer textBuffer)) { @@ -320,9 +326,8 @@ internal static void SubstituteAllStringMatches(TextDocument textDocument, strin /// The replacement string. internal static void SubstituteAllStringMatches(TextSelection textSelection, string patternString, string replacementString) { - UIThread.Run(() => + UIThread.RunOnUIThread(() => { - ThreadHelper.ThrowIfNotOnUIThread(); if (TryGetTextBufferAt(textSelection.Parent.Parent.FullName, out ITextBuffer textBuffer)) { @@ -342,9 +347,8 @@ internal static void SubstituteAllStringMatches(TextSelection textSelection, str /// The replacement string. internal static void SubstituteAllStringMatches(EditPoint startPoint, EditPoint endPoint, string patternString, string replacementString) { - UIThread.Run(() => + UIThread.RunOnUIThread(() => { - ThreadHelper.ThrowIfNotOnUIThread(); if (TryGetTextBufferAt(startPoint.Parent.Parent.FullName, out ITextBuffer textBuffer)) { @@ -360,8 +364,6 @@ internal static void SubstituteAllStringMatches(EditPoint startPoint, EditPoint private static EditPoint GetEditPointForSnapshotPosition(TextDocument textDocument, ITextSnapshot textSnapshot, int position) { - ThreadHelper.ThrowIfNotOnUIThread(); - var editPoint = textDocument.CreateEditPoint(); var textSnapshotLine = textSnapshot.GetLineFromPosition(position); editPoint.MoveToLineAndOffset(textSnapshotLine.LineNumber + 1, position - textSnapshotLine.Start.Position + 1); @@ -384,8 +386,6 @@ private static IFinder GetFinder(string findWhat, string replaceWith, ITextBuffe private static Span GetSnapshotSpanForTextSelection(ITextSnapshot textSnapshot, TextSelection selection) { - ThreadHelper.ThrowIfNotOnUIThread(); - var startPosition = GetSnapshotPositionForTextPoint(textSnapshot, selection.AnchorPoint); var endPosition = GetSnapshotPositionForTextPoint(textSnapshot, selection.ActivePoint); @@ -401,16 +401,12 @@ private static Span GetSnapshotSpanForTextSelection(ITextSnapshot textSnapshot, private static int GetSnapshotPositionForTextPoint(ITextSnapshot textSnapshot, TextPoint textPoint) { - ThreadHelper.ThrowIfNotOnUIThread(); - var textSnapshotLine = textSnapshot.GetLineFromLineNumber(textPoint.Line - 1); return textSnapshotLine.Start.Position + textPoint.LineCharOffset - 1; } private static Span GetSnapshotSpanForExtent(ITextSnapshot textSnapshot, EditPoint startPoint, EditPoint endPoint) { - ThreadHelper.ThrowIfNotOnUIThread(); - var startPosition = GetSnapshotPositionForTextPoint(textSnapshot, startPoint); var endPosition = GetSnapshotPositionForTextPoint(textSnapshot, endPoint); @@ -471,4 +467,4 @@ private static bool TryGetTextBufferAt(string filePath, out ITextBuffer textBuff #endregion Private Methods } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Helpers/UIHierarchyHelper.cs b/CodeMaidShared/Helpers/UIHierarchyHelper.cs index dc7b433e5..113aaebf4 100644 --- a/CodeMaidShared/Helpers/UIHierarchyHelper.cs +++ b/CodeMaidShared/Helpers/UIHierarchyHelper.cs @@ -1,9 +1,10 @@ -using EnvDTE; +using EnvDTE; using EnvDTE80; using SteveCadwallader.CodeMaid.Properties; using System; using System.Collections.Generic; using System.Linq; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Helpers { @@ -21,6 +22,7 @@ internal static class UIHierarchyHelper /// The parent item to collapse from. internal static void CollapseRecursively(UIHierarchyItem parentItem) { + ThreadHelper.ThrowIfNotOnUIThread(); if (parentItem == null) { throw new ArgumentNullException(nameof(parentItem)); @@ -55,6 +57,7 @@ internal static void CollapseRecursively(UIHierarchyItem parentItem) /// The enumerable set of selected UI hierarchy items. internal static IEnumerable GetSelectedUIHierarchyItems(CodeMaidPackage package) { + ThreadHelper.ThrowIfNotOnUIThread(); var solutionExplorer = GetSolutionExplorer(package); return ((object[])solutionExplorer.SelectedItems).Cast().ToList(); @@ -67,6 +70,7 @@ internal static IEnumerable GetSelectedUIHierarchyItems(CodeMai /// The solution explorer. internal static UIHierarchy GetSolutionExplorer(CodeMaidPackage package) { + ThreadHelper.ThrowIfNotOnUIThread(); return package.IDE.ToolWindows.SolutionExplorer; } @@ -77,6 +81,7 @@ internal static UIHierarchy GetSolutionExplorer(CodeMaidPackage package) /// The top level (solution) UI hierarchy item, otherwise null. internal static UIHierarchyItem GetTopUIHierarchyItem(CodeMaidPackage package) { + ThreadHelper.ThrowIfNotOnUIThread(); var solutionExplorer = GetSolutionExplorer(package); return solutionExplorer.UIHierarchyItems.Count > 0 @@ -91,6 +96,7 @@ internal static UIHierarchyItem GetTopUIHierarchyItem(CodeMaidPackage package) /// True if there are expanded children, false otherwise. internal static bool HasExpandedChildren(UIHierarchyItem parentItem) { + ThreadHelper.ThrowIfNotOnUIThread(); if (parentItem == null) { throw new ArgumentNullException(nameof(parentItem)); @@ -111,6 +117,7 @@ internal static bool HasExpandedChildren(UIHierarchyItem parentItem) /// True if the item should be collapsed, otherwise false. private static bool ShouldCollapseItem(UIHierarchyItem parentItem) { + ThreadHelper.ThrowIfNotOnUIThread(); // Make sure not to collapse the solution, causes odd behavior. if (parentItem.Object is Solution) { @@ -135,4 +142,4 @@ private static bool ShouldCollapseItem(UIHierarchyItem parentItem) #endregion Private Methods } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Helpers/UIThread.cs b/CodeMaidShared/Helpers/UIThread.cs index df45539e7..481c12ed5 100644 --- a/CodeMaidShared/Helpers/UIThread.cs +++ b/CodeMaidShared/Helpers/UIThread.cs @@ -21,17 +21,36 @@ public static void Run(Action action) } } + public static void RunOnUIThread(Action action) + { + Run(() => + { + ThreadHelper.ThrowIfNotOnUIThread(); + action(); + }); + } + public static T Run(Func func) { if (ThreadHelper.CheckAccess()) { return func(); } + return ThreadHelper.JoinableTaskFactory.Run(async () => { await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); return func(); }); } + + public static T RunOnUIThread(Func func) + { + return Run(() => + { + ThreadHelper.ThrowIfNotOnUIThread(); + return func(); + }); + } } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Helpers/UndoTransactionHelper.cs b/CodeMaidShared/Helpers/UndoTransactionHelper.cs index 53f449275..a26390292 100644 --- a/CodeMaidShared/Helpers/UndoTransactionHelper.cs +++ b/CodeMaidShared/Helpers/UndoTransactionHelper.cs @@ -1,5 +1,6 @@ -using SteveCadwallader.CodeMaid.Properties; +using SteveCadwallader.CodeMaid.Properties; using System; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Helpers { @@ -40,6 +41,7 @@ public UndoTransactionHelper(CodeMaidPackage package, string transactionName) /// The action to be performed wihin a catch block. public void Run(Action tryAction, Action catchAction = null) { + ThreadHelper.ThrowIfNotOnUIThread(); bool shouldCloseUndoContext = false; // Start an undo transaction (unless inside one already or within an auto save context). @@ -80,4 +82,4 @@ public void Run(Action tryAction, Action catchAction = null) #endregion Methods } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Integration/Commands/AboutCommand.cs b/CodeMaidShared/Integration/Commands/AboutCommand.cs index f7875136e..24ca76cd3 100644 --- a/CodeMaidShared/Integration/Commands/AboutCommand.cs +++ b/CodeMaidShared/Integration/Commands/AboutCommand.cs @@ -1,5 +1,6 @@ using SteveCadwallader.CodeMaid.UI.Dialogs.About; using System.Threading.Tasks; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Integration.Commands { @@ -38,9 +39,10 @@ public static async Task InitializeAsync(CodeMaidPackage package) /// protected override void OnExecute() { + ThreadHelper.ThrowIfNotOnUIThread(); base.OnExecute(); new AboutWindow().ShowModal(); } } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Integration/Commands/BaseCommand.cs b/CodeMaidShared/Integration/Commands/BaseCommand.cs index ab42920fa..020fd4509 100644 --- a/CodeMaidShared/Integration/Commands/BaseCommand.cs +++ b/CodeMaidShared/Integration/Commands/BaseCommand.cs @@ -64,6 +64,7 @@ protected virtual void OnBeforeQueryStatus() /// protected virtual void OnExecute() { + ThreadHelper.ThrowIfNotOnUIThread(); OutputWindowHelper.DiagnosticWriteLine($"{GetType().Name}.OnExecute invoked"); } @@ -85,8 +86,9 @@ private static void BaseCommand_BeforeQueryStatus(object sender, EventArgs e) /// The instance containing the event data. private static void BaseCommand_Execute(object sender, EventArgs e) { + ThreadHelper.ThrowIfNotOnUIThread(); var command = sender as BaseCommand; command?.OnExecute(); } } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Integration/Commands/BuildProgressToolWindowCommand.cs b/CodeMaidShared/Integration/Commands/BuildProgressToolWindowCommand.cs index 49f001e03..1bef64e3f 100644 --- a/CodeMaidShared/Integration/Commands/BuildProgressToolWindowCommand.cs +++ b/CodeMaidShared/Integration/Commands/BuildProgressToolWindowCommand.cs @@ -2,6 +2,7 @@ using Microsoft.VisualStudio.Shell.Interop; using SteveCadwallader.CodeMaid.Properties; using System.Threading.Tasks; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Integration.Commands { @@ -31,6 +32,7 @@ private IVsWindowFrame BuildProgressWindowFrame { get { + ThreadHelper.ThrowIfNotOnUIThread(); var buildProgress = Package.BuildProgressForceLoad; if (buildProgress != null) { @@ -54,6 +56,7 @@ public static async Task InitializeAsync(CodeMaidPackage package) public override async Task SwitchAsync(bool on) { + await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); await base.SwitchAsync(on); if (!on) @@ -69,6 +72,7 @@ public override async Task SwitchAsync(bool on) /// The action. internal void OnBuildBegin(vsBuildScope scope, vsBuildAction action) { + ThreadHelper.ThrowIfNotOnUIThread(); var buildProgress = Package.BuildProgressForceLoad; if (buildProgress != null) { @@ -88,6 +92,7 @@ internal void OnBuildBegin(vsBuildScope scope, vsBuildAction action) /// The action. internal void OnBuildDone(vsBuildScope scope, vsBuildAction action) { + ThreadHelper.ThrowIfNotOnUIThread(); var buildProgress = Package.BuildProgressForceLoad; if (buildProgress != null) { @@ -150,6 +155,7 @@ protected override void OnExecute() /// The frame. private static void DockWindowIfFloating(IVsWindowFrame frame) { + ThreadHelper.ThrowIfNotOnUIThread(); // Get the current tool window frame mode. frame.GetProperty((int)__VSFPROPID.VSFPROPID_FrameMode, out object currentFrameMode); @@ -190,6 +196,7 @@ private void ShowBuildProgressToolWindow() /// private void ShowBuildProgressToolWindowWithoutActivation() { + ThreadHelper.ThrowIfNotOnUIThread(); var frame = BuildProgressWindowFrame; if (frame != null) { @@ -198,4 +205,4 @@ private void ShowBuildProgressToolWindowWithoutActivation() } } } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Integration/Commands/CleanupActiveCodeCommand.cs b/CodeMaidShared/Integration/Commands/CleanupActiveCodeCommand.cs index 2aa47ab6b..59103899b 100644 --- a/CodeMaidShared/Integration/Commands/CleanupActiveCodeCommand.cs +++ b/CodeMaidShared/Integration/Commands/CleanupActiveCodeCommand.cs @@ -3,6 +3,7 @@ using SteveCadwallader.CodeMaid.Logic.Cleaning; using SteveCadwallader.CodeMaid.Properties; using System.Threading.Tasks; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Integration.Commands { @@ -54,6 +55,7 @@ public static async Task InitializeAsync(CodeMaidPackage package) /// The document about to be saved. internal void OnBeforeDocumentSave(Document document) { + ThreadHelper.ThrowIfNotOnUIThread(); if (!Settings.Default.Cleaning_AutoCleanupOnFileSave) return; if (!CodeCleanupAvailabilityLogic.CanCleanupDocument(document)) return; @@ -77,6 +79,7 @@ internal void OnBeforeDocumentSave(Document document) /// protected override void OnBeforeQueryStatus() { + ThreadHelper.ThrowIfNotOnUIThread(); Enabled = Package.ActiveDocument != null; } @@ -85,9 +88,10 @@ protected override void OnBeforeQueryStatus() /// protected override void OnExecute() { + ThreadHelper.ThrowIfNotOnUIThread(); base.OnExecute(); CodeCleanupManager.Cleanup(Package.ActiveDocument); } } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Integration/Commands/CleanupAllCodeCommand.cs b/CodeMaidShared/Integration/Commands/CleanupAllCodeCommand.cs index b6d29c6fc..fd1224d74 100644 --- a/CodeMaidShared/Integration/Commands/CleanupAllCodeCommand.cs +++ b/CodeMaidShared/Integration/Commands/CleanupAllCodeCommand.cs @@ -7,6 +7,7 @@ using System.Linq; using System.Threading.Tasks; using System.Windows; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Integration.Commands { @@ -57,6 +58,7 @@ public static async Task InitializeAsync(CodeMaidPackage package) /// protected override void OnBeforeQueryStatus() { + ThreadHelper.ThrowIfNotOnUIThread(); Enabled = Package.IDE.Solution.IsOpen; } @@ -65,6 +67,7 @@ protected override void OnBeforeQueryStatus() /// protected override void OnExecute() { + ThreadHelper.ThrowIfNotOnUIThread(); base.OnExecute(); if (!CodeCleanupAvailabilityLogic.IsCleanupEnvironmentAvailable()) @@ -88,4 +91,4 @@ protected override void OnExecute() } } } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Integration/Commands/CleanupOpenCodeCommand.cs b/CodeMaidShared/Integration/Commands/CleanupOpenCodeCommand.cs index 39f1599f2..800f3f3bb 100644 --- a/CodeMaidShared/Integration/Commands/CleanupOpenCodeCommand.cs +++ b/CodeMaidShared/Integration/Commands/CleanupOpenCodeCommand.cs @@ -1,10 +1,11 @@ -using EnvDTE; +using EnvDTE; using SteveCadwallader.CodeMaid.Helpers; using SteveCadwallader.CodeMaid.Logic.Cleaning; using SteveCadwallader.CodeMaid.UI.Dialogs.CleanupProgress; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Integration.Commands { @@ -69,6 +70,7 @@ protected override void OnBeforeQueryStatus() /// protected override void OnExecute() { + ThreadHelper.ThrowIfNotOnUIThread(); base.OnExecute(); using (new ActiveDocumentRestorer(Package)) @@ -80,4 +82,4 @@ protected override void OnExecute() } } } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Integration/Commands/CleanupSelectedCodeCommand.cs b/CodeMaidShared/Integration/Commands/CleanupSelectedCodeCommand.cs index 3d3dcb2d4..a218771d5 100644 --- a/CodeMaidShared/Integration/Commands/CleanupSelectedCodeCommand.cs +++ b/CodeMaidShared/Integration/Commands/CleanupSelectedCodeCommand.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Integration.Commands { @@ -55,6 +56,7 @@ public static async Task InitializeAsync(CodeMaidPackage package) /// protected override void OnBeforeQueryStatus() { + ThreadHelper.ThrowIfNotOnUIThread(); Enabled = Package.IDE.Solution.IsOpen; } @@ -63,6 +65,7 @@ protected override void OnBeforeQueryStatus() /// protected override void OnExecute() { + ThreadHelper.ThrowIfNotOnUIThread(); base.OnExecute(); using (new ActiveDocumentRestorer(Package)) @@ -74,4 +77,4 @@ protected override void OnExecute() } } } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Integration/Commands/CloseAllReadOnlyCommand.cs b/CodeMaidShared/Integration/Commands/CloseAllReadOnlyCommand.cs index 86ae54d53..085d5e856 100644 --- a/CodeMaidShared/Integration/Commands/CloseAllReadOnlyCommand.cs +++ b/CodeMaidShared/Integration/Commands/CloseAllReadOnlyCommand.cs @@ -1,6 +1,7 @@ using EnvDTE; using System.Linq; using System.Threading.Tasks; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Integration.Commands { @@ -39,6 +40,7 @@ public static async Task InitializeAsync(CodeMaidPackage package) /// protected override void OnBeforeQueryStatus() { + ThreadHelper.ThrowIfNotOnUIThread(); Enabled = Package.IDE.Documents.Cast().Any(x => x.ReadOnly && x.Saved); } @@ -47,6 +49,7 @@ protected override void OnBeforeQueryStatus() /// protected override void OnExecute() { + ThreadHelper.ThrowIfNotOnUIThread(); base.OnExecute(); var docs = Package.IDE.Documents; @@ -60,4 +63,4 @@ protected override void OnExecute() } } } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Integration/Commands/CollapseAllSolutionExplorerCommand.cs b/CodeMaidShared/Integration/Commands/CollapseAllSolutionExplorerCommand.cs index 83178cbb9..a75a2426a 100644 --- a/CodeMaidShared/Integration/Commands/CollapseAllSolutionExplorerCommand.cs +++ b/CodeMaidShared/Integration/Commands/CollapseAllSolutionExplorerCommand.cs @@ -2,6 +2,7 @@ using SteveCadwallader.CodeMaid.Helpers; using SteveCadwallader.CodeMaid.Properties; using System.Threading.Tasks; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Integration.Commands { @@ -50,6 +51,7 @@ public static async Task InitializeAsync(CodeMaidPackage package) /// internal void OnSolutionOpened() { + ThreadHelper.ThrowIfNotOnUIThread(); if (!Settings.Default.Collapsing_CollapseSolutionWhenOpened) return; var topItem = TopUIHierarchyItem; @@ -93,4 +95,4 @@ protected override void OnExecute() } } } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Integration/Commands/CollapseSelectedSolutionExplorerCommand.cs b/CodeMaidShared/Integration/Commands/CollapseSelectedSolutionExplorerCommand.cs index 4bc7a8bb5..1d2fc1d3b 100644 --- a/CodeMaidShared/Integration/Commands/CollapseSelectedSolutionExplorerCommand.cs +++ b/CodeMaidShared/Integration/Commands/CollapseSelectedSolutionExplorerCommand.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Integration.Commands { @@ -46,6 +47,7 @@ public static async Task InitializeAsync(CodeMaidPackage package) /// protected override void OnBeforeQueryStatus() { + ThreadHelper.ThrowIfNotOnUIThread(); Enabled = SelectedUIHierarchyItems.Any(x => x.UIHierarchyItems.Expanded); } @@ -54,6 +56,7 @@ protected override void OnBeforeQueryStatus() /// protected override void OnExecute() { + ThreadHelper.ThrowIfNotOnUIThread(); base.OnExecute(); foreach (UIHierarchyItem item in SelectedUIHierarchyItems) @@ -62,4 +65,4 @@ protected override void OnExecute() } } } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Integration/Commands/CommentFormatCommand.cs b/CodeMaidShared/Integration/Commands/CommentFormatCommand.cs index 1b178c268..89e3122aa 100644 --- a/CodeMaidShared/Integration/Commands/CommentFormatCommand.cs +++ b/CodeMaidShared/Integration/Commands/CommentFormatCommand.cs @@ -3,6 +3,7 @@ using SteveCadwallader.CodeMaid.Logic.Formatting; using SteveCadwallader.CodeMaid.Properties; using System.Threading.Tasks; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Integration.Commands { @@ -51,6 +52,7 @@ public static async Task InitializeAsync(CodeMaidPackage package) /// protected override void OnBeforeQueryStatus() { + ThreadHelper.ThrowIfNotOnUIThread(); Enabled = ActiveTextDocument != null; } @@ -59,6 +61,7 @@ protected override void OnBeforeQueryStatus() /// protected override void OnExecute() { + ThreadHelper.ThrowIfNotOnUIThread(); base.OnExecute(); var activeTextDocument = ActiveTextDocument; @@ -107,4 +110,4 @@ protected override void OnExecute() } } } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Integration/Commands/FindInSolutionExplorerCommand.cs b/CodeMaidShared/Integration/Commands/FindInSolutionExplorerCommand.cs index ac7fc2fc1..92f2ecae3 100644 --- a/CodeMaidShared/Integration/Commands/FindInSolutionExplorerCommand.cs +++ b/CodeMaidShared/Integration/Commands/FindInSolutionExplorerCommand.cs @@ -47,6 +47,7 @@ public static async Task InitializeAsync(CodeMaidPackage package) /// protected override void OnBeforeQueryStatus() { + ThreadHelper.ThrowIfNotOnUIThread(); Enabled = Package.ActiveDocument != null; } @@ -55,6 +56,7 @@ protected override void OnBeforeQueryStatus() /// protected override void OnExecute() { + ThreadHelper.ThrowIfNotOnUIThread(); base.OnExecute(); Document document = Package.ActiveDocument; @@ -96,6 +98,7 @@ protected override void OnExecute() /// private void ClearSolutionExplorerSearchFilter() { + ThreadHelper.ThrowIfNotOnUIThread(); var solutionExplorer = VsShellUtilities.GetUIHierarchyWindow(Package, VSConstants.StandardToolWindows.SolutionExplorer); var ws = solutionExplorer as IVsWindowSearch; ws?.ClearSearch(); @@ -108,6 +111,7 @@ private void ClearSolutionExplorerSearchFilter() /// The parent item to inspect. private void ToggleSolutionFoldersOpenTemporarily(UIHierarchyItem parentItem) { + ThreadHelper.ThrowIfNotOnUIThread(); if (parentItem == null) { throw new ArgumentNullException(nameof(parentItem)); @@ -143,4 +147,4 @@ private void ToggleSolutionFoldersOpenTemporarily(UIHierarchyItem parentItem) } } } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Integration/Commands/JoinLinesCommand.cs b/CodeMaidShared/Integration/Commands/JoinLinesCommand.cs index 13ce85f8e..fd373152f 100644 --- a/CodeMaidShared/Integration/Commands/JoinLinesCommand.cs +++ b/CodeMaidShared/Integration/Commands/JoinLinesCommand.cs @@ -2,6 +2,7 @@ using SteveCadwallader.CodeMaid.Helpers; using SteveCadwallader.CodeMaid.Properties; using System.Threading.Tasks; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Integration.Commands { @@ -48,6 +49,7 @@ public static async Task InitializeAsync(CodeMaidPackage package) /// protected override void OnBeforeQueryStatus() { + ThreadHelper.ThrowIfNotOnUIThread(); Enabled = ActiveTextDocument != null; } @@ -56,6 +58,7 @@ protected override void OnBeforeQueryStatus() /// protected override void OnExecute() { + ThreadHelper.ThrowIfNotOnUIThread(); base.OnExecute(); var activeTextDocument = ActiveTextDocument; @@ -75,6 +78,7 @@ protected override void OnExecute() /// The text selection. private void JoinText(TextSelection textSelection) { + ThreadHelper.ThrowIfNotOnUIThread(); // If the selection has no length, try to pick up the next line. if (textSelection.IsEmpty) { @@ -92,4 +96,4 @@ private void JoinText(TextSelection textSelection) textSelection.CharRight(); } } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Integration/Commands/OptionsCommand.cs b/CodeMaidShared/Integration/Commands/OptionsCommand.cs index 106bd7bc2..b314e3376 100644 --- a/CodeMaidShared/Integration/Commands/OptionsCommand.cs +++ b/CodeMaidShared/Integration/Commands/OptionsCommand.cs @@ -1,5 +1,6 @@ using SteveCadwallader.CodeMaid.UI.Dialogs.Options; using System.Threading.Tasks; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Integration.Commands { @@ -38,9 +39,10 @@ public static async Task InitializeAsync(CodeMaidPackage package) /// protected override void OnExecute() { + ThreadHelper.ThrowIfNotOnUIThread(); base.OnExecute(); new OptionsWindow { DataContext = new OptionsViewModel(Package) }.ShowModal(); } } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Integration/Commands/ReadOnlyToggleCommand.cs b/CodeMaidShared/Integration/Commands/ReadOnlyToggleCommand.cs index 0ad79e358..c618df5b6 100644 --- a/CodeMaidShared/Integration/Commands/ReadOnlyToggleCommand.cs +++ b/CodeMaidShared/Integration/Commands/ReadOnlyToggleCommand.cs @@ -4,6 +4,7 @@ using System; using System.IO; using System.Threading.Tasks; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Integration.Commands { @@ -42,6 +43,7 @@ public static async Task InitializeAsync(CodeMaidPackage package) /// protected override void OnBeforeQueryStatus() { + ThreadHelper.ThrowIfNotOnUIThread(); Enabled = Package.ActiveDocument != null; } @@ -50,6 +52,7 @@ protected override void OnBeforeQueryStatus() /// protected override void OnExecute() { + ThreadHelper.ThrowIfNotOnUIThread(); base.OnExecute(); Document document = Package.ActiveDocument; @@ -69,4 +72,4 @@ protected override void OnExecute() } } } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Integration/Commands/RemoveRegionCommand.cs b/CodeMaidShared/Integration/Commands/RemoveRegionCommand.cs index 3d63917e2..37e3abd7a 100644 --- a/CodeMaidShared/Integration/Commands/RemoveRegionCommand.cs +++ b/CodeMaidShared/Integration/Commands/RemoveRegionCommand.cs @@ -1,9 +1,10 @@ -using EnvDTE; +using EnvDTE; using SteveCadwallader.CodeMaid.Helpers; using SteveCadwallader.CodeMaid.Logic.Cleaning; using SteveCadwallader.CodeMaid.Model; using SteveCadwallader.CodeMaid.Properties; using System.Threading.Tasks; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Integration.Commands { @@ -63,6 +64,7 @@ public static async Task InitializeAsync(CodeMaidPackage package) /// protected override void OnBeforeQueryStatus() { + ThreadHelper.ThrowIfNotOnUIThread(); var regionCommandScope = GetRegionCommandScope(); Enabled = regionCommandScope != RegionCommandScope.None; @@ -88,6 +90,7 @@ protected override void OnBeforeQueryStatus() /// protected override void OnExecute() { + ThreadHelper.ThrowIfNotOnUIThread(); base.OnExecute(); var regionCommandScope = GetRegionCommandScope(); @@ -113,6 +116,7 @@ protected override void OnExecute() /// The scope that should be used for the region command. private RegionCommandScope GetRegionCommandScope() { + ThreadHelper.ThrowIfNotOnUIThread(); if (_removeRegionLogic.CanRemoveRegions(Package.ActiveDocument)) { var activeTextDocument = ActiveTextDocument; @@ -139,4 +143,4 @@ private RegionCommandScope GetRegionCommandScope() return RegionCommandScope.None; } } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Integration/Commands/ReorganizeActiveCodeCommand.cs b/CodeMaidShared/Integration/Commands/ReorganizeActiveCodeCommand.cs index d49c58de7..1ffb29ca9 100644 --- a/CodeMaidShared/Integration/Commands/ReorganizeActiveCodeCommand.cs +++ b/CodeMaidShared/Integration/Commands/ReorganizeActiveCodeCommand.cs @@ -1,5 +1,6 @@ using SteveCadwallader.CodeMaid.Logic.Reorganizing; using Task = System.Threading.Tasks.Task; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Integration.Commands { @@ -48,6 +49,7 @@ public static async Task InitializeAsync(CodeMaidPackage package) /// protected override void OnBeforeQueryStatus() { + ThreadHelper.ThrowIfNotOnUIThread(); Enabled = Package.ActiveDocument != null; } @@ -56,9 +58,10 @@ protected override void OnBeforeQueryStatus() /// protected override void OnExecute() { + ThreadHelper.ThrowIfNotOnUIThread(); base.OnExecute(); CodeReorganizationManager.Reorganize(Package.ActiveDocument); } } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Integration/Commands/SettingCleanupOnSaveCommand.cs b/CodeMaidShared/Integration/Commands/SettingCleanupOnSaveCommand.cs index 1977cbbd9..695d3336c 100644 --- a/CodeMaidShared/Integration/Commands/SettingCleanupOnSaveCommand.cs +++ b/CodeMaidShared/Integration/Commands/SettingCleanupOnSaveCommand.cs @@ -1,5 +1,6 @@ -using SteveCadwallader.CodeMaid.Properties; +using SteveCadwallader.CodeMaid.Properties; using System.Threading.Tasks; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Integration.Commands { @@ -61,6 +62,7 @@ protected override void OnBeforeQueryStatus() /// protected override void OnExecute() { + ThreadHelper.ThrowIfNotOnUIThread(); base.OnExecute(); CleanupOnSave = !CleanupOnSave; @@ -69,4 +71,4 @@ protected override void OnExecute() Package.IDE.StatusBar.Text = $"{Resources.CodeMaidTurnedAutomaticCleanupOnSave} {CleanupOnSaveStateText}."; } } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Integration/Commands/SortLinesCommand.cs b/CodeMaidShared/Integration/Commands/SortLinesCommand.cs index 3e5dd0105..ee084f150 100644 --- a/CodeMaidShared/Integration/Commands/SortLinesCommand.cs +++ b/CodeMaidShared/Integration/Commands/SortLinesCommand.cs @@ -1,4 +1,4 @@ -using EnvDTE; +using EnvDTE; using SteveCadwallader.CodeMaid.Helpers; using SteveCadwallader.CodeMaid.Properties; using System; @@ -6,6 +6,7 @@ using System.Text; using System.Threading.Tasks; using TextSelection = EnvDTE.TextSelection; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Integration.Commands { @@ -52,6 +53,7 @@ public static async Task InitializeAsync(CodeMaidPackage package) /// protected override void OnBeforeQueryStatus() { + ThreadHelper.ThrowIfNotOnUIThread(); Enabled = ActiveTextDocument != null; } @@ -60,6 +62,7 @@ protected override void OnBeforeQueryStatus() /// protected override void OnExecute() { + ThreadHelper.ThrowIfNotOnUIThread(); base.OnExecute(); var activeTextDocument = ActiveTextDocument; @@ -79,6 +82,7 @@ protected override void OnExecute() /// The text selection. private void SortText(TextSelection textSelection) { + ThreadHelper.ThrowIfNotOnUIThread(); // If the selection has no length, try to pick up the next line. if (textSelection.IsEmpty) { @@ -126,4 +130,4 @@ private void SortText(TextSelection textSelection) } } } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Integration/Commands/SpadeContextDeleteCommand.cs b/CodeMaidShared/Integration/Commands/SpadeContextDeleteCommand.cs index a96e358e1..f3f110298 100644 --- a/CodeMaidShared/Integration/Commands/SpadeContextDeleteCommand.cs +++ b/CodeMaidShared/Integration/Commands/SpadeContextDeleteCommand.cs @@ -1,10 +1,11 @@ -using EnvDTE; +using EnvDTE; using SteveCadwallader.CodeMaid.Helpers; using SteveCadwallader.CodeMaid.Model.CodeItems; using SteveCadwallader.CodeMaid.Properties; using System; using System.Linq; using System.Threading.Tasks; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Integration.Commands { @@ -62,6 +63,7 @@ protected override void OnBeforeQueryStatus() /// protected override void OnExecute() { + ThreadHelper.ThrowIfNotOnUIThread(); base.OnExecute(); var spade = Package.Spade; @@ -97,4 +99,4 @@ private static bool IsDeletable(BaseCodeItem codeItem) return !(codeItem is CodeItemRegion) || !((CodeItemRegion)codeItem).IsPseudoGroup; } } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Integration/Commands/SpadeContextFindReferencesCommand.cs b/CodeMaidShared/Integration/Commands/SpadeContextFindReferencesCommand.cs index c9cfae7be..4b42c9d7e 100644 --- a/CodeMaidShared/Integration/Commands/SpadeContextFindReferencesCommand.cs +++ b/CodeMaidShared/Integration/Commands/SpadeContextFindReferencesCommand.cs @@ -1,7 +1,8 @@ -using EnvDTE; +using EnvDTE; using SteveCadwallader.CodeMaid.Model.CodeItems; using System.Linq; using System.Threading.Tasks; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Integration.Commands { @@ -50,6 +51,7 @@ protected override void OnBeforeQueryStatus() /// protected override void OnExecute() { + ThreadHelper.ThrowIfNotOnUIThread(); base.OnExecute(); var spade = Package.Spade; @@ -72,4 +74,4 @@ protected override void OnExecute() Package.IDE.ExecuteCommand("Edit.FindAllReferences"); } } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Integration/Commands/SpadeContextInsertRegionCommand.cs b/CodeMaidShared/Integration/Commands/SpadeContextInsertRegionCommand.cs index c6e9e6f68..e8cda98d4 100644 --- a/CodeMaidShared/Integration/Commands/SpadeContextInsertRegionCommand.cs +++ b/CodeMaidShared/Integration/Commands/SpadeContextInsertRegionCommand.cs @@ -1,9 +1,10 @@ -using SteveCadwallader.CodeMaid.Helpers; +using SteveCadwallader.CodeMaid.Helpers; using SteveCadwallader.CodeMaid.Logic.Reorganizing; using SteveCadwallader.CodeMaid.Model.CodeItems; using SteveCadwallader.CodeMaid.Properties; using System.Linq; using System.Threading.Tasks; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Integration.Commands { @@ -47,6 +48,7 @@ public static async Task InitializeAsync(CodeMaidPackage package) /// protected override void OnBeforeQueryStatus() { + ThreadHelper.ThrowIfNotOnUIThread(); bool visible = false; var spade = Package.Spade; @@ -65,6 +67,7 @@ protected override void OnBeforeQueryStatus() /// protected override void OnExecute() { + ThreadHelper.ThrowIfNotOnUIThread(); base.OnExecute(); var spade = Package.Spade; @@ -100,4 +103,4 @@ protected override void OnExecute() } } } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Integration/Commands/SpadeContextRemoveRegionCommand.cs b/CodeMaidShared/Integration/Commands/SpadeContextRemoveRegionCommand.cs index c9af9945b..5adf188ad 100644 --- a/CodeMaidShared/Integration/Commands/SpadeContextRemoveRegionCommand.cs +++ b/CodeMaidShared/Integration/Commands/SpadeContextRemoveRegionCommand.cs @@ -1,7 +1,8 @@ -using SteveCadwallader.CodeMaid.Logic.Cleaning; +using SteveCadwallader.CodeMaid.Logic.Cleaning; using SteveCadwallader.CodeMaid.Model.CodeItems; using System.Linq; using System.Threading.Tasks; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Integration.Commands { @@ -59,6 +60,7 @@ protected override void OnBeforeQueryStatus() /// protected override void OnExecute() { + ThreadHelper.ThrowIfNotOnUIThread(); base.OnExecute(); var spade = Package.Spade; @@ -81,4 +83,4 @@ private static bool IsRemoveableRegion(CodeItemRegion region) return !region.IsPseudoGroup && region.StartLine > 0 && region.EndLine > 0; } } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Integration/Commands/SpadeOptionsCommand.cs b/CodeMaidShared/Integration/Commands/SpadeOptionsCommand.cs index 6d3b69b46..31fd10e2c 100644 --- a/CodeMaidShared/Integration/Commands/SpadeOptionsCommand.cs +++ b/CodeMaidShared/Integration/Commands/SpadeOptionsCommand.cs @@ -1,6 +1,7 @@ using SteveCadwallader.CodeMaid.UI.Dialogs.Options; using SteveCadwallader.CodeMaid.UI.Dialogs.Options.Digging; using System.Threading.Tasks; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Integration.Commands { @@ -39,9 +40,10 @@ public static async Task InitializeAsync(CodeMaidPackage package) /// protected override void OnExecute() { + ThreadHelper.ThrowIfNotOnUIThread(); base.OnExecute(); new OptionsWindow { DataContext = new OptionsViewModel(Package, typeof(DiggingViewModel)) }.ShowModal(); } } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Integration/Commands/SpadeRefreshCommand.cs b/CodeMaidShared/Integration/Commands/SpadeRefreshCommand.cs index 569cfb49b..bb797d019 100644 --- a/CodeMaidShared/Integration/Commands/SpadeRefreshCommand.cs +++ b/CodeMaidShared/Integration/Commands/SpadeRefreshCommand.cs @@ -1,4 +1,5 @@ using System.Threading.Tasks; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Integration.Commands { @@ -37,6 +38,7 @@ public static async Task InitializeAsync(CodeMaidPackage package) /// protected override void OnExecute() { + ThreadHelper.ThrowIfNotOnUIThread(); base.OnExecute(); var spade = Package.Spade; @@ -46,4 +48,4 @@ protected override void OnExecute() } } } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Integration/Commands/SpadeSearchCommand.cs b/CodeMaidShared/Integration/Commands/SpadeSearchCommand.cs index 9bdfa2d83..c44aba0fe 100644 --- a/CodeMaidShared/Integration/Commands/SpadeSearchCommand.cs +++ b/CodeMaidShared/Integration/Commands/SpadeSearchCommand.cs @@ -1,4 +1,5 @@ -using System.Threading.Tasks; +using System.Threading.Tasks; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Integration.Commands { @@ -37,6 +38,7 @@ public static async Task InitializeAsync(CodeMaidPackage package) /// protected override void OnExecute() { + ThreadHelper.ThrowIfNotOnUIThread(); base.OnExecute(); var spade = Package.Spade; @@ -46,4 +48,4 @@ protected override void OnExecute() } } } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Integration/Commands/SpadeToolWindowCommand.cs b/CodeMaidShared/Integration/Commands/SpadeToolWindowCommand.cs index c574d99e8..3d36ad4fd 100644 --- a/CodeMaidShared/Integration/Commands/SpadeToolWindowCommand.cs +++ b/CodeMaidShared/Integration/Commands/SpadeToolWindowCommand.cs @@ -1,6 +1,7 @@ using EnvDTE; using Microsoft.VisualStudio.Shell.Interop; using System.Threading.Tasks; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Integration.Commands { @@ -36,6 +37,7 @@ public static async Task InitializeAsync(CodeMaidPackage package) public override async Task SwitchAsync(bool on) { + await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); await base.SwitchAsync(on); if (!on) @@ -50,6 +52,7 @@ public override async Task SwitchAsync(bool on) /// The document that was saved. internal void OnAfterDocumentSave(Document document) { + ThreadHelper.ThrowIfNotOnUIThread(); var spade = Package.Spade; if (spade != null) { @@ -63,6 +66,7 @@ internal void OnAfterDocumentSave(Document document) /// The document that got focus, may be null. internal void OnWindowChange(Document document) { + ThreadHelper.ThrowIfNotOnUIThread(); var spade = Package.Spade; if (spade != null) { @@ -84,4 +88,4 @@ protected override void OnExecute() } } } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Integration/Commands/SwitchFileCommand.cs b/CodeMaidShared/Integration/Commands/SwitchFileCommand.cs index 0aa70c225..1181cb60f 100644 --- a/CodeMaidShared/Integration/Commands/SwitchFileCommand.cs +++ b/CodeMaidShared/Integration/Commands/SwitchFileCommand.cs @@ -6,6 +6,7 @@ using System.IO; using System.Linq; using Task = System.Threading.Tasks.Task; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Integration.Commands { @@ -59,6 +60,7 @@ public static async Task InitializeAsync(CodeMaidPackage package) /// protected override void OnBeforeQueryStatus() { + ThreadHelper.ThrowIfNotOnUIThread(); string alternatePath = GetAlternatePathIfExists(Package.ActiveDocument); bool canAlterate = !string.IsNullOrEmpty(alternatePath); @@ -95,6 +97,7 @@ protected override void OnExecute() /// The path to an alternate document, otherwise null. private string GetAlternatePathIfExists(Document document) { + ThreadHelper.ThrowIfNotOnUIThread(); var alternatePaths = GetAlternatePaths(document); return alternatePaths.FirstOrDefault(x => !string.IsNullOrEmpty(x) && Package.IDE.Solution.FindProjectItem(x) != null); @@ -107,6 +110,7 @@ private string GetAlternatePathIfExists(Document document) /// The alternate paths, otherwise null. private IEnumerable GetAlternatePaths(Document document) { + ThreadHelper.ThrowIfNotOnUIThread(); var results = new List(); if (document != null) @@ -139,4 +143,4 @@ private IEnumerable GetAlternatePaths(Document document) return results; } } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Integration/Events/BuildProgressEventListener.cs b/CodeMaidShared/Integration/Events/BuildProgressEventListener.cs index f2f70afe3..c9fcbd3de 100644 --- a/CodeMaidShared/Integration/Events/BuildProgressEventListener.cs +++ b/CodeMaidShared/Integration/Events/BuildProgressEventListener.cs @@ -1,6 +1,7 @@ using EnvDTE; using SteveCadwallader.CodeMaid.Helpers; using System.Threading.Tasks; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Integration.Events { @@ -16,6 +17,7 @@ internal sealed class BuildProgressEventListener : BaseEventListener private BuildProgressEventListener(CodeMaidPackage package) : base(package) { + ThreadHelper.ThrowIfNotOnUIThread(); // Store access to the build events, otherwise events will not register properly via DTE. BuildEvents = Package.IDE.Events.BuildEvents; } @@ -66,6 +68,7 @@ public static async Task InitializeAsync(CodeMaidPackage package) /// protected override void RegisterListeners() { + ThreadHelper.ThrowIfNotOnUIThread(); BuildEvents.OnBuildBegin += BuildEvents_OnBuildBegin; BuildEvents.OnBuildProjConfigBegin += BuildEvents_OnBuildProjConfigBegin; BuildEvents.OnBuildProjConfigDone += BuildEvents_OnBuildProjConfigDone; @@ -77,6 +80,7 @@ protected override void RegisterListeners() /// protected override void UnRegisterListeners() { + ThreadHelper.ThrowIfNotOnUIThread(); BuildEvents.OnBuildBegin -= BuildEvents_OnBuildBegin; BuildEvents.OnBuildProjConfigBegin -= BuildEvents_OnBuildProjConfigBegin; BuildEvents.OnBuildProjConfigDone -= BuildEvents_OnBuildProjConfigDone; @@ -90,6 +94,7 @@ protected override void UnRegisterListeners() /// The action. private void BuildEvents_OnBuildBegin(vsBuildScope scope, vsBuildAction action) { + ThreadHelper.ThrowIfNotOnUIThread(); var buildBegin = BuildBegin; if (buildBegin != null) { @@ -106,6 +111,7 @@ private void BuildEvents_OnBuildBegin(vsBuildScope scope, vsBuildAction action) /// The action. private void BuildEvents_OnBuildDone(vsBuildScope scope, vsBuildAction action) { + ThreadHelper.ThrowIfNotOnUIThread(); var buildDone = BuildDone; if (buildDone != null) { @@ -124,6 +130,7 @@ private void BuildEvents_OnBuildDone(vsBuildScope scope, vsBuildAction action) /// The solution config. private void BuildEvents_OnBuildProjConfigBegin(string project, string projectConfig, string platform, string solutionConfig) { + ThreadHelper.ThrowIfNotOnUIThread(); var buildProjConfigBegin = BuildProjConfigBegin; if (buildProjConfigBegin != null) { @@ -143,6 +150,7 @@ private void BuildEvents_OnBuildProjConfigBegin(string project, string projectCo /// True if project build was successful, otherwise false. private void BuildEvents_OnBuildProjConfigDone(string project, string projectConfig, string platform, string solutionConfig, bool success) { + ThreadHelper.ThrowIfNotOnUIThread(); var buildProjConfigDone = BuildProjConfigDone; if (buildProjConfigDone != null) { @@ -152,4 +160,4 @@ private void BuildEvents_OnBuildProjConfigDone(string project, string projectCon } } } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Integration/Events/DocumentEventListener.cs b/CodeMaidShared/Integration/Events/DocumentEventListener.cs index b27fd2a4b..bd24b2487 100644 --- a/CodeMaidShared/Integration/Events/DocumentEventListener.cs +++ b/CodeMaidShared/Integration/Events/DocumentEventListener.cs @@ -1,7 +1,8 @@ -using EnvDTE; +using EnvDTE; using SteveCadwallader.CodeMaid.Helpers; using System; using System.Threading.Tasks; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Integration.Events { @@ -17,6 +18,7 @@ internal sealed class DocumentEventListener : BaseEventListener private DocumentEventListener(CodeMaidPackage package) : base(package) { + ThreadHelper.ThrowIfNotOnUIThread(); // Store access to the document events, otherwise events will not register properly via DTE. DocumentEvents = Package.IDE.Events.DocumentEvents; } @@ -52,6 +54,7 @@ public static async Task InitializeAsync(CodeMaidPackage package) /// protected override void RegisterListeners() { + ThreadHelper.ThrowIfNotOnUIThread(); DocumentEvents.DocumentClosing += DocumentEvents_DocumentClosing; } @@ -60,6 +63,7 @@ protected override void RegisterListeners() /// protected override void UnRegisterListeners() { + ThreadHelper.ThrowIfNotOnUIThread(); DocumentEvents.DocumentClosing -= DocumentEvents_DocumentClosing; } @@ -69,6 +73,7 @@ protected override void UnRegisterListeners() /// The document that is closing. private void DocumentEvents_DocumentClosing(Document document) { + ThreadHelper.ThrowIfNotOnUIThread(); var onDocumentClosing = OnDocumentClosing; if (onDocumentClosing != null) { @@ -78,4 +83,4 @@ private void DocumentEvents_DocumentClosing(Document document) } } } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Integration/Events/RunningDocumentTableEventListener.cs b/CodeMaidShared/Integration/Events/RunningDocumentTableEventListener.cs index a6d737766..00e595d7a 100644 --- a/CodeMaidShared/Integration/Events/RunningDocumentTableEventListener.cs +++ b/CodeMaidShared/Integration/Events/RunningDocumentTableEventListener.cs @@ -90,6 +90,7 @@ await package.SettingsMonitor.WatchAsync(new[] { /// S_OK if successful, otherwise an error code. public int OnAfterSave(uint docCookie) { + ThreadHelper.ThrowIfNotOnUIThread(); var afterSave = AfterSave; if (afterSave != null) { @@ -152,6 +153,7 @@ protected override void UnRegisterListeners() /// The document object, otherwise null. private Document GetDocumentFromCookie(uint docCookie) { + ThreadHelper.ThrowIfNotOnUIThread(); // Retrieve document information from the cookie to get the full document name. var documentName = RunningDocumentTable.GetDocumentInfo(docCookie).Moniker; @@ -159,4 +161,4 @@ private Document GetDocumentFromCookie(uint docCookie) return Package.IDE.Documents.OfType().FirstOrDefault(x => x.FullName == documentName); } } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Integration/Events/SolutionEventListener.cs b/CodeMaidShared/Integration/Events/SolutionEventListener.cs index 9f7e0f646..269cc8fea 100644 --- a/CodeMaidShared/Integration/Events/SolutionEventListener.cs +++ b/CodeMaidShared/Integration/Events/SolutionEventListener.cs @@ -2,6 +2,7 @@ using SteveCadwallader.CodeMaid.Helpers; using System; using System.Threading.Tasks; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Integration.Events { @@ -17,6 +18,7 @@ internal sealed class SolutionEventListener : BaseEventListener private SolutionEventListener(CodeMaidPackage package) : base(package) { + ThreadHelper.ThrowIfNotOnUIThread(); // Store access to the solutions events, otherwise events will not register properly via DTE. SolutionEvents = Package.IDE.Events.SolutionEvents; } @@ -63,6 +65,7 @@ public static async Task InitializeAsync(CodeMaidPackage package) /// protected override void RegisterListeners() { + ThreadHelper.ThrowIfNotOnUIThread(); SolutionEvents.Opened += SolutionEvents_Opened; SolutionEvents.AfterClosing += SolutionEvents_AfterClosing; } @@ -72,6 +75,7 @@ protected override void RegisterListeners() /// protected override void UnRegisterListeners() { + ThreadHelper.ThrowIfNotOnUIThread(); SolutionEvents.Opened -= SolutionEvents_Opened; SolutionEvents.AfterClosing -= SolutionEvents_AfterClosing; } @@ -81,6 +85,7 @@ protected override void UnRegisterListeners() /// private void SolutionEvents_AfterClosing() { + ThreadHelper.ThrowIfNotOnUIThread(); var onSolutionClosed = OnSolutionClosed; if (onSolutionClosed != null) { @@ -95,6 +100,7 @@ private void SolutionEvents_AfterClosing() /// private void SolutionEvents_Opened() { + ThreadHelper.ThrowIfNotOnUIThread(); var onSolutionOpened = OnSolutionOpened; if (onSolutionOpened != null) { @@ -104,4 +110,4 @@ private void SolutionEvents_Opened() } } } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Integration/Events/TextEditorEventListener.cs b/CodeMaidShared/Integration/Events/TextEditorEventListener.cs index 18485f194..f0f1efd08 100644 --- a/CodeMaidShared/Integration/Events/TextEditorEventListener.cs +++ b/CodeMaidShared/Integration/Events/TextEditorEventListener.cs @@ -1,7 +1,8 @@ -using EnvDTE; +using EnvDTE; using SteveCadwallader.CodeMaid.Helpers; using System; using System.Threading.Tasks; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Integration.Events { @@ -17,6 +18,7 @@ internal sealed class TextEditorEventListener : BaseEventListener private TextEditorEventListener(CodeMaidPackage package) : base(package) { + ThreadHelper.ThrowIfNotOnUIThread(); // Store access to the text editor events, otherwise events will not register properly // via DTE. TextEditorEvents = Package.IDE.Events.TextEditorEvents; @@ -53,6 +55,7 @@ public static async Task InitializeAsync(CodeMaidPackage package) /// protected override void RegisterListeners() { + ThreadHelper.ThrowIfNotOnUIThread(); TextEditorEvents.LineChanged += TextEditorEvents_LineChanged; } @@ -61,6 +64,7 @@ protected override void RegisterListeners() /// protected override void UnRegisterListeners() { + ThreadHelper.ThrowIfNotOnUIThread(); TextEditorEvents.LineChanged -= TextEditorEvents_LineChanged; } @@ -72,6 +76,7 @@ protected override void UnRegisterListeners() /// A hint as to the type of change that has occurred. private void TextEditorEvents_LineChanged(TextPoint startPoint, TextPoint endPoint, int hint) { + ThreadHelper.ThrowIfNotOnUIThread(); var textDocument = startPoint?.Parent; if (textDocument == null) return; @@ -86,4 +91,4 @@ private void TextEditorEvents_LineChanged(TextPoint startPoint, TextPoint endPoi } } } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Integration/Events/WindowEventListener.cs b/CodeMaidShared/Integration/Events/WindowEventListener.cs index dff919a09..b1cfef908 100644 --- a/CodeMaidShared/Integration/Events/WindowEventListener.cs +++ b/CodeMaidShared/Integration/Events/WindowEventListener.cs @@ -2,6 +2,7 @@ using SteveCadwallader.CodeMaid.Helpers; using System; using System.Threading.Tasks; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Integration.Events { @@ -17,6 +18,7 @@ internal sealed class WindowEventListener : BaseEventListener private WindowEventListener(CodeMaidPackage package) : base(package) { + ThreadHelper.ThrowIfNotOnUIThread(); // Store access to the window events, otherwise events will not register properly via DTE. WindowEvents = Package.IDE.Events.WindowEvents; } @@ -52,6 +54,7 @@ public static async Task InitializeAsync(CodeMaidPackage package) /// protected override void RegisterListeners() { + ThreadHelper.ThrowIfNotOnUIThread(); WindowEvents.WindowActivated += WindowEvents_WindowActivated; } @@ -60,6 +63,7 @@ protected override void RegisterListeners() /// protected override void UnRegisterListeners() { + ThreadHelper.ThrowIfNotOnUIThread(); WindowEvents.WindowActivated -= WindowEvents_WindowActivated; } @@ -69,6 +73,7 @@ protected override void UnRegisterListeners() /// The document that got focus, may be null. private void RaiseWindowChange(Document document) { + ThreadHelper.ThrowIfNotOnUIThread(); var onWindowChange = OnWindowChange; if (onWindowChange != null) { @@ -85,6 +90,7 @@ private void RaiseWindowChange(Document document) /// The window that lost focus. private void WindowEvents_WindowActivated(Window gotFocus, Window lostFocus) { + ThreadHelper.ThrowIfNotOnUIThread(); if (gotFocus.Kind == "Document") { RaiseWindowChange(gotFocus.Document); @@ -95,4 +101,4 @@ private void WindowEvents_WindowActivated(Window gotFocus, Window lostFocus) } } } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Logic/Cleaning/CodeCleanupAvailabilityLogic.cs b/CodeMaidShared/Logic/Cleaning/CodeCleanupAvailabilityLogic.cs index 21af739b1..e765a43ca 100644 --- a/CodeMaidShared/Logic/Cleaning/CodeCleanupAvailabilityLogic.cs +++ b/CodeMaidShared/Logic/Cleaning/CodeCleanupAvailabilityLogic.cs @@ -9,6 +9,7 @@ using System.IO; using System.Linq; using System.Text.RegularExpressions; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Logic.Cleaning { @@ -37,7 +38,7 @@ internal class CodeCleanupAvailabilityLogic .Where(y => !string.IsNullOrEmpty(y)) .ToList()); - private EditorFactory _editorFactory; + private readonly Lazy _editorFactory = new Lazy(() => new EditorFactory()); #endregion Fields @@ -84,7 +85,7 @@ private CodeCleanupAvailabilityLogic(CodeMaidPackage package) /// /// A default editor factory, used for its knowledge of language service-extension mappings. /// - private EditorFactory EditorFactory => _editorFactory ?? (_editorFactory = new EditorFactory()); + private EditorFactory EditorFactory => _editorFactory.Value; #endregion Properties @@ -98,6 +99,7 @@ private CodeCleanupAvailabilityLogic(CodeMaidPackage package) /// True if item can be cleaned up, otherwise false. internal bool CanCleanupDocument(Document document, bool allowUserPrompts = false) { + ThreadHelper.ThrowIfNotOnUIThread(); if (!IsCleanupEnvironmentAvailable()) { OutputWindowHelper.DiagnosticWriteLine($"CodeCleanupAvailabilityLogic.CanCleanupDocument returned false due to the cleanup environment not being available."); @@ -156,6 +158,7 @@ internal bool CanCleanupDocument(Document document, bool allowUserPrompts = fals /// True if item can be cleaned up, otherwise false. internal bool CanCleanupProjectItem(ProjectItem projectItem) { + ThreadHelper.ThrowIfNotOnUIThread(); if (!IsCleanupEnvironmentAvailable()) { OutputWindowHelper.DiagnosticWriteLine($"CodeCleanupAvailabilityLogic.CanCleanupProjectItem returned false due to the cleanup environment not being available."); @@ -203,6 +206,7 @@ internal bool CanCleanupProjectItem(ProjectItem projectItem) /// True if cleanup can occur, false otherwise. internal bool IsCleanupEnvironmentAvailable() { + ThreadHelper.ThrowIfNotOnUIThread(); return _package.IDE.Debugger.CurrentMode == dbgDebugMode.dbgDesignMode; } @@ -217,6 +221,7 @@ internal bool IsCleanupEnvironmentAvailable() /// The file extension, otherwise an empty string. private static string GetProjectItemExtension(ProjectItem projectItem) { + ThreadHelper.ThrowIfNotOnUIThread(); try { return Path.GetExtension(projectItem.Name) ?? string.Empty; @@ -234,6 +239,7 @@ private static string GetProjectItemExtension(ProjectItem projectItem) /// True an auto-generated header is detected, otherwise false. private bool HasAutoGeneratedHeader(Document document) { + ThreadHelper.ThrowIfNotOnUIThread(); var textDocument = document.GetTextDocument(); if (textDocument != null) { @@ -260,6 +266,7 @@ private bool HasAutoGeneratedHeader(Document document) /// private bool IsDocumentExcludedBecauseExternal(Document document, bool allowUserPrompts) { + ThreadHelper.ThrowIfNotOnUIThread(); if (!document.IsExternal()) return false; switch ((AskYesNo)Settings.Default.Cleaning_PerformPartialCleanupOnExternal) @@ -289,6 +296,7 @@ private bool IsDocumentExcludedBecauseExternal(Document document, bool allowUser /// True if the document language is included, otherwise false. private bool IsDocumentLanguageIncludedByOptions(Document document) { + ThreadHelper.ThrowIfNotOnUIThread(); var projectItem = document.ProjectItem; var extension = GetProjectItemExtension(projectItem); if (extension.Equals(".php", StringComparison.CurrentCultureIgnoreCase)) @@ -342,7 +350,15 @@ private bool IsFileNameExcludedByOptions(string filename) return false; } - return cleanupExclusions.Any(cleanupExclusion => Regex.IsMatch(filename, cleanupExclusion, RegexOptions.IgnoreCase)); + try + { + return cleanupExclusions.Any(cleanupExclusion => Regex.IsMatch(filename, cleanupExclusion, RegexOptions.IgnoreCase)); + } + catch (ArgumentException ex) + { + OutputWindowHelper.WarningWriteLine($"Invalid regex expression found in cleaning exclusions options: {ex.Message}"); + return false; + } } /// @@ -364,7 +380,15 @@ private bool IsFileNameIncludedByOptions(string filename) } else { - return cleanupInclusions.Any(cleanupInclusion => Regex.IsMatch(filename, cleanupInclusion, RegexOptions.IgnoreCase)); + try + { + return cleanupInclusions.Any(cleanupInclusion => Regex.IsMatch(filename, cleanupInclusion, RegexOptions.IgnoreCase)); + } + catch (ArgumentException ex) + { + OutputWindowHelper.WarningWriteLine($"Invalid regex expression found in cleaning inclusions options: {ex.Message}"); + return true; + } } } @@ -376,6 +400,7 @@ private bool IsFileNameIncludedByOptions(string filename) /// True if the parent is excluded by options, otherwise false. private static bool IsParentCodeGeneratorExcludedByOptions(Document document) { + ThreadHelper.ThrowIfNotOnUIThread(); if (document == null) return false; return IsParentCodeGeneratorExcludedByOptions(document.ProjectItem); @@ -389,6 +414,7 @@ private static bool IsParentCodeGeneratorExcludedByOptions(Document document) /// True if the parent is excluded by options, otherwise false. private static bool IsParentCodeGeneratorExcludedByOptions(ProjectItem projectItem) { + ThreadHelper.ThrowIfNotOnUIThread(); var parentProjectItem = projectItem?.GetParentProjectItem(); if (parentProjectItem == null) return false; @@ -408,6 +434,7 @@ private static bool IsParentCodeGeneratorExcludedByOptions(ProjectItem projectIt /// True if the document language is included, otherwise false. private bool IsProjectItemLanguageIncludedByOptions(ProjectItem projectItem) { + ThreadHelper.ThrowIfNotOnUIThread(); var extension = GetProjectItemExtension(projectItem); if (extension.Equals(".js", StringComparison.CurrentCultureIgnoreCase)) { @@ -453,6 +480,7 @@ private bool IsProjectItemLanguageIncludedByOptions(ProjectItem projectItem) /// True if external files should be cleaned, otherwise false. private static bool PromptUserAboutCleaningExternalFiles(Document document) { + ThreadHelper.ThrowIfNotOnUIThread(); try { var viewModel = new YesNoPromptViewModel @@ -494,4 +522,4 @@ private static bool PromptUserAboutCleaningExternalFiles(Document document) #endregion Private Methods } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Logic/Cleaning/CodeCleanupManager.cs b/CodeMaidShared/Logic/Cleaning/CodeCleanupManager.cs index 850536ca5..a16f8989d 100644 --- a/CodeMaidShared/Logic/Cleaning/CodeCleanupManager.cs +++ b/CodeMaidShared/Logic/Cleaning/CodeCleanupManager.cs @@ -8,6 +8,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Logic.Cleaning { @@ -101,6 +102,7 @@ private CodeCleanupManager(CodeMaidPackage package) /// The project item for cleanup. internal void Cleanup(ProjectItem projectItem) { + ThreadHelper.ThrowIfNotOnUIThread(); if (!_codeCleanupAvailabilityLogic.CanCleanupProjectItem(projectItem)) return; // Attempt to open the document if not already opened. @@ -135,6 +137,7 @@ internal void Cleanup(ProjectItem projectItem) /// The document for cleanup. internal void Cleanup(Document document) { + ThreadHelper.ThrowIfNotOnUIThread(); if (!_codeCleanupAvailabilityLogic.CanCleanupDocument(document, true)) return; // Make sure the document to be cleaned up is active, required for some commands like format document. @@ -186,6 +189,7 @@ internal void Cleanup(Document document) /// The code cleanup method, otherwise null. private Action FindCodeCleanupMethod(Document document) { + ThreadHelper.ThrowIfNotOnUIThread(); switch (document.GetCodeLanguage()) { case CodeLanguage.CSharp: @@ -227,6 +231,7 @@ private Action FindCodeCleanupMethod(Document document) /// The document for cleanup. private void RunCodeCleanupCSharp(Document document) { + ThreadHelper.ThrowIfNotOnUIThread(); var textDocument = document.GetTextDocument(); // Perform any actions that can modify the file code model first. @@ -346,6 +351,7 @@ private void RunCodeCleanupCSharp(Document document) /// The document for cleanup. private void RunCodeCleanupVB(Document document) { + ThreadHelper.ThrowIfNotOnUIThread(); var textDocument = document.GetTextDocument(); // Perform any actions that can modify the file code model first. @@ -446,6 +452,7 @@ private void RunCodeCleanupVB(Document document) /// The document for cleanup. private void RunCodeCleanupC(Document document) { + ThreadHelper.ThrowIfNotOnUIThread(); var textDocument = document.GetTextDocument(); RunExternalFormatting(textDocument); @@ -475,6 +482,7 @@ private void RunCodeCleanupC(Document document) /// The document for cleanup. private void RunCodeCleanupMarkup(Document document) { + ThreadHelper.ThrowIfNotOnUIThread(); var textDocument = document.GetTextDocument(); RunExternalFormatting(textDocument); @@ -506,6 +514,7 @@ private void RunCodeCleanupMarkup(Document document) /// The document for cleanup. private void RunCodeCleanupGeneric(Document document) { + ThreadHelper.ThrowIfNotOnUIThread(); var textDocument = document.GetTextDocument(); RunExternalFormatting(textDocument); @@ -534,6 +543,7 @@ private void RunCodeCleanupGeneric(Document document) /// The text document to cleanup. private void RunExternalFormatting(TextDocument textDocument) { + ThreadHelper.ThrowIfNotOnUIThread(); RunVisualStudioFormatDocument(textDocument); RunJetBrainsReSharperCleanup(textDocument); RunTelerikJustCodeCleanup(textDocument); @@ -547,6 +557,7 @@ private void RunExternalFormatting(TextDocument textDocument) /// The text document to cleanup. private void RunVisualStudioFormatDocument(TextDocument textDocument) { + ThreadHelper.ThrowIfNotOnUIThread(); if (!Settings.Default.Cleaning_RunVisualStudioFormatDocumentCommand) return; _commandHelper.ExecuteCommand(textDocument, "Edit.FormatDocument"); @@ -571,6 +582,7 @@ private void RunJetBrainsReSharperCleanup(TextDocument textDocument) /// The text document to cleanup. private void RunTelerikJustCodeCleanup(TextDocument textDocument) { + ThreadHelper.ThrowIfNotOnUIThread(); if (!Settings.Default.ThirdParty_UseTelerikJustCodeCleanup) return; _commandHelper.ExecuteCommand(textDocument, "JustCode.JustCode_CleanCodeWithDefaultProfile"); @@ -603,4 +615,4 @@ private void RunOtherCleanupCommands(TextDocument textDocument) #endregion Private Cleanup Methods } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Logic/Cleaning/FileHeaderLogic.cs b/CodeMaidShared/Logic/Cleaning/FileHeaderLogic.cs index c9d19992c..a872ad0c7 100644 --- a/CodeMaidShared/Logic/Cleaning/FileHeaderLogic.cs +++ b/CodeMaidShared/Logic/Cleaning/FileHeaderLogic.cs @@ -1,10 +1,11 @@ -using EnvDTE; +using EnvDTE; using SteveCadwallader.CodeMaid.Helpers; using SteveCadwallader.CodeMaid.Properties; using SteveCadwallader.CodeMaid.UI.Enumerations; using System; using System.Collections.Generic; using System.ComponentModel; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Logic.Cleaning { @@ -57,6 +58,7 @@ private FileHeaderLogic(CodeMaidPackage package) /// The text document to update. internal void UpdateFileHeader(TextDocument textDocument) { + ThreadHelper.ThrowIfNotOnUIThread(); var settingsFileHeader = FileHeaderHelper.GetFileHeaderFromSettings(textDocument); if (string.IsNullOrWhiteSpace(settingsFileHeader)) { @@ -85,6 +87,7 @@ internal void UpdateFileHeader(TextDocument textDocument) private int GetHeaderLength(TextDocument textDocument, bool skipUsings) { + ThreadHelper.ThrowIfNotOnUIThread(); var headerBlock = ReadTextBlock(textDocument); var language = textDocument.GetCodeLanguage(); @@ -111,6 +114,7 @@ private string GetCurrentHeader(TextDocument textDocument, bool skipUsings) private int GetNbLinesToSkip(TextDocument textDocument) { + ThreadHelper.ThrowIfNotOnUIThread(); var docHeadBlock = ReadTextBlock(textDocument); return FileHeaderHelper.GetNbLinesToSkip("using ", docHeadBlock, new List { "namespace ", "[assembly:" }); @@ -118,6 +122,7 @@ private int GetNbLinesToSkip(TextDocument textDocument) private void InsertFileHeader(TextDocument textDocument, string settingsFileHeader) { + ThreadHelper.ThrowIfNotOnUIThread(); switch (FileHeaderHelper.GetFileHeaderPositionFromSettings(textDocument)) { case HeaderPosition.DocumentStart: @@ -193,6 +198,7 @@ private string ReadTextBlock(TextDocument textDocument) private void ReplaceFileHeader(TextDocument textDocument, string settingsFileHeader) { + ThreadHelper.ThrowIfNotOnUIThread(); switch (FileHeaderHelper.GetFileHeaderPositionFromSettings(textDocument)) { case HeaderPosition.DocumentStart: @@ -263,4 +269,4 @@ private void ReplaceFileHeaderDocumentStart(TextDocument textDocument, string se #endregion Methods } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Logic/Cleaning/InsertBlankLinePaddingLogic.cs b/CodeMaidShared/Logic/Cleaning/InsertBlankLinePaddingLogic.cs index bace30f5a..91e556d97 100644 --- a/CodeMaidShared/Logic/Cleaning/InsertBlankLinePaddingLogic.cs +++ b/CodeMaidShared/Logic/Cleaning/InsertBlankLinePaddingLogic.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Logic.Cleaning { @@ -185,6 +186,7 @@ internal bool ShouldBeFollowedByBlankLine(BaseCodeItem codeItem) /// The regions to pad. internal void InsertPaddingBeforeRegionTags(IEnumerable regions) { + ThreadHelper.ThrowIfNotOnUIThread(); if (!Settings.Default.Cleaning_InsertBlankLinePaddingBeforeRegionTags) return; foreach (var region in regions.Where(x => !x.IsInvalidated)) @@ -201,6 +203,7 @@ internal void InsertPaddingBeforeRegionTags(IEnumerable regions) /// The regions to pad. internal void InsertPaddingAfterRegionTags(IEnumerable regions) { + ThreadHelper.ThrowIfNotOnUIThread(); if (!Settings.Default.Cleaning_InsertBlankLinePaddingAfterRegionTags) return; foreach (var region in regions.Where(x => !x.IsInvalidated)) @@ -217,6 +220,7 @@ internal void InsertPaddingAfterRegionTags(IEnumerable regions) /// The regions to pad. internal void InsertPaddingBeforeEndRegionTags(IEnumerable regions) { + ThreadHelper.ThrowIfNotOnUIThread(); if (!Settings.Default.Cleaning_InsertBlankLinePaddingBeforeEndRegionTags) return; foreach (var region in regions.Where(x => !x.IsInvalidated)) @@ -233,6 +237,7 @@ internal void InsertPaddingBeforeEndRegionTags(IEnumerable regio /// The regions to pad. internal void InsertPaddingAfterEndRegionTags(IEnumerable regions) { + ThreadHelper.ThrowIfNotOnUIThread(); if (!Settings.Default.Cleaning_InsertBlankLinePaddingAfterEndRegionTags) return; foreach (var region in regions.Where(x => !x.IsInvalidated)) @@ -251,6 +256,7 @@ internal void InsertPaddingAfterEndRegionTags(IEnumerable region internal void InsertPaddingBeforeCodeElements(IEnumerable codeElements) where T : BaseCodeItemElement { + ThreadHelper.ThrowIfNotOnUIThread(); foreach (T codeElement in codeElements.Where(ShouldBePrecededByBlankLine)) { TextDocumentHelper.InsertBlankLineBeforePoint(codeElement.StartPoint); @@ -265,6 +271,7 @@ internal void InsertPaddingBeforeCodeElements(IEnumerable codeElements) internal void InsertPaddingAfterCodeElements(IEnumerable codeElements) where T : BaseCodeItemElement { + ThreadHelper.ThrowIfNotOnUIThread(); foreach (T codeElement in codeElements.Where(ShouldBeFollowedByBlankLine)) { TextDocumentHelper.InsertBlankLineAfterPoint(codeElement.EndPoint); @@ -277,6 +284,7 @@ internal void InsertPaddingAfterCodeElements(IEnumerable codeElements) /// The text document. internal void InsertPaddingBeforeCaseStatements(TextDocument textDocument) { + ThreadHelper.ThrowIfNotOnUIThread(); if (!Settings.Default.Cleaning_InsertBlankLinePaddingBeforeCaseStatements) return; const string pattern = @"(^[ \t]*)(break;|return([ \t][^;]*)?;)\r?\n([ \t]*)(case|default)"; @@ -292,6 +300,7 @@ internal void InsertPaddingBeforeCaseStatements(TextDocument textDocument) /// The text document. internal void InsertPaddingBeforeSingleLineComments(TextDocument textDocument) { + ThreadHelper.ThrowIfNotOnUIThread(); if (!Settings.Default.Cleaning_InsertBlankLinePaddingBeforeSingleLineComments) return; const string pattern = @"(^[ \t]*(?!//)[^ \t\r\n\{].*\r?\n)([ \t]*//)(?!//)"; @@ -306,10 +315,13 @@ internal void InsertPaddingBeforeSingleLineComments(TextDocument textDocument) /// The properties. internal void InsertPaddingBetweenMultiLinePropertyAccessors(IEnumerable properties) { + ThreadHelper.ThrowIfNotOnUIThread(); if (!Settings.Default.Cleaning_InsertBlankLinePaddingBetweenPropertiesMultiLineAccessors) return; foreach (var property in properties) { + if (property?.CodeProperty == null) continue; + var getter = property.CodeProperty.Getter; var setter = property.CodeProperty.Setter; @@ -325,4 +337,4 @@ internal void InsertPaddingBetweenMultiLinePropertyAccessors(IEnumerableThe classes. public void InsertExplicitAccessModifiersOnClasses(IEnumerable classes) { + ThreadHelper.ThrowIfNotOnUIThread(); if (!Settings.Default.Cleaning_InsertExplicitAccessModifiersOnClasses) return; foreach (var codeClass in classes.Select(x => x.CodeClass).Where(y => y != null)) @@ -67,7 +69,14 @@ public void InsertExplicitAccessModifiersOnClasses(IEnumerable cl if (!IsAccessModifierExplicitlySpecifiedOnCodeElement(classDeclaration, codeClass.Access)) { // Set the access value to itself to cause the code to be added. - codeClass.Access = codeClass.Access; + try + { + codeClass.Access = codeClass.Access; + } + catch (Exception ex) + { + OutputWindowHelper.WarningWriteLine($"Unable to set explicit access modifier on class {codeClass.Name}: {ex.Message}"); + } } } } @@ -78,6 +87,7 @@ public void InsertExplicitAccessModifiersOnClasses(IEnumerable cl /// The delegates. public void InsertExplicitAccessModifiersOnDelegates(IEnumerable delegates) { + ThreadHelper.ThrowIfNotOnUIThread(); if (!Settings.Default.Cleaning_InsertExplicitAccessModifiersOnDelegates) return; foreach (var codeDelegate in delegates.Select(x => x.CodeDelegate).Where(y => y != null)) @@ -87,7 +97,14 @@ public void InsertExplicitAccessModifiersOnDelegates(IEnumerableThe enumerations. public void InsertExplicitAccessModifiersOnEnumerations(IEnumerable enumerations) { + ThreadHelper.ThrowIfNotOnUIThread(); if (!Settings.Default.Cleaning_InsertExplicitAccessModifiersOnEnumerations) return; foreach (var codeEnum in enumerations.Select(x => x.CodeEnum).Where(y => y != null)) @@ -107,7 +125,14 @@ public void InsertExplicitAccessModifiersOnEnumerations(IEnumerableThe events. public void InsertExplicitAccessModifiersOnEvents(IEnumerable events) { + ThreadHelper.ThrowIfNotOnUIThread(); if (!Settings.Default.Cleaning_InsertExplicitAccessModifiersOnEvents) return; - foreach (var codeEvent in events.Select(x => x.CodeEvent).Where(y => y != null)) + foreach (var item in events) { + if (item?.CodeEvent == null) continue; + var codeEvent = item.CodeEvent; + try { // Skip events defined inside an interface. @@ -147,7 +176,14 @@ public void InsertExplicitAccessModifiersOnEvents(IEnumerable eve if (!IsAccessModifierExplicitlySpecifiedOnCodeElement(eventDeclaration, codeEvent.Access)) { // Set the access value to itself to cause the code to be added. - codeEvent.Access = codeEvent.Access; + try + { + codeEvent.Access = codeEvent.Access; + } + catch (Exception ex) + { + OutputWindowHelper.WarningWriteLine($"Unable to set explicit access modifier on event {codeEvent.Name}: {ex.Message}"); + } } } } @@ -158,10 +194,14 @@ public void InsertExplicitAccessModifiersOnEvents(IEnumerable eve /// The fields. public void InsertExplicitAccessModifiersOnFields(IEnumerable fields) { + ThreadHelper.ThrowIfNotOnUIThread(); if (!Settings.Default.Cleaning_InsertExplicitAccessModifiersOnFields) return; - foreach (var codeField in fields.Select(x => x.CodeVariable).Where(y => y != null)) + foreach (var item in fields) { + if (item?.CodeVariable == null) continue; + var codeField = item.CodeVariable; + try { // Skip "fields" defined inside an enumeration. @@ -181,7 +221,14 @@ public void InsertExplicitAccessModifiersOnFields(IEnumerable fie if (!IsAccessModifierExplicitlySpecifiedOnCodeElement(fieldDeclaration, codeField.Access)) { // Set the access value to itself to cause the code to be added. - codeField.Access = codeField.Access; + try + { + codeField.Access = codeField.Access; + } + catch (Exception ex) + { + OutputWindowHelper.WarningWriteLine($"Unable to set explicit access modifier on field {codeField.Name}: {ex.Message}"); + } } } } @@ -192,16 +239,27 @@ public void InsertExplicitAccessModifiersOnFields(IEnumerable fie /// The interfaces. public void InsertExplicitAccessModifiersOnInterfaces(IEnumerable interfaces) { + ThreadHelper.ThrowIfNotOnUIThread(); if (!Settings.Default.Cleaning_InsertExplicitAccessModifiersOnInterfaces) return; - foreach (var codeInterface in interfaces.Select(x => x.CodeInterface).Where(y => y != null)) + foreach (var item in interfaces) { + if (item?.CodeInterface == null) continue; + var codeInterface = item.CodeInterface; + var interfaceDeclaration = CodeElementHelper.GetInterfaceDeclaration(codeInterface); if (!IsAccessModifierExplicitlySpecifiedOnCodeElement(interfaceDeclaration, codeInterface.Access)) { // Set the access value to itself to cause the code to be added. - codeInterface.Access = codeInterface.Access; + try + { + codeInterface.Access = codeInterface.Access; + } + catch (Exception ex) + { + OutputWindowHelper.WarningWriteLine($"Unable to set explicit access modifier on interface {codeInterface.Name}: {ex.Message}"); + } } } } @@ -212,10 +270,14 @@ public void InsertExplicitAccessModifiersOnInterfaces(IEnumerableThe methods. public void InsertExplicitAccessModifiersOnMethods(IEnumerable methods) { + ThreadHelper.ThrowIfNotOnUIThread(); if (!Settings.Default.Cleaning_InsertExplicitAccessModifiersOnMethods) return; - foreach (var codeFunction in methods.Select(x => x.CodeFunction).Where(y => y != null)) + foreach (var item in methods) { + if (item?.CodeFunction == null) continue; + var codeFunction = item.CodeFunction; + try { // Skip static constructors - they should not have an access modifier. @@ -259,7 +321,14 @@ public void InsertExplicitAccessModifiersOnMethods(IEnumerable m if (!IsAccessModifierExplicitlySpecifiedOnCodeElement(methodDeclaration, codeFunction.Access)) { // Set the access value to itself to cause the code to be added. - codeFunction.Access = codeFunction.Access; + try + { + codeFunction.Access = codeFunction.Access; + } + catch (Exception ex) + { + OutputWindowHelper.WarningWriteLine($"Unable to set explicit access modifier on method {codeFunction.Name}: {ex.Message}"); + } } } } @@ -270,10 +339,14 @@ public void InsertExplicitAccessModifiersOnMethods(IEnumerable m /// The properties. public void InsertExplicitAccessModifiersOnProperties(IEnumerable properties) { + ThreadHelper.ThrowIfNotOnUIThread(); if (!Settings.Default.Cleaning_InsertExplicitAccessModifiersOnProperties) return; - foreach (var codeProperty in properties.Select(x => x.CodeProperty).Where(y => y != null)) + foreach (var item in properties) { + if (item?.CodeProperty == null) continue; + var codeProperty = item.CodeProperty; + try { // Skip explicit interface implementations. @@ -299,7 +372,14 @@ public void InsertExplicitAccessModifiersOnProperties(IEnumerableThe structs. public void InsertExplicitAccessModifiersOnStructs(IEnumerable structs) { + ThreadHelper.ThrowIfNotOnUIThread(); if (!Settings.Default.Cleaning_InsertExplicitAccessModifiersOnStructs) return; - foreach (var codeStruct in structs.Select(x => x.CodeStruct).Where(y => y != null)) + foreach (var item in structs) { + if (item?.CodeStruct == null) continue; + var codeStruct = item.CodeStruct; + var structDeclaration = CodeElementHelper.GetStructDeclaration(codeStruct); if (!IsAccessModifierExplicitlySpecifiedOnCodeElement(structDeclaration, codeStruct.Access)) { // Set the access value to itself to cause the code to be added. - codeStruct.Access = codeStruct.Access; + try + { + codeStruct.Access = codeStruct.Access; + } + catch (Exception ex) + { + OutputWindowHelper.WarningWriteLine($"Unable to set explicit access modifier on struct {codeStruct.Name}: {ex.Message}"); + } } } } @@ -336,6 +427,7 @@ public void InsertExplicitAccessModifiersOnStructs(IEnumerable s /// True if access modifier is explicitly specified, otherwise false. private static bool IsAccessModifierExplicitlySpecifiedOnCodeElement(string codeElementDeclaration, vsCMAccess accessModifier) { + ThreadHelper.ThrowIfNotOnUIThread(); string keyword = CodeElementHelper.GetAccessModifierKeyword(accessModifier); return IsKeywordSpecified(codeElementDeclaration, keyword); @@ -349,6 +441,7 @@ private static bool IsAccessModifierExplicitlySpecifiedOnCodeElement(string code /// True if the keyword is present, otherwise false. private static bool IsKeywordSpecified(string codeElementDeclaration, string keyword) { + ThreadHelper.ThrowIfNotOnUIThread(); string matchString = @"(^|\s)" + keyword + @"\s"; return RegexNullSafe.IsMatch(codeElementDeclaration, matchString); @@ -356,4 +449,4 @@ private static bool IsKeywordSpecified(string codeElementDeclaration, string key #endregion Helper Methods } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Logic/Cleaning/InsertWhitespaceLogic.cs b/CodeMaidShared/Logic/Cleaning/InsertWhitespaceLogic.cs index d745feca2..1c9c56c20 100644 --- a/CodeMaidShared/Logic/Cleaning/InsertWhitespaceLogic.cs +++ b/CodeMaidShared/Logic/Cleaning/InsertWhitespaceLogic.cs @@ -1,7 +1,8 @@ -using EnvDTE; +using EnvDTE; using SteveCadwallader.CodeMaid.Helpers; using SteveCadwallader.CodeMaid.Properties; using System; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Logic.Cleaning { @@ -52,6 +53,7 @@ private InsertWhitespaceLogic(CodeMaidPackage package) /// The text document to cleanup. internal void InsertBlankSpaceBeforeSelfClosingAngleBracket(TextDocument textDocument) { + ThreadHelper.ThrowIfNotOnUIThread(); if (!Settings.Default.Cleaning_InsertBlankSpaceBeforeSelfClosingAngleBrackets) return; const string pattern = @"([^ \t])/>"; @@ -66,6 +68,7 @@ internal void InsertBlankSpaceBeforeSelfClosingAngleBracket(TextDocument textDoc /// The text document to cleanup. internal void InsertEOFTrailingNewLine(TextDocument textDocument) { + ThreadHelper.ThrowIfNotOnUIThread(); if (!Settings.Default.Cleaning_InsertEndOfFileTrailingNewLine) return; EditPoint cursor = textDocument.EndPoint.CreateEditPoint(); @@ -78,4 +81,4 @@ internal void InsertEOFTrailingNewLine(TextDocument textDocument) #endregion Methods } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Logic/Cleaning/RemoveRegionLogic.cs b/CodeMaidShared/Logic/Cleaning/RemoveRegionLogic.cs index 8bc094f7d..4e935f77c 100644 --- a/CodeMaidShared/Logic/Cleaning/RemoveRegionLogic.cs +++ b/CodeMaidShared/Logic/Cleaning/RemoveRegionLogic.cs @@ -1,4 +1,4 @@ -using EnvDTE; +using EnvDTE; using SteveCadwallader.CodeMaid.Helpers; using SteveCadwallader.CodeMaid.Model; using SteveCadwallader.CodeMaid.Model.CodeItems; @@ -7,6 +7,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Logic.Cleaning { @@ -60,6 +61,7 @@ private RemoveRegionLogic(CodeMaidPackage package) /// True if document can remove regions, otherwise false. internal bool CanRemoveRegions(Document document) { + ThreadHelper.ThrowIfNotOnUIThread(); return _package.IDE.Debugger.CurrentMode == dbgDebugMode.dbgDesignMode && document != null && (document.GetCodeLanguage() == CodeLanguage.CSharp || @@ -72,6 +74,7 @@ internal bool CanRemoveRegions(Document document) /// The text document to update. internal void RemoveRegions(TextDocument textDocument) { + ThreadHelper.ThrowIfNotOnUIThread(); // Retrieve the regions and put them in reverse order (reduces line number updates during removal). var regions = _codeModelHelper.RetrieveCodeRegions(textDocument).OrderByDescending(x => x.StartLine); @@ -79,7 +82,7 @@ internal void RemoveRegions(TextDocument textDocument) { foreach (var region in regions) { - RemoveRegion(region); + RemoveRegion(region, createTransaction: false); } }); } @@ -90,6 +93,7 @@ internal void RemoveRegions(TextDocument textDocument) /// The text selection to update. internal void RemoveRegions(TextSelection textSelection) { + ThreadHelper.ThrowIfNotOnUIThread(); // Retrieve the regions and put them in reverse order (reduces line number updates during removal). var regions = _codeModelHelper.RetrieveCodeRegions(textSelection).OrderByDescending(x => x.StartLine); @@ -97,7 +101,7 @@ internal void RemoveRegions(TextSelection textSelection) { foreach (var region in regions) { - RemoveRegion(region); + RemoveRegion(region, createTransaction: false); } }); } @@ -108,12 +112,14 @@ internal void RemoveRegions(TextSelection textSelection) /// The regions to update. internal void RemoveRegions(IEnumerable regions) { + ThreadHelper.ThrowIfNotOnUIThread(); new UndoTransactionHelper(_package, Resources.CodeMaidRemoveRegions).Run(() => { + ThreadHelper.ThrowIfNotOnUIThread(); // Iterate through regions in reverse order (reduces line number updates during removal). foreach (var region in regions.OrderByDescending(x => x.StartLine)) { - RemoveRegion(region); + RemoveRegion(region, createTransaction: false); } }); } @@ -124,6 +130,7 @@ internal void RemoveRegions(IEnumerable regions) /// The regions to update. internal void RemoveRegionsPerSettings(IEnumerable regions) { + ThreadHelper.ThrowIfNotOnUIThread(); var setting = (NoneEmptyAll)Settings.Default.Cleaning_RemoveRegions; if (setting == NoneEmptyAll.None) return; @@ -136,7 +143,7 @@ internal void RemoveRegionsPerSettings(IEnumerable regions) continue; } - RemoveRegion(region); + RemoveRegion(region, createTransaction: false); } } @@ -144,14 +151,15 @@ internal void RemoveRegionsPerSettings(IEnumerable regions) /// Removes the region tags from the specified region. /// /// The region to update. - internal void RemoveRegion(CodeItemRegion region) + internal void RemoveRegion(CodeItemRegion region, bool createTransaction = true) { + ThreadHelper.ThrowIfNotOnUIThread(); if (region == null || region.IsInvalidated || region.IsPseudoGroup || region.StartLine <= 0 || region.EndLine <= 0) { return; } - new UndoTransactionHelper(_package, Resources.CodeMaidRemoveRegion + region.Name).Run(() => + Action removeAction = () => { var end = region.EndPoint.CreateEditPoint(); end.StartOfLine(); @@ -166,9 +174,18 @@ internal void RemoveRegion(CodeItemRegion region) start.Insert(Environment.NewLine); region.IsInvalidated = true; - }); + }; + + if (createTransaction) + { + new UndoTransactionHelper(_package, Resources.CodeMaidRemoveRegion + region.Name).Run(removeAction); + } + else + { + removeAction(); + } } #endregion Methods } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Logic/Cleaning/RemoveWhitespaceLogic.cs b/CodeMaidShared/Logic/Cleaning/RemoveWhitespaceLogic.cs index be0d1ad92..6b261f4f1 100644 --- a/CodeMaidShared/Logic/Cleaning/RemoveWhitespaceLogic.cs +++ b/CodeMaidShared/Logic/Cleaning/RemoveWhitespaceLogic.cs @@ -1,7 +1,8 @@ -using EnvDTE; +using EnvDTE; using SteveCadwallader.CodeMaid.Helpers; using SteveCadwallader.CodeMaid.Properties; using System; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Logic.Cleaning { @@ -52,6 +53,7 @@ private RemoveWhitespaceLogic(CodeMaidPackage package) /// The text document to cleanup. internal void RemoveBlankLinesAtBottom(TextDocument textDocument) { + ThreadHelper.ThrowIfNotOnUIThread(); if (!Settings.Default.Cleaning_RemoveBlankLinesAtBottom) return; EditPoint cursor = textDocument.EndPoint.CreateEditPoint(); @@ -64,6 +66,7 @@ internal void RemoveBlankLinesAtBottom(TextDocument textDocument) /// The text document to cleanup. internal void RemoveBlankLinesAtTop(TextDocument textDocument) { + ThreadHelper.ThrowIfNotOnUIThread(); if (!Settings.Default.Cleaning_RemoveBlankLinesAtTop) return; EditPoint cursor = textDocument.StartPoint.CreateEditPoint(); @@ -76,6 +79,7 @@ internal void RemoveBlankLinesAtTop(TextDocument textDocument) /// The text document to cleanup. internal void RemoveBlankLinesAfterAttributes(TextDocument textDocument) { + ThreadHelper.ThrowIfNotOnUIThread(); if (!Settings.Default.Cleaning_RemoveBlankLinesAfterAttributes) return; const string pattern = @"(^[ \t]*\[[^\]]+\][ \t]*(//[^\r\n]*)*)(\r?\n){2}(?![ \t]*//)"; @@ -90,6 +94,7 @@ internal void RemoveBlankLinesAfterAttributes(TextDocument textDocument) /// The text document to cleanup. internal void RemoveBlankLinesAfterOpeningBrace(TextDocument textDocument) { + ThreadHelper.ThrowIfNotOnUIThread(); if (!Settings.Default.Cleaning_RemoveBlankLinesAfterOpeningBrace) return; const string pattern = @"\{([ \t]*(//[^\r\n]*)*)(\r?\n){2,}"; @@ -104,6 +109,7 @@ internal void RemoveBlankLinesAfterOpeningBrace(TextDocument textDocument) /// The text document to cleanup. internal void RemoveBlankLinesBeforeClosingBrace(TextDocument textDocument) { + ThreadHelper.ThrowIfNotOnUIThread(); if (!Settings.Default.Cleaning_RemoveBlankLinesBeforeClosingBrace) return; const string pattern = @"(\r?\n){2,}([ \t]*)\}"; @@ -118,6 +124,7 @@ internal void RemoveBlankLinesBeforeClosingBrace(TextDocument textDocument) /// The text document to cleanup. internal void RemoveBlankLinesBeforeClosingTag(TextDocument textDocument) { + ThreadHelper.ThrowIfNotOnUIThread(); if (!Settings.Default.Cleaning_RemoveBlankLinesBeforeClosingTags) return; const string pattern = @"(\r?\n){2,}([ \t]*)The text document to cleanup. internal void RemoveBlankLinesBetweenChainedStatements(TextDocument textDocument) { + ThreadHelper.ThrowIfNotOnUIThread(); if (!Settings.Default.Cleaning_RemoveBlankLinesBetweenChainedStatements) return; const string pattern = @"(\r?\n){2,}([ \t]*)(else|catch|finally)( |\t|\r?\n)"; @@ -146,6 +154,7 @@ internal void RemoveBlankLinesBetweenChainedStatements(TextDocument textDocument /// The text document to cleanup. internal void RemoveBlankSpacesBeforeClosingAngleBracket(TextDocument textDocument) { + ThreadHelper.ThrowIfNotOnUIThread(); if (!Settings.Default.Cleaning_RemoveBlankSpacesBeforeClosingAngleBrackets) return; // Remove blank spaces before regular closing angle brackets. @@ -177,6 +186,7 @@ internal void RemoveBlankSpacesBeforeClosingAngleBracket(TextDocument textDocume /// The text document to cleanup. internal void RemoveEOFTrailingNewLine(TextDocument textDocument) { + ThreadHelper.ThrowIfNotOnUIThread(); if (!Settings.Default.Cleaning_RemoveEndOfFileTrailingNewLine) return; EditPoint cursor = textDocument.EndPoint.CreateEditPoint(); @@ -185,6 +195,7 @@ internal void RemoveEOFTrailingNewLine(TextDocument textDocument) { // Make an exception for C++ resource files to work-around known EOF issue: http://connect.microsoft.com/VisualStudio/feedback/details/173903/resource-compiler-returns-a-rc1004-unexpected-eof-found-error#details if (textDocument.GetCodeLanguage() == CodeLanguage.CPlusPlus && + textDocument.Parent != null && (textDocument.Parent.FullName.EndsWith(".h") || textDocument.Parent.FullName.EndsWith(".rc2"))) { return; @@ -202,6 +213,7 @@ internal void RemoveEOFTrailingNewLine(TextDocument textDocument) /// The text document to cleanup. internal void RemoveEOLWhitespace(TextDocument textDocument) { + ThreadHelper.ThrowIfNotOnUIThread(); if (!Settings.Default.Cleaning_RemoveEndOfLineWhitespace) return; const string pattern = @"[ \t]+\r?\n"; @@ -216,6 +228,7 @@ internal void RemoveEOLWhitespace(TextDocument textDocument) /// The text document to cleanup. internal void RemoveMultipleConsecutiveBlankLines(TextDocument textDocument) { + ThreadHelper.ThrowIfNotOnUIThread(); if (!Settings.Default.Cleaning_RemoveMultipleConsecutiveBlankLines) return; const string pattern = @"(\r?\n){3,}"; @@ -226,4 +239,4 @@ internal void RemoveMultipleConsecutiveBlankLines(TextDocument textDocument) #endregion Methods } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Logic/Cleaning/UpdateLogic.cs b/CodeMaidShared/Logic/Cleaning/UpdateLogic.cs index a4651b080..6047a6276 100644 --- a/CodeMaidShared/Logic/Cleaning/UpdateLogic.cs +++ b/CodeMaidShared/Logic/Cleaning/UpdateLogic.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Logic.Cleaning { @@ -62,6 +63,7 @@ private UpdateLogic(CodeMaidPackage package) /// The text document to cleanup. internal void UpdateEndRegionDirectives(TextDocument textDocument) { + ThreadHelper.ThrowIfNotOnUIThread(); if (!Settings.Default.Cleaning_UpdateEndRegionDirectives) return; var regionStack = new Stack(); @@ -108,16 +110,16 @@ internal void UpdateEndRegionDirectives(TextDocument textDocument) cursor.Delete(eolCursor); cursor.Insert(" " + matchingRegion); } + else + { + // This document is improperly formatted, but we continue processing instead of aborting. + OutputWindowHelper.DiagnosticWriteLine("Improperly formatted endregion directive found; skipping this directive."); + } } - else - { - // This document is improperly formatted, abort. - return; - } - } - // Note: eolCursor may be outdated now if changes have been made. - cursor.EndOfLine(); + // Note: eolCursor may be outdated now if changes have been made. + cursor.EndOfLine(); + } } } @@ -127,24 +129,29 @@ internal void UpdateEndRegionDirectives(TextDocument textDocument) /// The events to update. internal void UpdateEventAccessorsToBothBeSingleLineOrMultiLine(IEnumerable events) { + ThreadHelper.ThrowIfNotOnUIThread(); if (!Settings.Default.Cleaning_UpdateAccessorsToBothBeSingleLineOrMultiLine) return; foreach (var item in events) { + if (item?.CodeEvent == null) continue; UpdateAccessorsToBothBeSingleLineOrMultiLine(item.CodeEvent.Adder, item.CodeEvent.Remover); } } + /// /// Updates the property accessors to either both be single-line or multi-line. /// /// The properties to update. internal void UpdatePropertyAccessorsToBothBeSingleLineOrMultiLine(IEnumerable properties) { + ThreadHelper.ThrowIfNotOnUIThread(); if (!Settings.Default.Cleaning_UpdateAccessorsToBothBeSingleLineOrMultiLine) return; foreach (var item in properties) { + if (item?.CodeProperty == null) continue; UpdateAccessorsToBothBeSingleLineOrMultiLine(item.CodeProperty.Getter, item.CodeProperty.Setter); } } @@ -155,9 +162,10 @@ internal void UpdatePropertyAccessorsToBothBeSingleLineOrMultiLine(IEnumerableThe methods to update. internal void UpdateSingleLineMethods(IEnumerable methods) { + ThreadHelper.ThrowIfNotOnUIThread(); if (!Settings.Default.Cleaning_UpdateSingleLineMethods) return; - var singleLineMethods = methods.Where(x => x.StartPoint.Line == x.EndPoint.Line && x.OverrideKind != vsCMOverrideKind.vsCMOverrideKindAbstract && !(x.CodeFunction.Parent is CodeInterface)); + var singleLineMethods = methods.Where(x => x != null && x.CodeFunction != null && x.StartPoint.Line == x.EndPoint.Line && x.OverrideKind != vsCMOverrideKind.vsCMOverrideKindAbstract && !(x.CodeFunction.Parent is CodeInterface)); foreach (var singleLineMethod in singleLineMethods) { SpreadSingleLineMethodOntoMultipleLines(singleLineMethod.CodeFunction); @@ -170,6 +178,7 @@ internal void UpdateSingleLineMethods(IEnumerable methods) /// The method to update. private void JoinMultiLineMethodOntoSingleLine(CodeFunction method) { + ThreadHelper.ThrowIfNotOnUIThread(); var start = method.StartPoint.CreateEditPoint(); var end = method.EndPoint.CreateEditPoint(); @@ -186,6 +195,7 @@ private void JoinMultiLineMethodOntoSingleLine(CodeFunction method) /// The method to update. private void SpreadSingleLineMethodOntoMultipleLines(CodeFunction method) { + ThreadHelper.ThrowIfNotOnUIThread(); try { var start = method.GetStartPoint(vsCMPart.vsCMPartBody).CreateEditPoint(); @@ -220,6 +230,7 @@ private void SpreadSingleLineMethodOntoMultipleLines(CodeFunction method) /// The second accessor. private void UpdateAccessorsToBothBeSingleLineOrMultiLine(CodeFunction first, CodeFunction second) { + ThreadHelper.ThrowIfNotOnUIThread(); if (first == null || second == null) return; bool isFirstSingleLine = first.StartPoint.Line == first.EndPoint.Line; @@ -257,4 +268,4 @@ private void UpdateAccessorsToBothBeSingleLineOrMultiLine(CodeFunction first, Co #endregion Methods } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Logic/Cleaning/UsingStatementCleanupLogic.cs b/CodeMaidShared/Logic/Cleaning/UsingStatementCleanupLogic.cs index f83dfd305..2b0f94dcd 100644 --- a/CodeMaidShared/Logic/Cleaning/UsingStatementCleanupLogic.cs +++ b/CodeMaidShared/Logic/Cleaning/UsingStatementCleanupLogic.cs @@ -3,6 +3,7 @@ using SteveCadwallader.CodeMaid.Properties; using System; using System.Linq; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Logic.Cleaning { @@ -67,6 +68,7 @@ private UsingStatementCleanupLogic(CodeMaidPackage package) /// The text document to update. public void RemoveAndSortUsingStatements(TextDocument textDocument) { + ThreadHelper.ThrowIfNotOnUIThread(); if (!Settings.Default.Cleaning_RunVisualStudioRemoveAndSortUsingStatements) return; if (_package.IsAutoSaveContext && Settings.Default.Cleaning_SkipRemoveAndSortUsingStatementsDuringAutoCleanupOnSave) return; @@ -74,14 +76,14 @@ public void RemoveAndSortUsingStatements(TextDocument textDocument) const string patternFormat = @"^[ \t]*{0}[ \t]*\r?\n"; var usingStatementsToReinsert = _usingStatementsToReinsertWhenRemoved.Value - .Where(usingStatement => TextDocumentHelper.FirstOrDefaultMatch(textDocument, string.Format(patternFormat, usingStatement)) != null) + .Where(usingStatement => TextDocumentHelper.FirstOrDefaultMatch(textDocument, string.Format(patternFormat, System.Text.RegularExpressions.Regex.Escape(usingStatement))) != null) .ToList(); _commandHelper.ExecuteCommand(textDocument, "EditorContextMenus.CodeWindow.RemoveAndSort"); // Ignore any using statements that are still referenced usingStatementsToReinsert = usingStatementsToReinsert - .Where(usingStatement => TextDocumentHelper.FirstOrDefaultMatch(textDocument, string.Format(patternFormat, usingStatement)) == null) + .Where(usingStatement => TextDocumentHelper.FirstOrDefaultMatch(textDocument, string.Format(patternFormat, System.Text.RegularExpressions.Regex.Escape(usingStatement))) == null) .ToList(); if (usingStatementsToReinsert.Count > 0) @@ -102,4 +104,4 @@ public void RemoveAndSortUsingStatements(TextDocument textDocument) #endregion Methods } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Logic/Digging/OutliningSynchronizationManager.cs b/CodeMaidShared/Logic/Digging/OutliningSynchronizationManager.cs index 48fd457fe..7f3c97e9a 100644 --- a/CodeMaidShared/Logic/Digging/OutliningSynchronizationManager.cs +++ b/CodeMaidShared/Logic/Digging/OutliningSynchronizationManager.cs @@ -60,6 +60,7 @@ public Document Document get { return _document; } set { + ThreadHelper.ThrowIfNotOnUIThread(); if (_document != value) { if (_document != null && _outliningManager != null) @@ -92,6 +93,7 @@ public Document Document /// The code items. public void UpdateCodeItems(SetCodeItems codeItems) { + ThreadHelper.ThrowIfNotOnUIThread(); TearDownCodeItemParents(); // Retrieve and cache an updated list of code item parents. @@ -111,6 +113,7 @@ public void UpdateCodeItems(SetCodeItems codeItems) /// The event arguments. private void OnCodeItemParentIsExpandedChanged(object sender, EventArgs eventArgs) { + ThreadHelper.ThrowIfNotOnUIThread(); if (sender is ICodeItemParent codeItemParent) { var iCollapsible = FindCollapsibleFromCodeItemParent(codeItemParent); @@ -189,6 +192,7 @@ private ICodeItemParent FindCodeItemParentFromCollapsible(ICollapsible collapsib /// The on the same starting line, otherwise null. private ICollapsible FindCollapsibleFromCodeItemParent(ICodeItemParent parent) { + ThreadHelper.ThrowIfNotOnUIThread(); if (_outliningManager == null || _wpfTextView == null) { return null; @@ -234,6 +238,7 @@ private static int GetStartLineForCollapsible(ICollapsible collapsible) /// The associated outlining manager, otherwise null. private IOutliningManager GetOutliningManager(Document document) { + ThreadHelper.ThrowIfNotOnUIThread(); try { _wpfTextView = GetWpfTextView(document); @@ -257,6 +262,7 @@ private IOutliningManager GetOutliningManager(Document document) /// The associated WPF text view, otherwise null. private IWpfTextView GetWpfTextView(Document document) { + ThreadHelper.ThrowIfNotOnUIThread(); var textView = GetTextView(document); if (textView != null && _editorAdaptersFactoryService != null) { @@ -273,6 +279,7 @@ private IWpfTextView GetWpfTextView(Document document) /// The associated text view, otherwise null. private IVsTextView GetTextView(Document document) { + ThreadHelper.ThrowIfNotOnUIThread(); if (document == null) { return null; @@ -309,6 +316,7 @@ private static IEnumerable RecursivelyGetAllCodeItemParents(Set /// private void InitializeCodeItemParents() { + ThreadHelper.ThrowIfNotOnUIThread(); foreach (var codeItemParent in _codeItemParents ?? Enumerable.Empty()) { var iCollapsible = FindCollapsibleFromCodeItemParent(codeItemParent); @@ -345,6 +353,7 @@ private void TearDownCodeItemParents() /// public void Dispose() { + ThreadHelper.ThrowIfNotOnUIThread(); TearDownCodeItemParents(); Document = null; @@ -352,4 +361,4 @@ public void Dispose() #endregion Implementation of IDisposable } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Logic/Formatting/CommentFormatLogic.cs b/CodeMaidShared/Logic/Formatting/CommentFormatLogic.cs index 318037f88..e47a82005 100644 --- a/CodeMaidShared/Logic/Formatting/CommentFormatLogic.cs +++ b/CodeMaidShared/Logic/Formatting/CommentFormatLogic.cs @@ -1,9 +1,10 @@ -using EnvDTE; +using EnvDTE; using SteveCadwallader.CodeMaid.Helpers; using SteveCadwallader.CodeMaid.Model.Comments; using SteveCadwallader.CodeMaid.Model.Comments.Options; using SteveCadwallader.CodeMaid.Properties; using System.Linq; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Logic.Formatting { @@ -44,6 +45,7 @@ private CommentFormatLogic(CodeMaidPackage package) /// The text document. public void FormatComments(TextDocument textDocument) { + ThreadHelper.ThrowIfNotOnUIThread(); if (!Settings.Default.Formatting_CommentRunDuringCleanup) return; FormatComments(textDocument, textDocument.StartPoint.CreateEditPoint(), textDocument.EndPoint.CreateEditPoint()); @@ -58,12 +60,14 @@ public void FormatComments(TextDocument textDocument) /// The end point. public bool FormatComments(TextDocument textDocument, EditPoint start, EditPoint end) { + ThreadHelper.ThrowIfNotOnUIThread(); bool foundComments = false; var options = FormatterOptions .FromSettings(Settings.Default) .Set(o => { + ThreadHelper.ThrowIfNotOnUIThread(); o.TabSize = textDocument.TabSize; o.IgnoreTokens = CodeCommentHelper .GetTaskListTokens(_package) @@ -113,4 +117,4 @@ internal static CommentFormatLogic GetInstance(CodeMaidPackage package) #endregion Methods } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Logic/Reorganizing/CodeReorganizationAvailabilityLogic.cs b/CodeMaidShared/Logic/Reorganizing/CodeReorganizationAvailabilityLogic.cs index 35976e8dd..dad0fbcd5 100644 --- a/CodeMaidShared/Logic/Reorganizing/CodeReorganizationAvailabilityLogic.cs +++ b/CodeMaidShared/Logic/Reorganizing/CodeReorganizationAvailabilityLogic.cs @@ -1,9 +1,10 @@ -using EnvDTE; +using EnvDTE; using SteveCadwallader.CodeMaid.Helpers; using SteveCadwallader.CodeMaid.Properties; using SteveCadwallader.CodeMaid.UI.Dialogs.Prompts; using SteveCadwallader.CodeMaid.UI.Enumerations; using System; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Logic.Reorganizing { @@ -56,6 +57,7 @@ private CodeReorganizationAvailabilityLogic(CodeMaidPackage package) /// True if item can be reorganized, otherwise false. internal bool CanReorganize(Document document, bool allowUserPrompts = false) { + ThreadHelper.ThrowIfNotOnUIThread(); if (!IsReorganizationEnvironmentAvailable()) { OutputWindowHelper.DiagnosticWriteLine($"CodeReorganizationAvailabilityLogic.CanReorganize returned false due to the reorganization environment not being available."); @@ -95,6 +97,7 @@ internal bool CanReorganize(Document document, bool allowUserPrompts = false) /// True if reorganization can occur, false otherwise. internal bool IsReorganizationEnvironmentAvailable() { + ThreadHelper.ThrowIfNotOnUIThread(); return _package.IDE.Debugger.CurrentMode == dbgDebugMode.dbgDesignMode; } @@ -113,6 +116,7 @@ internal bool IsReorganizationEnvironmentAvailable() /// private bool IsDocumentExcludedBecausePreprocessorConditionals(Document document, bool allowUserPrompts) { + ThreadHelper.ThrowIfNotOnUIThread(); if (!HasPreprocessorConditionalCompilationDirectives(document)) return false; switch ((AskYesNo)Settings.Default.Reorganizing_PerformWhenPreprocessorConditionals) @@ -142,6 +146,7 @@ private bool IsDocumentExcludedBecausePreprocessorConditionals(Document document /// True if preprocessor conditional compilation directives are detected, otherwise false. private bool HasPreprocessorConditionalCompilationDirectives(Document document) { + ThreadHelper.ThrowIfNotOnUIThread(); var textDocument = document.GetTextDocument(); if (textDocument != null) { @@ -164,6 +169,7 @@ private bool HasPreprocessorConditionalCompilationDirectives(Document document) /// True if files with preprocessor conditionals should be reorganized, otherwise false. private static bool PromptUserAboutReorganizingPreprocessorConditionals(Document document) { + ThreadHelper.ThrowIfNotOnUIThread(); try { var viewModel = new YesNoPromptViewModel @@ -203,4 +209,4 @@ private static bool PromptUserAboutReorganizingPreprocessorConditionals(Document #endregion Private Methods } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Logic/Reorganizing/CodeReorganizationManager.cs b/CodeMaidShared/Logic/Reorganizing/CodeReorganizationManager.cs index e333e76bc..0807953b8 100644 --- a/CodeMaidShared/Logic/Reorganizing/CodeReorganizationManager.cs +++ b/CodeMaidShared/Logic/Reorganizing/CodeReorganizationManager.cs @@ -8,6 +8,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Logic.Reorganizing { @@ -73,6 +74,7 @@ private CodeReorganizationManager(CodeMaidPackage package) /// The base item. internal void MoveItemAboveBase(BaseCodeItem itemToMove, BaseCodeItem baseItem) { + ThreadHelper.ThrowIfNotOnUIThread(); new UndoTransactionHelper(_package, Resources.CodeMaidMoveItemAbove).Run( () => RepositionItemAboveBase(itemToMove, baseItem)); } @@ -84,6 +86,7 @@ internal void MoveItemAboveBase(BaseCodeItem itemToMove, BaseCodeItem baseItem) /// The base item. internal void MoveItemBelowBase(BaseCodeItem itemToMove, BaseCodeItem baseItem) { + ThreadHelper.ThrowIfNotOnUIThread(); new UndoTransactionHelper(_package, Resources.CodeMaidMoveItemBelow).Run( () => RepositionItemBelowBase(itemToMove, baseItem)); } @@ -95,6 +98,7 @@ internal void MoveItemBelowBase(BaseCodeItem itemToMove, BaseCodeItem baseItem) /// The base item. internal void MoveItemIntoBase(BaseCodeItem itemToMove, ICodeItemParent baseItem) { + ThreadHelper.ThrowIfNotOnUIThread(); new UndoTransactionHelper(_package, Resources.CodeMaidMoveItemInto).Run( () => RepositionItemIntoBase(itemToMove, baseItem)); } @@ -105,6 +109,7 @@ internal void MoveItemIntoBase(BaseCodeItem itemToMove, ICodeItemParent baseItem /// The document for reorganizing. internal void Reorganize(Document document) { + ThreadHelper.ThrowIfNotOnUIThread(); if (!_codeReorganizationAvailabilityLogic.CanReorganize(document, true)) return; new UndoTransactionHelper(_package, string.Format(Resources.CodeMaidReorganizeFor0, document.Name)).Run( @@ -139,6 +144,7 @@ internal void Reorganize(Document document) /// The set of reorganizable code item elements. private static IList GetReorganizableCodeItemElements(IEnumerable codeItems) { + ThreadHelper.ThrowIfNotOnUIThread(); // Get all code item elements. var codeItemElements = codeItems.OfType().ToList(); @@ -160,6 +166,7 @@ private static IList GetReorganizableCodeItemElements(IEnum /// private static string GetTextAndRemoveItem(BaseCodeItem itemToRemove, out int cursorOffset) { + ThreadHelper.ThrowIfNotOnUIThread(); // Refresh the code item and capture its end points. itemToRemove.RefreshCachedPositionAndName(); var removeStartPoint = itemToRemove.StartPoint; @@ -205,6 +212,7 @@ private bool ShouldBeSeparatedByNewLine(BaseCodeItem firstItem, BaseCodeItem sec /// True if the parent's children should be reorganized, otherwise false. private bool ShouldReorganizeChildren(BaseCodeItemElement parent) { + ThreadHelper.ThrowIfNotOnUIThread(); // Enumeration values should never be reordered. if (parent is CodeItemEnum) { @@ -237,6 +245,7 @@ private bool ShouldReorganizeChildren(BaseCodeItemElement parent) /// The base item. private void RepositionItemAboveBase(BaseCodeItem itemToMove, BaseCodeItem baseItem) { + ThreadHelper.ThrowIfNotOnUIThread(); if (itemToMove == baseItem) return; bool separateWithNewLine = ShouldBeSeparatedByNewLine(itemToMove, baseItem); @@ -272,6 +281,7 @@ private void RepositionItemAboveBase(BaseCodeItem itemToMove, BaseCodeItem baseI /// The base item. private void RepositionItemBelowBase(BaseCodeItem itemToMove, BaseCodeItem baseItem) { + ThreadHelper.ThrowIfNotOnUIThread(); if (itemToMove == baseItem) return; bool separateWithNewLine = ShouldBeSeparatedByNewLine(baseItem, itemToMove); @@ -311,6 +321,7 @@ private void RepositionItemBelowBase(BaseCodeItem itemToMove, BaseCodeItem baseI /// The base item. private void RepositionItemIntoBase(BaseCodeItem itemToMove, ICodeItemParent baseItem) { + ThreadHelper.ThrowIfNotOnUIThread(); if (itemToMove == baseItem) return; bool padWithNewLine = _insertBlankLinePaddingLogic.ShouldBeFollowedByBlankLine(itemToMove); @@ -346,6 +357,7 @@ private void RepositionItemIntoBase(BaseCodeItem itemToMove, ICodeItemParent bas /// The parent to the code items, otherwise null. private void RecursivelyReorganize(IEnumerable codeItems, ICodeItemParent parent = null) { + ThreadHelper.ThrowIfNotOnUIThread(); if (!codeItems.Any()) { // If there are no code items, the only action we may want to take is conditionally insert regions. @@ -405,6 +417,7 @@ private void RecursivelyReorganize(IEnumerable codeItems, ICodeIte /// An updated code items collection. private IEnumerable RegionsRemoveExisting(IEnumerable codeItems) { + ThreadHelper.ThrowIfNotOnUIThread(); if (!Settings.Default.Reorganizing_RegionsRemoveExistingRegions) { return codeItems; @@ -479,6 +492,7 @@ private IEnumerable RegionsFlatten(IEnumerable codeI /// The parent to the code items, otherwise null. private void RegionsInsert(IEnumerable codeItems, ICodeItemParent parent) { + ThreadHelper.ThrowIfNotOnUIThread(); if (Settings.Default.Reorganizing_RegionsInsertNewRegions) { // Only insert regions when directly inside the scope of a class, interface or struct. @@ -491,4 +505,4 @@ private void RegionsInsert(IEnumerable codeItems, ICodeItemParent #endregion Private Methods } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Logic/Reorganizing/GenerateRegionLogic.cs b/CodeMaidShared/Logic/Reorganizing/GenerateRegionLogic.cs index 3da5e7c92..c01bb6cf0 100644 --- a/CodeMaidShared/Logic/Reorganizing/GenerateRegionLogic.cs +++ b/CodeMaidShared/Logic/Reorganizing/GenerateRegionLogic.cs @@ -1,4 +1,4 @@ -using EnvDTE; +using EnvDTE; using SteveCadwallader.CodeMaid.Helpers; using SteveCadwallader.CodeMaid.Logic.Cleaning; using SteveCadwallader.CodeMaid.Model.CodeItems; @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Linq; using Thread = System.Threading.Thread; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Logic.Reorganizing { @@ -92,6 +93,7 @@ public IEnumerable GetRegionsToRemove(IEnumerable /// The default insertion point. public void InsertRegions(IEnumerable codeItems, EditPoint insertPoint) { + ThreadHelper.ThrowIfNotOnUIThread(); // Refresh and sort the code items. foreach (var codeItem in codeItems) { @@ -168,6 +170,7 @@ public void InsertRegions(IEnumerable codeItems, EditPoint insertP /// The updated cursor. public EditPoint InsertRegionTag(CodeItemRegion region, EditPoint startPoint) { + ThreadHelper.ThrowIfNotOnUIThread(); var cursor = startPoint.CreateEditPoint(); // If the cursor is not preceeded only by whitespace, insert a new line. @@ -200,6 +203,7 @@ public EditPoint InsertRegionTag(CodeItemRegion region, EditPoint startPoint) /// The updated cursor. public EditPoint InsertEndRegionTag(CodeItemRegion region, EditPoint endPoint) { + ThreadHelper.ThrowIfNotOnUIThread(); var cursor = endPoint.CreateEditPoint(); // If the cursor is not preceeded only by whitespace, insert a new line. @@ -352,4 +356,4 @@ private CodeItemRegion ComposeRegionForCodeItem(BaseCodeItem codeItem) #endregion Methods } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Model/CodeItems/BaseCodeItem.cs b/CodeMaidShared/Model/CodeItems/BaseCodeItem.cs index aa5e15702..330667053 100644 --- a/CodeMaidShared/Model/CodeItems/BaseCodeItem.cs +++ b/CodeMaidShared/Model/CodeItems/BaseCodeItem.cs @@ -1,6 +1,7 @@ using EnvDTE; using SteveCadwallader.CodeMaid.UI; using System.Diagnostics; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Model.CodeItems { @@ -74,6 +75,7 @@ public virtual void LoadLazyInitializedValues() /// public virtual void RefreshCachedPositionAndName() { + ThreadHelper.ThrowIfNotOnUIThread(); StartLine = StartPoint.Line; StartOffset = StartPoint.AbsoluteCharOffset; EndLine = EndPoint.Line; @@ -82,4 +84,4 @@ public virtual void RefreshCachedPositionAndName() #endregion Methods } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Model/CodeItems/BaseCodeItemElement.cs b/CodeMaidShared/Model/CodeItems/BaseCodeItemElement.cs index 595e40810..0c5734851 100644 --- a/CodeMaidShared/Model/CodeItems/BaseCodeItemElement.cs +++ b/CodeMaidShared/Model/CodeItems/BaseCodeItemElement.cs @@ -2,6 +2,7 @@ using SteveCadwallader.CodeMaid.Helpers; using System; using System.Threading; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Model.CodeItems { @@ -67,6 +68,7 @@ public override void LoadLazyInitializedValues() /// public override void RefreshCachedPositionAndName() { + ThreadHelper.ThrowIfNotOnUIThread(); var startPoint = CodeElement.GetStartPoint(); var endPoint = CodeElement.GetEndPoint(); @@ -154,6 +156,7 @@ protected static T TryDefault(Func func) /// The adjusted starting point. private static EditPoint GetStartPointAdjustedForComments(TextPoint originalPoint) { + ThreadHelper.ThrowIfNotOnUIThread(); var commentPrefix = CodeCommentHelper.GetCommentPrefix(originalPoint.Parent); var point = originalPoint.CreateEditPoint(); @@ -177,4 +180,4 @@ private static EditPoint GetStartPointAdjustedForComments(TextPoint originalPoin #endregion Methods } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Model/CodeItems/CodeItemClass.cs b/CodeMaidShared/Model/CodeItems/CodeItemClass.cs index 8e0c95798..61bc6798d 100644 --- a/CodeMaidShared/Model/CodeItems/CodeItemClass.cs +++ b/CodeMaidShared/Model/CodeItems/CodeItemClass.cs @@ -1,6 +1,7 @@ using EnvDTE; using EnvDTE80; using System; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Model.CodeItems { @@ -55,4 +56,4 @@ public CodeItemClass() #endregion Properties } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Model/CodeItems/CodeItemEvent.cs b/CodeMaidShared/Model/CodeItems/CodeItemEvent.cs index 78003dc65..63db26ad6 100644 --- a/CodeMaidShared/Model/CodeItems/CodeItemEvent.cs +++ b/CodeMaidShared/Model/CodeItems/CodeItemEvent.cs @@ -2,6 +2,7 @@ using EnvDTE80; using SteveCadwallader.CodeMaid.Helpers; using System; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Model.CodeItems { @@ -79,4 +80,4 @@ public override void LoadLazyInitializedValues() #endregion Properties } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Model/CodeItems/CodeItemField.cs b/CodeMaidShared/Model/CodeItems/CodeItemField.cs index e475e1afa..fa8a37c77 100644 --- a/CodeMaidShared/Model/CodeItems/CodeItemField.cs +++ b/CodeMaidShared/Model/CodeItems/CodeItemField.cs @@ -1,6 +1,7 @@ using EnvDTE; using EnvDTE80; using System; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Model.CodeItems { @@ -96,4 +97,4 @@ public override void LoadLazyInitializedValues() #endregion Properties } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Model/CodeItems/CodeItemMethod.cs b/CodeMaidShared/Model/CodeItems/CodeItemMethod.cs index d11909bf4..75427dbd9 100644 --- a/CodeMaidShared/Model/CodeItems/CodeItemMethod.cs +++ b/CodeMaidShared/Model/CodeItems/CodeItemMethod.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Model.CodeItems { @@ -147,4 +148,4 @@ public override void LoadLazyInitializedValues() #endregion Properties } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Model/CodeItems/CodeItemProperty.cs b/CodeMaidShared/Model/CodeItems/CodeItemProperty.cs index 8277b5c64..29067dd30 100644 --- a/CodeMaidShared/Model/CodeItems/CodeItemProperty.cs +++ b/CodeMaidShared/Model/CodeItems/CodeItemProperty.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Model.CodeItems { @@ -113,4 +114,4 @@ public override void LoadLazyInitializedValues() #endregion Properties } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Model/CodeItems/CodeItemRegion.cs b/CodeMaidShared/Model/CodeItems/CodeItemRegion.cs index e8b77c816..bb5aac1f3 100644 --- a/CodeMaidShared/Model/CodeItems/CodeItemRegion.cs +++ b/CodeMaidShared/Model/CodeItems/CodeItemRegion.cs @@ -1,6 +1,7 @@ using EnvDTE; using System; using System.Linq; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Model.CodeItems { @@ -55,6 +56,7 @@ public EditPoint InsertPoint { get { + ThreadHelper.ThrowIfNotOnUIThread(); var startPoint = StartPoint; if (startPoint != null) { @@ -96,6 +98,7 @@ public bool IsEmpty { get { + ThreadHelper.ThrowIfNotOnUIThread(); if (Children.Any()) { return false; @@ -125,4 +128,4 @@ public bool IsEmpty #endregion Properties } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Model/CodeItems/FactoryCodeItems.cs b/CodeMaidShared/Model/CodeItems/FactoryCodeItems.cs index 9750d9f1c..7a8923317 100644 --- a/CodeMaidShared/Model/CodeItems/FactoryCodeItems.cs +++ b/CodeMaidShared/Model/CodeItems/FactoryCodeItems.cs @@ -1,5 +1,6 @@ using EnvDTE; using EnvDTE80; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Model.CodeItems { @@ -15,6 +16,7 @@ public static class FactoryCodeItems /// A generated code item element, otherwise null. public static BaseCodeItemElement CreateCodeItemElement(CodeElement codeElement) { + ThreadHelper.ThrowIfNotOnUIThread(); if (codeElement == null) return null; BaseCodeItemElement codeItem; @@ -76,4 +78,4 @@ public static BaseCodeItemElement CreateCodeItemElement(CodeElement codeElement) return codeItem; } } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Model/CodeModel.cs b/CodeMaidShared/Model/CodeModel.cs index 130208d7e..0b3167699 100644 --- a/CodeMaidShared/Model/CodeModel.cs +++ b/CodeMaidShared/Model/CodeModel.cs @@ -1,7 +1,8 @@ -using EnvDTE; +using EnvDTE; using SteveCadwallader.CodeMaid.Helpers; using SteveCadwallader.CodeMaid.Model.CodeItems; using System.Threading; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Model { @@ -53,6 +54,7 @@ internal bool IsBuilding get { return _isBuilding; } set { + ThreadHelper.ThrowIfNotOnUIThread(); if (_isBuilding != value) { OutputWindowHelper.DiagnosticWriteLine($"CodeModel.IsBuilding changing to '{value}' for '{Document.FullName}'"); @@ -83,6 +85,7 @@ internal bool IsStale get { return _isStale; } set { + ThreadHelper.ThrowIfNotOnUIThread(); if (_isStale != value) { OutputWindowHelper.DiagnosticWriteLine($"CodeModel.IsStale changing to '{value}' for '{Document.FullName}'"); @@ -94,4 +97,4 @@ internal bool IsStale #endregion Properties } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Model/CodeModelBuilder.cs b/CodeMaidShared/Model/CodeModelBuilder.cs index c04a11280..1440680bb 100644 --- a/CodeMaidShared/Model/CodeModelBuilder.cs +++ b/CodeMaidShared/Model/CodeModelBuilder.cs @@ -1,7 +1,8 @@ -using EnvDTE; +using EnvDTE; using SteveCadwallader.CodeMaid.Helpers; using SteveCadwallader.CodeMaid.Model.CodeItems; using System.Linq; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Model { @@ -57,6 +58,7 @@ internal static CodeModelBuilder GetInstance(CodeMaidPackage package) /// The set of code items within the document, including regions. internal SetCodeItems RetrieveAllCodeItems(Document document) { + ThreadHelper.ThrowIfNotOnUIThread(); var codeItems = new SetCodeItems(); var fileCodeModel = RetrieveFileCodeModel(document.ProjectItem); @@ -78,6 +80,7 @@ internal SetCodeItems RetrieveAllCodeItems(Document document) /// The associated FileCodeModel, otherwise null. private FileCodeModel RetrieveFileCodeModel(ProjectItem projectItem) { + ThreadHelper.ThrowIfNotOnUIThread(); if (projectItem == null) { return null; @@ -112,6 +115,7 @@ private FileCodeModel RetrieveFileCodeModel(ProjectItem projectItem) /// The FileCodeModel to walk. private static void RetrieveCodeItems(SetCodeItems codeItems, FileCodeModel fcm) { + ThreadHelper.ThrowIfNotOnUIThread(); if (fcm != null && fcm.CodeElements != null) { RetrieveCodeItemsFromElements(codeItems, fcm.CodeElements); @@ -125,6 +129,7 @@ private static void RetrieveCodeItems(SetCodeItems codeItems, FileCodeModel fcm) /// The CodeElements to walk. private static void RetrieveCodeItemsFromElements(SetCodeItems codeItems, CodeElements codeElements) { + ThreadHelper.ThrowIfNotOnUIThread(); foreach (CodeElement child in codeElements) { RetrieveCodeItemsRecursively(codeItems, child); @@ -139,6 +144,7 @@ private static void RetrieveCodeItemsFromElements(SetCodeItems codeItems, CodeEl /// The CodeElement to walk (add and recurse). private static void RetrieveCodeItemsRecursively(SetCodeItems codeItems, CodeElement codeElement) { + ThreadHelper.ThrowIfNotOnUIThread(); var parentCodeItem = FactoryCodeItems.CreateCodeItemElement(codeElement); if (parentCodeItem != null) { @@ -153,4 +159,4 @@ private static void RetrieveCodeItemsRecursively(SetCodeItems codeItems, CodeEle #endregion Private Methods } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Model/CodeModelCache.cs b/CodeMaidShared/Model/CodeModelCache.cs index 2bbbffe2d..589a8dfd9 100644 --- a/CodeMaidShared/Model/CodeModelCache.cs +++ b/CodeMaidShared/Model/CodeModelCache.cs @@ -1,7 +1,8 @@ -using EnvDTE; +using EnvDTE; using SteveCadwallader.CodeMaid.Helpers; using SteveCadwallader.CodeMaid.Properties; using System.Collections.Generic; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Model { @@ -38,6 +39,7 @@ internal CodeModelCache() /// A code model representing the document. internal CodeModel GetCodeModel(Document document) { + ThreadHelper.ThrowIfNotOnUIThread(); CodeModel codeModel; OutputWindowHelper.DiagnosticWriteLine($"CodeModelCache.GetCodeModel for '{document.FullName}'"); @@ -71,6 +73,7 @@ internal CodeModel GetCodeModel(Document document) /// The document. internal void RemoveCodeModel(Document document) { + ThreadHelper.ThrowIfNotOnUIThread(); lock (_cache) { if (_cache.Remove(document.FullName)) @@ -86,6 +89,7 @@ internal void RemoveCodeModel(Document document) /// The document. internal void StaleCodeModel(Document document) { + ThreadHelper.ThrowIfNotOnUIThread(); if (_cache.TryGetValue(document.FullName, out CodeModel codeModel)) { codeModel.IsStale = true; @@ -95,4 +99,4 @@ internal void StaleCodeModel(Document document) #endregion Internal Methods } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Model/CodeModelHelper.cs b/CodeMaidShared/Model/CodeModelHelper.cs index 391cb3200..56e8c10f5 100644 --- a/CodeMaidShared/Model/CodeModelHelper.cs +++ b/CodeMaidShared/Model/CodeModelHelper.cs @@ -1,8 +1,9 @@ -using EnvDTE; +using EnvDTE; using SteveCadwallader.CodeMaid.Helpers; using SteveCadwallader.CodeMaid.Model.CodeItems; using System.Collections.Generic; using System.Linq; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Model { @@ -86,6 +87,7 @@ internal static IEnumerable> GetCodeItemBlocks(IEnumerable codeIt /// True if there is a region under the cursor, otherwise false. internal bool IsCodeRegionUnderCursor(TextDocument textDocument) { + ThreadHelper.ThrowIfNotOnUIThread(); if (textDocument != null && textDocument.Selection != null) { var cursor = textDocument.GetEditPointAtCursor(); @@ -104,6 +106,7 @@ internal bool IsCodeRegionUnderCursor(TextDocument textDocument) /// An enumerable collection of regions. internal IEnumerable RetrieveCodeRegions(TextDocument textDocument) { + ThreadHelper.ThrowIfNotOnUIThread(); var editPoints = TextDocumentHelper.FindMatches(textDocument, RegionPattern); return RetrieveCodeRegions(editPoints); @@ -116,6 +119,7 @@ internal IEnumerable RetrieveCodeRegions(TextDocument textDocume /// An enumerable collection of regions. internal IEnumerable RetrieveCodeRegions(TextSelection textSelection) { + ThreadHelper.ThrowIfNotOnUIThread(); var editPoints = TextDocumentHelper.FindMatches(textSelection, RegionPattern); return RetrieveCodeRegions(editPoints); @@ -128,6 +132,7 @@ internal IEnumerable RetrieveCodeRegions(TextSelection textSelec /// The region under the cursor, otherwise null. internal CodeItemRegion RetrieveCodeRegionUnderCursor(TextDocument textDocument) { + ThreadHelper.ThrowIfNotOnUIThread(); if (IsCodeRegionUnderCursor(textDocument)) { var regions = RetrieveCodeRegions(textDocument); @@ -159,6 +164,7 @@ internal CodeItemRegion RetrieveCodeRegionUnderCursor(TextDocument textDocument) /// An enumerable collection of regions. private static IEnumerable RetrieveCodeRegions(IEnumerable editPoints) { + ThreadHelper.ThrowIfNotOnUIThread(); var regionStack = new Stack(); var codeItems = new List(); @@ -207,4 +213,4 @@ private static IEnumerable RetrieveCodeRegions(IEnumerableThe document. internal void OnDocumentChanged(Document document) { + ThreadHelper.ThrowIfNotOnUIThread(); if (document != null) { _codeModelCache.StaleCodeModel(document); @@ -81,6 +83,7 @@ internal void OnDocumentChanged(Document document) /// The document. internal void OnDocumentClosing(Document document) { + ThreadHelper.ThrowIfNotOnUIThread(); if (document != null) { _codeModelCache.RemoveCodeModel(document); @@ -101,6 +104,7 @@ internal void OnDocumentClosing(Document document) /// The set of code items within the document. internal SetCodeItems RetrieveAllCodeItems(Document document, bool loadLazyInitializedValues = false) { + ThreadHelper.ThrowIfNotOnUIThread(); if (document == null) { throw new ArgumentNullException(nameof(document)); @@ -144,7 +148,7 @@ internal SetCodeItems RetrieveAllCodeItems(Document document, bool loadLazyIniti /// /// The set of code items within the document if already available, otherwise null. /// - internal SetCodeItems RetrieveAllCodeItemsAsync(Document document, bool loadLazyInitializedValues = false) + internal SetCodeItems BeginRetrieveAllCodeItems(Document document, bool loadLazyInitializedValues = false) { if (document == null) { @@ -158,7 +162,7 @@ internal SetCodeItems RetrieveAllCodeItemsAsync(Document document, bool loadLazy } OutputWindowHelper.DiagnosticWriteLine( - $"CodeModelManager.RetrieveAllCodeItemsAsync for '{document.FullName}'"); + $"CodeModelManager.BeginRetrieveAllCodeItems for '{document.FullName}'"); var codeModel = _codeModelCache.GetCodeModel(document); if (codeModel.IsBuilding) @@ -170,7 +174,7 @@ internal SetCodeItems RetrieveAllCodeItemsAsync(Document document, bool loadLazy if (codeModel.IsStale) { // Asynchronously build the code items then raise an event. - Task.Run(() => + _ = Task.Run(() => { BuildCodeItems(codeModel); @@ -200,6 +204,7 @@ internal SetCodeItems RetrieveAllCodeItemsAsync(Document document, bool loadLazy /// The code model. private void BuildCodeItems(CodeModel codeModel) { + ThreadHelper.ThrowIfNotOnUIThread(); try { OutputWindowHelper.DiagnosticWriteLine( @@ -238,6 +243,7 @@ private void BuildCodeItems(CodeModel codeModel) /// The code model. private void LoadLazyInitializedValues(CodeModel codeModel) { + ThreadHelper.ThrowIfNotOnUIThread(); try { OutputWindowHelper.DiagnosticWriteLine( @@ -261,6 +267,7 @@ private void LoadLazyInitializedValues(CodeModel codeModel) /// The code model. private void RaiseCodeModelBuilt(CodeModel codeModel) { + ThreadHelper.ThrowIfNotOnUIThread(); var codeModelBuilt = CodeModelBuilt; if (codeModelBuilt != null) { @@ -273,4 +280,4 @@ private void RaiseCodeModelBuilt(CodeModel codeModel) #endregion Private Methods } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Model/CodeTree/CodeTreeBuilderAsync.cs b/CodeMaidShared/Model/CodeTree/CodeTreeBuilderAsync.cs index c1da7fc1e..fe80a723d 100644 --- a/CodeMaidShared/Model/CodeTree/CodeTreeBuilderAsync.cs +++ b/CodeMaidShared/Model/CodeTree/CodeTreeBuilderAsync.cs @@ -40,7 +40,7 @@ internal CodeTreeBuilderAsync(Action callback) /// Builds a code tree asynchronously from the specified request. /// /// The request. - internal void RetrieveCodeTreeAsync(CodeTreeRequest request) + internal void BeginRetrieveCodeTree(CodeTreeRequest request) { if (_bw.IsBusy) { @@ -92,7 +92,7 @@ private void OnRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { if (_pendingRequest != null) { - RetrieveCodeTreeAsync(_pendingRequest); + BeginRetrieveCodeTree(_pendingRequest); } else if (e.Error == null) { diff --git a/CodeMaidShared/Model/Comments/CodeComment.cs b/CodeMaidShared/Model/Comments/CodeComment.cs index 708af41ca..177664c22 100644 --- a/CodeMaidShared/Model/Comments/CodeComment.cs +++ b/CodeMaidShared/Model/Comments/CodeComment.cs @@ -1,10 +1,11 @@ -using EnvDTE; +using EnvDTE; using SteveCadwallader.CodeMaid.Helpers; using SteveCadwallader.CodeMaid.Model.Comments.Options; using System; using System.Linq; using System.Text.RegularExpressions; using System.Xml.Linq; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.Model.Comments { @@ -32,6 +33,7 @@ internal class CodeComment /// public CodeComment(TextPoint point, FormatterOptions options) { + ThreadHelper.ThrowIfNotOnUIThread(); if (point == null) { throw new ArgumentNullException(nameof(point)); @@ -90,6 +92,7 @@ public static string Format(string text, string prefix = null, Action public TextPoint Format() { + ThreadHelper.ThrowIfNotOnUIThread(); if (!IsValid) { throw new InvalidOperationException("Cannot format comment, the comment is not valid."); @@ -149,6 +152,7 @@ public TextPoint Format() /// The original text point to expand from. private void Expand(TextPoint point) { + ThreadHelper.ThrowIfNotOnUIThread(); var i = point.CreateEditPoint(); // Look up to find the start of the comment. @@ -183,12 +187,14 @@ private void Expand(TextPoint point) /// private EditPoint Expand(TextPoint point, Action foundAction) { + ThreadHelper.ThrowIfNotOnUIThread(); EditPoint current = point.CreateEditPoint(); EditPoint result = null; string prefix = null; do { + ThreadHelper.ThrowIfNotOnUIThread(); var line = current.Line; var text = current.GetLine(); @@ -242,4 +248,4 @@ private EditPoint Expand(TextPoint point, Action foundAction) #endregion Methods } -} \ No newline at end of file +} diff --git a/CodeMaidShared/Properties/Resources.zh-Hant.resx b/CodeMaidShared/Properties/Resources.zh-Hant.resx new file mode 100644 index 000000000..fcb77a6ac --- /dev/null +++ b/CodeMaidShared/Properties/Resources.zh-Hant.resx @@ -0,0 +1,930 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 無法匯入 + + + 存取層級排序應依 + + + 存取層級 優先於 類型 + + + 在自我關閉標籤中加入空格 + + + 在 XML 標記周圍加入間距 + + + 屬性之後 + + + 開啟大括號後 + + + 警示閾值 + + + 對齊 XML param 標記 + + + 全部 + + + 字母 + + + 按字母排序同一群組成員 + + + 始終將所有 XML 標記拆分為多行 + + + 始終將 summary 標籤分到多行 + + + 您準備好使用 CodeMaid 清理解決方案中的所有程式碼了嗎? + + + 您確定要所有 + + + 詢問 + + + 在檔案底部 + + + 在檔案頂部 + + + 自動偵測 + + + 自動 + + + 在檔案儲存時自動清理 + + + 自動儲存並關閉執行清理時開啟的文件 + + + 儲存時自動清理 - + + + 批次 + + + 關閉角括號前 + + + 關閉大括號前 + + + 在結束標記之前 + + + 在鏈結陳述式之間 + + + 正在建置 + + + 建置進度 + + + 「建置進度」視窗 + + + 快取檔案程式碼模型 + + + 取消 + + + 正在取消... + + + 正在清理 + + + 清理 + + + 檔案類型 + + + 更新 + + + 清理 + + + 清理活動程式碼 + + + 清理活動文件 + + + 清理所有程式碼 + + + 偵錯時無法執行清理。 + + + 清理已開啟的程式碼 + + + 清理已選的程式碼 + + + 清除方案總管搜尋篩選以找出被篩選掉的項目 + + + 關閉 + + + 關閉所有唯讀 + + + CodeMaid 已清理「{0}」。 + + + CodeMaid:清理所有程式碼 + + + CodeMaid:清理外部檔案 + + + 針對「{0}」的 CodeMaid 清理 + + + CodeMaid:清理進度 + + + CodeMaid:確認清理所有程式碼 + + + CodeMaid:確認重設 + + + CodeMaid:確認儲存擱置中的變更 + + + CodeMaid 刪除項目 + + + CodeMaid 未找到非程式碼註解 {0} 以重新格式化。 + + + CodeMaid:匯出 + + + CodeMaid 無法載入主題「{0}」。如需詳細資訊,請參閱輸出視窗。 + + + CodeMaid 已完成此註解的格式化。 + + + CodeMaid 已完成註解 {0} 的格式化。 + + + CodeMaid 格式化註解 + + + CodeMaid 已成功匯出 + + + CodeMaid 已成功從「{0}」匯入 {1}。 + + + CodeMaid 已成功重設 + + + CodeMaid:匯入 + + + CodeMaid 插入區域 + + + CodeMaid 正在清理「{0}」... + + + CodeMaid 連接 + + + CodeMaid 項目上移 + + + CodeMaid 項目下移 + + + CodeMaid 將項目移入 + + + CodeMaid 選項 + + + CodeMaid 移除所有 #region + + + CodeMaid 移除 #region + + + CodeMaid 移除 #region + + + CodeMaid 移除選取的 #region + + + CodeMaid 已整理「{0}」。 + + + CodeMaid 針對「{0}」整理 + + + CodeMaid:整理前置處理器條件 + + + CodeMaid:重設 + + + CodeMaid 排序 + + + CodeMaid Spade 視窗 + + + CodeMaid 已開啟儲存時自動清理 + + + CodeMaid 無法匯出 + + + CodeMaid 無法匯入 + + + CodeMaid 無法重設 + + + 收合所有方案總管 + + + 收合選取的方案總管 + + + 在開啟解決方案時將其收合 + + + 收合 + + + 複雜度 + + + 條件 + + + 設定檔 (*.config)|*.config|所有檔案 (*.*)|*.* + + + {0} 包含目前整理不支援的前置處理器條件 (例如 #if、#pragma)。是否仍要整理 (危險)? + + + 深色 + + + 正在部署 + + + 診斷 + + + 診斷 + + + 診斷模式 + + + 程式碼檢視 + + + Spade + + + 顯示 + + + 是否要執行部分清理? + + + 拖曳可重新排序,拖曳至另一項目上可分組,右鍵點擊可分割,雙擊可重新命名 + + + 空白 + + + 所有其他內容 (例如:.txt、README) + + + 排除 + + + 例如:#endregion 方法 + + + 匯出 + + + 例如:#region 公開方法、#region 私用方法 + + + 外部 + + + XML 註解內部值的額外縮排 + + + 儲存時自動清理 + + + 功能開關 + + + 功能 + + + 檔案頂端 + + + 檔案順序 + + + 尋找 + + + 在方案總管中尋找 + + + 字型 + + + 格式化註解 + + + 格式化 + + + 一般 + + + 處理例外 + + + 在建置停止時隱藏「建置進度」視窗 + + + 圖示 + + + 如果解決方案中只有一個專案,保持展開但子項目仍然收合 + + + 匯入 + + + 包含 + + + 在 #regions 中包含存取層級 + + + 縮排邊界 + + + 插入 + + + 在之後插入空行 + + + 在之前插入空行 + + + 在之間插入空行 + + + 在之前插入空格 + + + 在檔案結尾插入新行標記 (EOF) + + + 插入明確的存取修飾詞 + + + 插入/保留 #regions (即使它們是空白的) + + + 插入新 #regions + + + 所選內容 + + + 不在解決方案中,因此某些清理動作可能無法使用。 + + + 連接多行 + + + 保留 #regions 內的成員 + + + 將 XML 標記放在一起 + + + 淺色 + + + 正在載入... + + + 非同步載入模型 + + + 成員類型順序、分組和慣用名稱 + + + 中繼資料 + + + 其他 + + + 最複雜 + + + 僅名稱 + + + 命名空間 + + + 巡覽 + + + 新增 #region + + + + + + + + + 重設為預設值? + + + 其他 + + + 其他清理命令 (例如:ReSharper_SilentCleanupCode) + + + 效能 + + + 如果檔案不在解決方案中,則執行部分清理 + + + 在存在前置處理器條件時執行整理 + + + 每個命令一行 + + + 每個規則運算式一行 + + + 每個設定一行 + + + 每個 Using 陳述式一行 + + + 在其群組的結尾放置明確介面成員 + + + 預覽 + + + 主要排序應依 + + + 主要排序應為 + + + 進度 + + + 唯讀 + + + 唯讀切換 + + + 正在重建 + + + 正在重新整理... + + + #regions + + + 排除符合下列規則運算式的路徑 (例如:\.resx$ 或 \\lib\\) + + + 移除 + + + 移除全部 #regions(&R) + + + 移除空行 + + + 移除空格 + + + 移除目前 #region(&R) + + + 移除檔案結尾新行標記 (EOF) + + + 移除行尾空白 + + + 移除現有的 #regions + + + 移除多個連續的空行 + + + 移除 #region(s) + + + 移除 #regions + + + 移除已選 #regions(&R) + + + CodeMaid 正在整理「{0}」... + + + 整理活動檔案 + + + 整理活動文件 + + + 整理 + + + 類型 + + + 重設為預設 + + + 在清理過程中格式化註解 + + + 執行「設定文件格式」 + + + 執行 JetBrains ReSharper 清理 + + + 執行「移除並排序 Using」 + + + 在清理開始時執行整理 + + + 執行 Telerik JustCode 清理 + + + 執行 XAML Styler 清理 + + + 儲存 + + + 搜尋 Spade 視窗 + + + 。如需詳細資訊,請參閱輸出視窗。 + + + 選擇子類別以設定選項。 + + + 相關的副檔名 (例如:.cpp、.h) + + + + + + + + + 在 Windows 工作列的 Visual Studio 圖示中顯示建置進度 + + + 在建置開始時顯示「建置進度」視窗 + + + 顯示項目複雜度 (McCabe) + + + 顯示項目中繼資料 + + + 顯示項目類型 + + + 顯示方法參數 + + + 關閉解決方案時顯示起始頁 + + + 自動清理時跳過 + + + 當最後一個字超出註解長度時不強制換行 + + + 方案總管 + + + 解決方案特定設定 + + + 排序 + + + 排序多行 + + + Spade 視窗 + + + Spade 視窗 + + + 分割 + + + 成功 + + + 切換檔案(&W) + + + 切換檔案 + + + 切換 + + + 切換至(&W) + + + 將大綱與程式碼檔案同步 + + + T4 產生的程式碼 + + + 暫時開啟解決方案資料夾以尋找巢狀項目 + + + 主題 + + + 第三方 + + + 此動作無法復原。 + + + 到「{0}」。 + + + 工具視窗 + + + 類型 + + + 類型 優先於 存取層級 + + + 無法匯出 + + + 無法重設 + + + 無法切換唯讀狀態 + + + 在游標下方 + + + 未成功 + + + 將存取子更新為單行或多行 (SA1504) + + + 使用 #region 名稱更新 #endregion 標記 + + + 透過在個別的行上放置大括號來更新單行方法 (SA1502) + + + 使用者設定 + + + 使用復原交易 + + + 當被移除時,重新插入下列 Using 陳述式 (例如:using System;) + + + 視覺 + + + 警告 + + + 警告閾值 + + + 已停止 + + + 當巡覽中心在 + + + 按類型排序時,次要排序應為 + + + 清理時 + + + 空白 + + + 整個項目 + + + Windows 工作列 + + + 註解最大行寬 (到達時強制換行) + + + + + + 您有擱置中的變更,是否要在繼續之前儲存? + + + 僅套用於方法 + + diff --git a/CodeMaidShared/UI/Converters/CodeItemToMetadataStringConverter.cs b/CodeMaidShared/UI/Converters/CodeItemToMetadataStringConverter.cs index 1001e5aa1..1fff586f0 100644 --- a/CodeMaidShared/UI/Converters/CodeItemToMetadataStringConverter.cs +++ b/CodeMaidShared/UI/Converters/CodeItemToMetadataStringConverter.cs @@ -5,6 +5,7 @@ using System.Globalization; using System.Linq; using System.Windows.Data; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.UI.Converters { @@ -48,6 +49,7 @@ public class CodeItemToMetadataStringConverter : IValueConverter /// A converted value. If the method returns null, the valid null value is used. public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { + ThreadHelper.ThrowIfNotOnUIThread(); var codeItem = value as BaseCodeItemElement; if (codeItem == null) return string.Empty; @@ -181,6 +183,7 @@ private IEnumerable GenerateMetadataStrings(CodeItemMethod method) /// The metadata strings. private IEnumerable GenerateMetadataStrings(CodeItemProperty property) { + ThreadHelper.ThrowIfNotOnUIThread(); var strings = new List(); var methodStrings = new List(); @@ -207,4 +210,4 @@ private IEnumerable GenerateMetadataStrings(CodeItemProperty property) #endregion Methods } -} \ No newline at end of file +} diff --git a/CodeMaidShared/UI/Converters/NameParametersToTextBlockConverter.cs b/CodeMaidShared/UI/Converters/NameParametersToTextBlockConverter.cs index abd03d068..4a927a4f1 100644 --- a/CodeMaidShared/UI/Converters/NameParametersToTextBlockConverter.cs +++ b/CodeMaidShared/UI/Converters/NameParametersToTextBlockConverter.cs @@ -1,4 +1,4 @@ -using SteveCadwallader.CodeMaid.Helpers; +using SteveCadwallader.CodeMaid.Helpers; using SteveCadwallader.CodeMaid.Model.CodeItems; using SteveCadwallader.CodeMaid.Properties; using System; @@ -8,6 +8,7 @@ using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.UI.Converters { @@ -80,6 +81,7 @@ public NameParametersToTextBlockConverter() /// A converted value. If the method returns null, the valid null value is used. public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { + ThreadHelper.ThrowIfNotOnUIThread(); if (!(values[0] is ICodeItem codeItem)) { return null; @@ -175,6 +177,7 @@ private IEnumerable CreateInlinesForName(string text, string textToHighl /// The inlines representing the parameters. private IEnumerable CreateInlinesForParameters(ICodeItemParameters codeItem) { + ThreadHelper.ThrowIfNotOnUIThread(); var inlines = new List(); var opener = GetOpeningString(codeItem); @@ -335,4 +338,4 @@ private static string GetClosingString(ICodeItemParameters codeItem) #endregion Methods } -} \ No newline at end of file +} diff --git a/CodeMaidShared/UI/Dialogs/Options/Formatting/FormattingViewModel.cs b/CodeMaidShared/UI/Dialogs/Options/Formatting/FormattingViewModel.cs index 34764fa5c..e4b23207a 100644 --- a/CodeMaidShared/UI/Dialogs/Options/Formatting/FormattingViewModel.cs +++ b/CodeMaidShared/UI/Dialogs/Options/Formatting/FormattingViewModel.cs @@ -2,6 +2,7 @@ using SteveCadwallader.CodeMaid.Model.Comments.Options; using SteveCadwallader.CodeMaid.Properties; using System.Windows.Media; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.UI.Dialogs.Options.Formatting { @@ -34,6 +35,7 @@ public class FormattingViewModel : OptionsPageViewModel public FormattingViewModel(CodeMaidPackage package, Settings activeSettings) : base(package, activeSettings) { + ThreadHelper.ThrowIfNotOnUIThread(); Mappings = new SettingsToOptionsList(ActiveSettings, this) { new SettingToOptionMapping(x => ActiveSettings.Formatting_CommentRunDuringCleanup, x => CommentRunDuringCleanup), @@ -205,6 +207,7 @@ public Brush CommentPreviewTextBackground { get { + ThreadHelper.ThrowIfNotOnUIThread(); var color = System.Drawing.ColorTranslator.FromOle((int)_commentColors.Background); return new SolidColorBrush(Color.FromArgb(color.A, color.R, color.G, color.B)); @@ -233,4 +236,4 @@ private void UpdatePreviewText() #endregion Preview Text and Helpers } -} \ No newline at end of file +} diff --git a/CodeMaidShared/UI/Dialogs/Options/OptionsViewModel.cs b/CodeMaidShared/UI/Dialogs/Options/OptionsViewModel.cs index 0019f6ac7..f2d2946d5 100644 --- a/CodeMaidShared/UI/Dialogs/Options/OptionsViewModel.cs +++ b/CodeMaidShared/UI/Dialogs/Options/OptionsViewModel.cs @@ -17,6 +17,7 @@ using System.IO; using System.Linq; using System.Windows; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.UI.Dialogs.Options { @@ -40,6 +41,7 @@ public class OptionsViewModel : Bindable /// The type of the initially selected page. public OptionsViewModel(CodeMaidPackage package, Type initiallySelectedPageType = null) { + ThreadHelper.ThrowIfNotOnUIThread(); _settingsContextHelper = SettingsContextHelper.GetInstance(package); ActiveSettings = (Settings)SettingsBase.Synchronized(new Settings()); @@ -375,6 +377,7 @@ private bool OnSaveCommandCanExecute(object parameter) /// The command parameter. private void OnSaveCommandExecuted(object parameter) { + ThreadHelper.ThrowIfNotOnUIThread(); Save(); DialogResult = true; } @@ -384,6 +387,7 @@ private void OnSaveCommandExecuted(object parameter) /// private void Save() { + ThreadHelper.ThrowIfNotOnUIThread(); foreach (var optionsPageViewModel in Pages.Flatten()) { optionsPageViewModel.SaveSettings(); @@ -443,6 +447,7 @@ private bool OnSwitchSettingsCommandCanExecute(object parameter) /// The command parameter. private void OnSwitchSettingsCommandExecuted(object parameter) { + ThreadHelper.ThrowIfNotOnUIThread(); if (CheckToSavePendingChangesShouldCancelOperation()) { return; @@ -473,6 +478,7 @@ private void OnSwitchSettingsCommandExecuted(object parameter) /// True if the operation should be canceled, otherwise false. private bool CheckToSavePendingChangesShouldCancelOperation() { + ThreadHelper.ThrowIfNotOnUIThread(); if (HasChanges) { var result = MessageBox.Show(Resources.YouHavePendingChangesDoYouWantToSaveThemBeforeContinuing, @@ -524,6 +530,7 @@ private void OnPagePropertyChanged(object sender, PropertyChangedEventArgs e) /// private void RefreshPackageSettings() { + ThreadHelper.ThrowIfNotOnUIThread(); // Explicitly try to reload solution-specific settings in case they were newly created. if (!_settingsContextHelper.LoadSolutionSpecificSettings(Settings.Default)) { @@ -548,4 +555,4 @@ private void ReloadPagesFromSettings() #endregion Methods } -} \ No newline at end of file +} diff --git a/CodeMaidShared/UI/EditableTextBlock.xaml.cs b/CodeMaidShared/UI/EditableTextBlock.xaml.cs index 1154983a2..243fb3c86 100644 --- a/CodeMaidShared/UI/EditableTextBlock.xaml.cs +++ b/CodeMaidShared/UI/EditableTextBlock.xaml.cs @@ -1,4 +1,6 @@ -using System; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Threading; +using System; using System.Reflection; using System.Windows; using System.Windows.Input; @@ -67,11 +69,12 @@ private static void OnIsEditingChanged(DependencyObject obj, DependencyPropertyC editableTextBlock._originalValue = editableTextBlock.Text; // Focus and select all of the text. - editableTextBlock.Dispatcher.BeginInvoke(DispatcherPriority.Input, new Action(() => + ThreadHelper.JoinableTaskFactory.Run(async () => { + await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); editableTextBlock.TextBox.Focus(); editableTextBlock.TextBox.SelectAll(); - })); + }); } } diff --git a/CodeMaidShared/UI/ThemeManager.cs b/CodeMaidShared/UI/ThemeManager.cs index 0057b5e53..09d2b6fdc 100644 --- a/CodeMaidShared/UI/ThemeManager.cs +++ b/CodeMaidShared/UI/ThemeManager.cs @@ -1,4 +1,4 @@ -using EnvDTE80; +using EnvDTE80; using SteveCadwallader.CodeMaid.Helpers; using SteveCadwallader.CodeMaid.Properties; using SteveCadwallader.CodeMaid.UI.Enumerations; @@ -7,6 +7,7 @@ using System.Linq; using System.Windows; using System.Windows.Media; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.UI { @@ -111,6 +112,7 @@ public void ApplyTheme() /// The resolved theme. private ThemeMode ResolveActiveTheme() { + ThreadHelper.ThrowIfNotOnUIThread(); var theme = (ThemeMode)Settings.Default.General_Theme; return theme == ThemeMode.AutoDetect ? AutoDetectTheme() : theme; @@ -121,6 +123,7 @@ private ThemeMode ResolveActiveTheme() /// private ThemeMode AutoDetectTheme() { + ThreadHelper.ThrowIfNotOnUIThread(); const int medianColor = 128 * 3; var bgColor = GetColorFromUInt(_package.IDE.GetThemeColor(vsThemeColors.vsThemeColorToolWindowBackground)); @@ -146,6 +149,7 @@ private static Color GetColorFromUInt(uint number) /// The theme to apply. private void ApplyThemeToElement(FrameworkElement element, ThemeMode theme) { + ThreadHelper.ThrowIfNotOnUIThread(); if (element == null) return; if (element.Resources == null) @@ -184,6 +188,7 @@ private void ApplyThemeToElement(FrameworkElement element, ThemeMode theme) /// The loaded resource dictionary, otherwise null. private ResourceDictionary LoadResourceDictionary(Uri themeUri) { + ThreadHelper.ThrowIfNotOnUIThread(); try { var dictionary = (ResourceDictionary)Application.LoadComponent(themeUri); @@ -202,4 +207,4 @@ private ResourceDictionary LoadResourceDictionary(Uri themeUri) #endregion Methods } -} \ No newline at end of file +} diff --git a/CodeMaidShared/UI/ToolWindows/BuildProgress/BuildProgressToolWindow.cs b/CodeMaidShared/UI/ToolWindows/BuildProgress/BuildProgressToolWindow.cs index 875c9eeda..64fd67e55 100644 --- a/CodeMaidShared/UI/ToolWindows/BuildProgress/BuildProgressToolWindow.cs +++ b/CodeMaidShared/UI/ToolWindows/BuildProgress/BuildProgressToolWindow.cs @@ -91,6 +91,7 @@ private double ProgressPercentage public void Close() { + ThreadHelper.ThrowIfNotOnUIThread(); (Frame as IVsWindowFrame).CloseFrame((uint)__FRAMECLOSE.FRAMECLOSE_NoSave); } @@ -115,6 +116,7 @@ public override void OnToolWindowCreated() /// The action. internal void NotifyBuildBegin(vsBuildScope scope, vsBuildAction action) { + ThreadHelper.ThrowIfNotOnUIThread(); BuildAction = action; BuildScope = scope; BuildingProjects = new List(); @@ -243,6 +245,7 @@ private static string GetBuildTypeString(vsBuildScope buildScope, vsBuildAction /// private int GetNumberOfProjectsToBeBuilt() { + ThreadHelper.ThrowIfNotOnUIThread(); var solutionContexts = Package.IDE.Solution.SolutionBuild.ActiveConfiguration.SolutionContexts; int count = 0; @@ -286,4 +289,4 @@ private string GetToolWindowCaption() return $"{DefaultCaption}{progressString}: {buildString} {string.Join(", ", projectNames)}..."; } } -} \ No newline at end of file +} diff --git a/CodeMaidShared/UI/ToolWindows/BuildProgress/BuildProgressViewModel.cs b/CodeMaidShared/UI/ToolWindows/BuildProgress/BuildProgressViewModel.cs index b3bd1539d..6678fd88c 100644 --- a/CodeMaidShared/UI/ToolWindows/BuildProgress/BuildProgressViewModel.cs +++ b/CodeMaidShared/UI/ToolWindows/BuildProgress/BuildProgressViewModel.cs @@ -2,6 +2,7 @@ using System; using System.Windows; using System.Windows.Shell; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.UI.ToolWindows.BuildProgress { @@ -138,6 +139,7 @@ private bool OnCancelBuildCommandCanExecute(object parameter) /// The command parameter. private void OnCancelBuildCommandExecuted(object parameter) { + ThreadHelper.ThrowIfNotOnUIThread(); try { if (Package != null) @@ -185,4 +187,4 @@ private void UpdateTaskbarStatus() #endregion Methods } -} \ No newline at end of file +} diff --git a/CodeMaidShared/UI/ToolWindows/Spade/MemberSearchTask.cs b/CodeMaidShared/UI/ToolWindows/Spade/MemberSearchTask.cs index 028dd81da..d44fca5bc 100644 --- a/CodeMaidShared/UI/ToolWindows/Spade/MemberSearchTask.cs +++ b/CodeMaidShared/UI/ToolWindows/Spade/MemberSearchTask.cs @@ -1,4 +1,4 @@ -using Microsoft.VisualStudio; +using Microsoft.VisualStudio; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; using System; @@ -26,6 +26,7 @@ public MemberSearchTask(uint dwCookie, IVsSearchQuery pSearchQuery, IVsSearchCal /// protected override void OnStartSearch() { + ThreadHelper.ThrowIfNotOnUIThread(); ErrorCode = VSConstants.S_OK; try @@ -40,4 +41,4 @@ protected override void OnStartSearch() base.OnStartSearch(); } } -} \ No newline at end of file +} diff --git a/CodeMaidShared/UI/ToolWindows/Spade/SpadeToolWindow.cs b/CodeMaidShared/UI/ToolWindows/Spade/SpadeToolWindow.cs index 9b69fc7f8..5d15969a4 100644 --- a/CodeMaidShared/UI/ToolWindows/Spade/SpadeToolWindow.cs +++ b/CodeMaidShared/UI/ToolWindows/Spade/SpadeToolWindow.cs @@ -4,6 +4,7 @@ using Microsoft.VisualStudio.Imaging.Interop; using Microsoft.VisualStudio.PlatformUI; using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Threading; using Microsoft.VisualStudio.Shell.Interop; using SteveCadwallader.CodeMaid.Model; using SteveCadwallader.CodeMaid.Model.CodeItems; @@ -39,6 +40,7 @@ public sealed class SpadeToolWindow : ToolWindowPane, IVsWindowFrameNotify3 public SpadeToolWindow() : base(null) { + ThreadHelper.ThrowIfNotOnUIThread(); // Set the tool window caption. Caption = Resources.CodeMaidSpade; @@ -138,6 +140,7 @@ public override void ClearSearch() public void Close() { + ThreadHelper.ThrowIfNotOnUIThread(); (Frame as IVsWindowFrame).CloseFrame((uint)__FRAMECLOSE.FRAMECLOSE_NoSave); } @@ -159,6 +162,7 @@ public void NotifyActiveDocument(Document document) /// The document. public void NotifyDocumentSave(Document document) { + ThreadHelper.ThrowIfNotOnUIThread(); if (Document == document) { // Refresh the document if active. @@ -174,6 +178,7 @@ public void NotifyDocumentSave(Document document) public int OnShow(int fShow) { + ThreadHelper.ThrowIfNotOnUIThread(); // Track the visibility of this tool window. switch ((__FRAMESHOW)fShow) { @@ -199,6 +204,7 @@ public int OnShow(int fShow) /// public override void OnToolWindowCreated() { + ThreadHelper.ThrowIfNotOnUIThread(); base.OnToolWindowCreated(); // Register for events to this window. @@ -210,7 +216,7 @@ public override void OnToolWindowCreated() // Get an instance of the code model manager. _codeModelManager = CodeModelManager.GetInstance(Package); - Package.JoinableTaskFactory.RunAsync(async () => + _ = Package.JoinableTaskFactory.RunAsync(async () => await Package.SettingsMonitor.WatchAsync(s => s.Feature_SpadeToolWindow, on => { if (on) @@ -245,13 +251,18 @@ await Package.SettingsMonitor.WatchAsync(s => s.Feature_SpadeToolWindow, on => { _viewModel.Dispatcher = spadeContent.Dispatcher; - spadeContent.Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(Package.ThemeManager.ApplyTheme)); + _ = Package.JoinableTaskFactory.RunAsync(async () => + { + await Package.JoinableTaskFactory.SwitchToMainThreadAsync(); + Package.ThemeManager.ApplyTheme(); + }); } } } public override void ProvideSearchSettings(IVsUIDataSource pSearchSettings) { + ThreadHelper.ThrowIfNotOnUIThread(); base.ProvideSearchSettings(pSearchSettings); Utilities.SetValue(pSearchSettings, SearchSettingsDataSource.PropertyNames.ControlMinWidth, 200U); @@ -264,6 +275,7 @@ public override void ProvideSearchSettings(IVsUIDataSource pSearchSettings) /// public void Refresh() { + ThreadHelper.ThrowIfNotOnUIThread(); Package?.ThemeManager.ApplyTheme(); ConditionallyUpdateCodeModel(true); @@ -275,6 +287,7 @@ public void Refresh() /// True if refreshing a document, otherwise false. private void ConditionallyUpdateCodeModel(bool isRefresh) { + ThreadHelper.ThrowIfNotOnUIThread(); if (!IsVisible) return; _viewModel.Document = Document; @@ -298,7 +311,7 @@ private void ConditionallyUpdateCodeModel(bool isRefresh) _viewModel.IsLoading = true; } - var codeItems = _codeModelManager.RetrieveAllCodeItemsAsync(Document, true); + var codeItems = _codeModelManager.BeginRetrieveAllCodeItems(Document, true); if (codeItems != null) { UpdateViewModelRawCodeItems(codeItems); @@ -314,6 +327,7 @@ private void ConditionallyUpdateCodeModel(bool isRefresh) /// The code model. private void OnCodeModelBuilt(CodeModel codeModel) { + ThreadHelper.ThrowIfNotOnUIThread(); if (Document == codeModel.Document) { UpdateViewModelRawCodeItems(codeModel.CodeItems); @@ -348,4 +362,4 @@ private void UpdateViewModelRawCodeItems(SetCodeItems codeItems) _viewModel.IsRefreshing = false; } } -} \ No newline at end of file +} diff --git a/CodeMaidShared/UI/ToolWindows/Spade/SpadeView.xaml.cs b/CodeMaidShared/UI/ToolWindows/Spade/SpadeView.xaml.cs index cc2debe43..00dce5119 100644 --- a/CodeMaidShared/UI/ToolWindows/Spade/SpadeView.xaml.cs +++ b/CodeMaidShared/UI/ToolWindows/Spade/SpadeView.xaml.cs @@ -1,4 +1,5 @@ using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Threading; using SteveCadwallader.CodeMaid.Helpers; using SteveCadwallader.CodeMaid.Logic.Reorganizing; using SteveCadwallader.CodeMaid.Model.CodeItems; @@ -152,6 +153,7 @@ private void OnPreviewMouseWheel(object sender, MouseWheelEventArgs e) /// private void OnTreeViewItemKeyDown(object sender, KeyEventArgs e) { + ThreadHelper.ThrowIfNotOnUIThread(); var treeViewItem = e.Source as TreeViewItem; if (treeViewItem == null || Keyboard.Modifiers != ModifierKeys.None) return; @@ -252,6 +254,7 @@ private void OnTreeViewItemHeaderMouseMove(object sender, MouseEventArgs e) /// private void OnTreeViewItemHeaderMouseUp(object sender, MouseButtonEventArgs e) { + ThreadHelper.ThrowIfNotOnUIThread(); _dragCandidate = null; _dragStartPoint = null; @@ -364,6 +367,7 @@ private void OnTreeViewItemHeaderDragLeave(object sender, DragEventArgs e) /// private void OnTreeViewItemHeaderDrop(object sender, DragEventArgs e) { + ThreadHelper.ThrowIfNotOnUIThread(); if (!e.Data.GetDataPresent(typeof(IList))) return; var treeViewItem = FindParentTreeViewItem(sender); @@ -528,8 +532,11 @@ private void JumpToCodeItem(BaseCodeItem codeItem) var viewModel = ViewModel; if (codeItem == null || viewModel == null || codeItem.StartOffset <= 0) return; - Dispatcher.BeginInvoke( - new Action(() => TextDocumentHelper.MoveToCodeItem(viewModel.Document, codeItem, Settings.Default.Digging_CenterOnWhole))); + _ = viewModel.Package?.JoinableTaskFactory.RunAsync(async () => + { + await viewModel.Package.JoinableTaskFactory.SwitchToMainThreadAsync(); + TextDocumentHelper.MoveToCodeItem(viewModel.Document, codeItem, Settings.Default.Digging_CenterOnWhole); + }); } /// @@ -549,8 +556,11 @@ private void SelectCodeItem(BaseCodeItem codeItem) var viewModel = ViewModel; if (codeItem == null || viewModel == null || codeItem.StartOffset <= 0) return; - Dispatcher.BeginInvoke( - new Action(() => TextDocumentHelper.SelectCodeItem(viewModel.Document, codeItem))); + _ = viewModel.Package?.JoinableTaskFactory.RunAsync(async () => + { + await viewModel.Package.JoinableTaskFactory.SwitchToMainThreadAsync(); + TextDocumentHelper.SelectCodeItem(viewModel.Document, codeItem); + }); } /// @@ -561,7 +571,7 @@ private void ShowContextMenu(Point point) { if (ViewModel?.Package is var package) { - package.JoinableTaskFactory.RunAsync(async () => + _ = package.JoinableTaskFactory.RunAsync(async () => { if (await package.GetServiceAsync(typeof(IMenuCommandService)) is OleMenuCommandService menuCommandService) { @@ -575,4 +585,4 @@ private void ShowContextMenu(Point point) #endregion Methods } -} \ No newline at end of file +} diff --git a/CodeMaidShared/UI/ToolWindows/Spade/SpadeViewModel.cs b/CodeMaidShared/UI/ToolWindows/Spade/SpadeViewModel.cs index 6894395cf..cad74354c 100644 --- a/CodeMaidShared/UI/ToolWindows/Spade/SpadeViewModel.cs +++ b/CodeMaidShared/UI/ToolWindows/Spade/SpadeViewModel.cs @@ -1,4 +1,5 @@ using EnvDTE; +using Microsoft.VisualStudio.Threading; using SteveCadwallader.CodeMaid.Logic.Digging; using SteveCadwallader.CodeMaid.Model.CodeItems; using SteveCadwallader.CodeMaid.Model.CodeTree; @@ -6,6 +7,7 @@ using System; using System.Collections.Generic; using System.Windows.Threading; +using Microsoft.VisualStudio.Shell; namespace SteveCadwallader.CodeMaid.UI.ToolWindows.Spade { @@ -180,7 +182,7 @@ public void RequestRefresh() /// private void RequestUpdatedOrganizedCodeItems() { - _codeTreeBuilderAsync.RetrieveCodeTreeAsync(new CodeTreeRequest(Document, RawCodeItems, SortOrder, NameFilter)); + _codeTreeBuilderAsync.BeginRetrieveCodeTree(new CodeTreeRequest(Document, RawCodeItems, SortOrder, NameFilter)); } /// @@ -189,9 +191,13 @@ private void RequestUpdatedOrganizedCodeItems() /// The code items snapshot. private void UpdateOrganizedCodeItems(SnapshotCodeItems snapshot) { - if (Document == snapshot.Document) + if (Document == snapshot.Document && Package != null) { - Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() => OrganizedCodeItems = snapshot.CodeItems)); + _ = Package.JoinableTaskFactory.RunAsync(async () => + { + await Package.JoinableTaskFactory.SwitchToMainThreadAsync(); + OrganizedCodeItems = snapshot.CodeItems; + }); } } @@ -200,6 +206,7 @@ private void UpdateOrganizedCodeItems(SnapshotCodeItems snapshot) /// private void UpdateOutliningSynchronization() { + ThreadHelper.ThrowIfNotOnUIThread(); if (Settings.Default.Digging_SynchronizeOutlining) { if (_outliningSynchronizationManager == null) @@ -219,4 +226,4 @@ private void UpdateOutliningSynchronization() #endregion Methods } -} \ No newline at end of file +} diff --git a/appveyor.yml b/appveyor.yml index 5892559a4..79bdd7b7a 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -20,7 +20,7 @@ build_script: - msbuild /p:configuration=Release /p:DeployExtension=false /p:ZipPackageCompressionLevel=normal /v:m test: - assemblies: SteveCadwallader.CodeMaid.UnitTests.dll + assemblies: SteveCadwallader.CodeMaid.UnitTests.exe after_test: - ps: if ($env:APPVEYOR_REPO_BRANCH -eq 'dev' -or $env:APPVEYOR_REPO_BRANCH -eq 'release') { Vsix-PushArtifacts | Vsix-PublishToGallery }