diff --git a/DPackRx.Tests/DPackRx.Tests.csproj b/DPackRx.Tests/DPackRx.Tests.csproj index 5e7e87a..6029e08 100644 --- a/DPackRx.Tests/DPackRx.Tests.csproj +++ b/DPackRx.Tests/DPackRx.Tests.csproj @@ -52,9 +52,11 @@ + + @@ -93,10 +95,10 @@ 13.0.1 - 3.13.2 + 3.13.3 - 4.1.0 + 4.3.1 17.0.31902.203 diff --git a/DPackRx.Tests/Features/SurroundWithFeatureTests.cs b/DPackRx.Tests/Features/SurroundWithFeatureTests.cs index 09ea410..2a97089 100644 --- a/DPackRx.Tests/Features/SurroundWithFeatureTests.cs +++ b/DPackRx.Tests/Features/SurroundWithFeatureTests.cs @@ -1,8 +1,4 @@ using System; -using System.Windows.Input; - -using Moq; -using NUnit.Framework; using DPackRx.CodeModel; using DPackRx.Features; @@ -12,6 +8,10 @@ using DPackRx.Package; using DPackRx.Services; +using Moq; + +using NUnit.Framework; + namespace DPackRx.Tests.Features { /// @@ -25,10 +25,9 @@ public class SurroundWithFeatureTests private Mock _serviceProviderMock; private Mock _logMock; private Mock _optionsServiceMock; - private Mock _shellHelperServiceMock; private Mock _shellSelectionServiceMock; private Mock _fileTypeResolverMock; - private Mock _keyboardServiceMock; + private Mock _surroundWithFormatterServiceMock; private LanguageSettings _settings; #endregion @@ -45,9 +44,6 @@ public void Setup() _optionsServiceMock = new Mock(); - _shellHelperServiceMock = new Mock(); - _shellHelperServiceMock.Setup(s => s.ExecuteCommand(It.IsNotNull(), null)).Verifiable(); - _shellSelectionServiceMock = new Mock(); _shellSelectionServiceMock.Setup(s => s.IsContextActive(It.IsAny())).Returns(true).Verifiable(); _shellSelectionServiceMock.Setup(s => s.GetActiveProject()).Returns(new object()).Verifiable(); @@ -57,9 +53,8 @@ public void Setup() _fileTypeResolverMock = new Mock(); _fileTypeResolverMock.Setup(f => f.GetCurrentLanguage(It.IsAny(), out webProject)).Returns(_settings).Verifiable(); - _keyboardServiceMock = new Mock(); - _keyboardServiceMock.Setup(k => k.Type(It.IsAny())).Verifiable(); - _keyboardServiceMock.Setup(k => k.Type(It.IsAny())).Verifiable(); + _surroundWithFormatterServiceMock = new Mock(); + _surroundWithFormatterServiceMock.Setup(s => s.Format(It.IsAny())).Verifiable(); } [TearDown] @@ -68,10 +63,9 @@ public void TearDown() _serviceProviderMock = null; _logMock = null; _optionsServiceMock = null; - _shellHelperServiceMock = null; _shellSelectionServiceMock = null; _fileTypeResolverMock = null; - _keyboardServiceMock = null; + _surroundWithFormatterServiceMock = null; _settings = null; } @@ -85,8 +79,7 @@ public void TearDown() private IFeature GetFeature() { return new SurroundWithFeature(_serviceProviderMock.Object, _logMock.Object, _optionsServiceMock.Object, - _shellHelperServiceMock.Object, _shellSelectionServiceMock.Object, _fileTypeResolverMock.Object, - _keyboardServiceMock.Object); + _shellSelectionServiceMock.Object, _fileTypeResolverMock.Object, _surroundWithFormatterServiceMock.Object); } #endregion @@ -160,12 +153,12 @@ public void IsValidContext_UnsupportedLanguage() _shellSelectionServiceMock.Verify(s => s.IsContextActive(It.IsAny()), Times.Never); } - [TestCase(CommandIDs.SW_TRY_CATCH, SurroundWithFeature.SNIPPET_TRY_CATCH)] - [TestCase(CommandIDs.SW_TRY_FINALLY, SurroundWithFeature.SNIPPET_TRY_FINALLY)] - [TestCase(CommandIDs.SW_FOR, SurroundWithFeature.SNIPPET_FOR)] - [TestCase(CommandIDs.SW_FOR_EACH, SurroundWithFeature.SNIPPET_FOR_EACH)] - [TestCase(CommandIDs.SW_REGION, SurroundWithFeature.SNIPPET_REGION)] - public void Execute(int commandId, string command) + [TestCase(CommandIDs.SW_TRY_CATCH)] + [TestCase(CommandIDs.SW_TRY_FINALLY)] + [TestCase(CommandIDs.SW_FOR)] + [TestCase(CommandIDs.SW_FOR_EACH)] + [TestCase(CommandIDs.SW_REGION)] + public void Execute(int commandId) { var feature = GetFeature(); var webProject = false; @@ -175,30 +168,23 @@ public void Execute(int commandId, string command) Assert.That(result, Is.True); _shellSelectionServiceMock.Verify(s => s.GetActiveProject(), Times.Once); _fileTypeResolverMock.Verify(f => f.GetCurrentLanguage(It.IsAny(), out webProject), Times.Once); - _shellHelperServiceMock.Verify(s => s.ExecuteCommand(SurroundWithFeature.SURROUND_WITH_COMMAND, null), Times.Once); - _shellHelperServiceMock.Verify(s => s.ExecuteCommand(SurroundWithFeature.ISERT_SNIPPET_COMMAND, null), Times.Never); - _keyboardServiceMock.Verify(k => k.Type(Key.Enter), Times.Once); - _keyboardServiceMock.Verify(k => k.Type(command), Times.Once); + _surroundWithFormatterServiceMock.Verify(s => s.Format(It.IsNotNull()), Times.Once); } - [TestCase("test")] - [TestCase("")] - [TestCase(null)] - public void Execute_LanguageName(string surroundWithLanguageName) + [Test] + public void Execute_UnsupportedLanguage() { var feature = GetFeature(); var webProject = false; - _settings.SurroundWithLanguageName = surroundWithLanguageName; + _settings = new LanguageSettings("Xml", "Xml") { Type = LanguageType.Xml, SurroundWith = true }; + _fileTypeResolverMock.Setup(f => f.GetCurrentLanguage(It.IsAny(), out webProject)).Returns(_settings).Verifiable(); var result = feature.Execute(CommandIDs.SW_REGION); - Assert.That(result, Is.True); + Assert.That(result, Is.False); _shellSelectionServiceMock.Verify(s => s.GetActiveProject(), Times.Once); _fileTypeResolverMock.Verify(f => f.GetCurrentLanguage(It.IsAny(), out webProject), Times.Once); - _shellHelperServiceMock.Verify(s => s.ExecuteCommand(SurroundWithFeature.SURROUND_WITH_COMMAND, null), Times.Exactly(string.IsNullOrEmpty(surroundWithLanguageName) ? 1 : 0)); - _shellHelperServiceMock.Verify(s => s.ExecuteCommand(SurroundWithFeature.ISERT_SNIPPET_COMMAND, null), Times.Exactly(string.IsNullOrEmpty(surroundWithLanguageName) ? 0 : 1)); - _keyboardServiceMock.Verify(k => k.Type(Key.Enter), Times.Exactly(string.IsNullOrEmpty(surroundWithLanguageName) ? 1 : 2)); - _keyboardServiceMock.Verify(k => k.Type(SurroundWithFeature.SNIPPET_REGION), Times.Once); + _surroundWithFormatterServiceMock.Verify(s => s.Format(It.IsAny()), Times.Never); } [Test] diff --git a/DPackRx.Tests/Features/SurroundWithModelTests.cs b/DPackRx.Tests/Features/SurroundWithModelTests.cs new file mode 100644 index 0000000..862c7d6 --- /dev/null +++ b/DPackRx.Tests/Features/SurroundWithModelTests.cs @@ -0,0 +1,45 @@ +using DPackRx.Features.SurroundWith; + +using NUnit.Framework; + +namespace DPackRx.Tests.Features +{ + /// + /// SurroundWithModel tests. + /// + [TestFixture] + public class SurroundWithModelTests + { + #region Tests + + [Test] + public void SurroundWithModel() + { + var model = new SurroundWithModel(); + + Assert.That(model.Models, Is.Not.Null); + Assert.That(model.Models.Count, Is.Zero); + } + + [Test] + public void SurroundWithLanguageModel() + { + var model = new SurroundWithLanguageModel + { + Language = SurroundWithLanguage.CSharp, + Type = SurroundWithType.ForEach, + StartingCode = "test1", + EndingCode = "test2", + WordOffset = 123 + }; + + Assert.That(model.Language, Is.EqualTo(SurroundWithLanguage.CSharp)); + Assert.That(model.Type, Is.EqualTo(SurroundWithType.ForEach)); + Assert.That(model.StartingCode, Is.EqualTo("test1")); + Assert.That(model.EndingCode, Is.EqualTo("test2")); + Assert.That(model.WordOffset, Is.EqualTo(123)); + } + + #endregion + } +} \ No newline at end of file diff --git a/DPackRx.Tests/Language/LanguageSettingsTests.cs b/DPackRx.Tests/Language/LanguageSettingsTests.cs new file mode 100644 index 0000000..aa6e5bd --- /dev/null +++ b/DPackRx.Tests/Language/LanguageSettingsTests.cs @@ -0,0 +1,73 @@ +using System.Collections.Generic; + +using DPackRx.Language; +using DPackRx.Package; + +using NUnit.Framework; + +namespace DPackRx.Tests.Language +{ + /// + /// LanguageSettings tests. + /// + [TestFixture] + public class LanguageSettingsTests + { + #region Tests + + [TestCase(LanguageConsts.VS_CM_LANGUAGE_CSHARP, LanguageType.CSharp)] + [TestCase(LanguageConsts.VS_CM_LANGUAGE_VB, LanguageType.VB)] + [TestCase(LanguageConsts.VS_CM_LANGUAGE_VC, LanguageType.CPP)] + [TestCase(LanguageConsts.VS_LANGUAGE_JAVA_SCRIPT, LanguageType.JavaScript)] + [TestCase(LanguageConsts.VS_LANGUAGE_XML, LanguageType.Xml)] + [TestCase(LanguageConsts.VS_LANGUAGE_SOLUTION_ITEMS, LanguageType.SolutionItems)] + [TestCase(LanguageConsts.VS_LANGUAGE_SQL, LanguageType.Sql)] + [TestCase("bad", LanguageType.Unknown)] + public void GetLanguage(string projectLanguage, LanguageType languageType) + { + var language = new LanguageSettings(projectLanguage, "Test") + { + CheckDuplicateNames = true, + Comments = new Dictionary { { "test", true } }, + DesignerFiles = LanguageDesignerFiles.FullySupported, + Extensions = new Dictionary { { ".txt", true } }, + IgnoreCodeType = true, + Imports = LanguageImports.Supported, + ParentlessFullName = true, + ProjectGuid = "test", + SmartFormat = true, + SupportsCompileBuildAction = true, + SupportsGenerics = true, + SupportsStatistics = true, + SurroundWith = true, + WebLanguage = "test", + WebNames = new string[] { "test" }, + XmlDocs = new string[] { "test" }, + XmlDocSurround = true, + }; + + Assert.That(language.Language, Is.EqualTo(projectLanguage)); + Assert.That(language.FriendlyName, Is.EqualTo("Test")); + Assert.That(language.Type, Is.EqualTo(languageType)); + Assert.That(language.CheckDuplicateNames, Is.True); + Assert.That(language.Comments, Is.Not.Null.And.Count.EqualTo(1)); + Assert.That(language.DesignerFiles, Is.EqualTo(LanguageDesignerFiles.FullySupported)); + Assert.That(language.Extensions, Is.Not.Null.And.Count.EqualTo(1)); + Assert.That(language.IgnoreCodeType, Is.True); + Assert.That(language.Imports, Is.EqualTo(LanguageImports.Supported)); + Assert.That(language.ParentlessFullName, Is.True); + Assert.That(language.ProjectGuid, Is.EqualTo("test")); + Assert.That(language.SmartFormat, Is.True); + Assert.That(language.SupportsCompileBuildAction, Is.True); + Assert.That(language.SupportsGenerics, Is.True); + Assert.That(language.SupportsStatistics, Is.True); + Assert.That(language.SurroundWith, Is.True); + Assert.That(language.WebLanguage, Is.EqualTo("test")); + Assert.That(language.WebNames, Is.Not.Null.And.Length.EqualTo(1)); + Assert.That(language.XmlDocs, Is.Not.Null.And.Length.EqualTo(1)); + Assert.That(language.XmlDocSurround, Is.True); + } + + #endregion + } +} \ No newline at end of file diff --git a/DPackRx/DPackRx.csproj b/DPackRx/DPackRx.csproj index 02dbc15..14d85d4 100644 --- a/DPackRx/DPackRx.csproj +++ b/DPackRx/DPackRx.csproj @@ -75,7 +75,7 @@ - + @@ -85,7 +85,8 @@ - + + @@ -144,7 +145,6 @@ - @@ -215,6 +215,7 @@ + Designer diff --git a/DPackRx/Features/SurroundWith/SurroundWith.json b/DPackRx/Features/SurroundWith/SurroundWith.json new file mode 100644 index 0000000..74e4e1d --- /dev/null +++ b/DPackRx/Features/SurroundWith/SurroundWith.json @@ -0,0 +1,95 @@ +{ + "Models": [ + { + "Language": "CSharp", + "Type": "TryCatch", + "StartingCode": "try\r\n{", + "EndingCode": "}\r\ncatch (Exception ex)\r\n{\r\n}", + "WordOffset": 0 + }, + { + "Language": "CSharp", + "Type": "TryFinally", + "StartingCode": "try\r\n{", + "EndingCode": "}\r\nfinally\r\n{\r\n}", + "WordOffset": 0 + }, + { + "Language": "CSharp", + "Type": "For", + "StartingCode": "for (int index = 0; index < count; index++)\r\n{", + "EndingCode": "}", + "WordOffset": 9 + }, + { + "Language": "CSharp", + "Type": "ForEach", + "StartingCode": "foreach (var element in container)\r\n{", + "EndingCode": "}", + "WordOffset": 2 + }, + { + "Language": "CSharp", + "Type": "Region", + "StartingCode": "#region Name", + "EndingCode": "#endregion", + "WordOffset": 2 + }, + { + "Language": "VB", + "Type": "TryCatch", + "StartingCode": "Try\r\n", + "EndingCode": "Catch ex As Exception\r\nEnd Try", + "WordOffset": 0 + }, + { + "Language": "VB", + "Type": "TryFinally", + "StartingCode": "Try\r\n", + "EndingCode": "Finally\r\nEnd Try", + "WordOffset": 0 + }, + { + "Language": "VB", + "Type": "For", + "StartingCode": "For index As Integer = 1 To 1\r\n", + "EndingCode": "Next", + "WordOffset": 7 + }, + { + "Language": "VB", + "Type": "ForEach", + "StartingCode": "For Each element In container\r\n", + "EndingCode": "Next", + "WordOffset": 2 + }, + { + "Language": "VB", + "Type": "Region", + "StartingCode": "#Region \"Name\"", + "EndingCode": "#End Region", + "WordOffset": 3 + }, + { + "Language": "CPP", + "Type": "TryCatch", + "StartingCode": "try\r\n{", + "EndingCode": "}\r\ncatch (char *str)\r\n{\r\n}", + "WordOffset": 0 + }, + { + "Language": "CPP", + "Type": "TryFinally", + "StartingCode": "try\r\n{", + "EndingCode": "}\r\n__finally\r\n{\r\n}", + "WordOffset": 0 + }, + { + "Language": "CPP", + "Type": "For", + "StartingCode": "for (int index = 0; index < count; index++)\r\n{", + "EndingCode": "}", + "WordOffset": 9 + } + ] +} \ No newline at end of file diff --git a/DPackRx/Features/SurroundWith/SurroundWithFeature.cs b/DPackRx/Features/SurroundWith/SurroundWithFeature.cs index f5ae47b..06075d1 100644 --- a/DPackRx/Features/SurroundWith/SurroundWithFeature.cs +++ b/DPackRx/Features/SurroundWith/SurroundWithFeature.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; -using System.Windows.Input; +using System.IO; +using System.Linq; using DPackRx.CodeModel; using DPackRx.Language; @@ -8,6 +9,8 @@ using DPackRx.Package; using DPackRx.Services; +using Newtonsoft.Json; + namespace DPackRx.Features.SurroundWith { /// @@ -19,29 +22,20 @@ public class SurroundWithFeature : Feature { #region Fields - private readonly IShellHelperService _shellHelperService; private readonly IShellSelectionService _shellSelectionService; private readonly IFileTypeResolver _fileTypeResolver; - private readonly IKeyboardService _keyboardService; - - protected internal const string SURROUND_WITH_COMMAND = "Edit.SurroundWith"; - protected internal const string ISERT_SNIPPET_COMMAND = "Edit.InsertSnippet"; - protected internal const string SNIPPET_TRY_CATCH = "try"; - protected internal const string SNIPPET_TRY_FINALLY = "tryf"; - protected internal const string SNIPPET_FOR = "for"; - protected internal const string SNIPPET_FOR_EACH = "foreach"; - protected internal const string SNIPPET_REGION = "#region"; + private readonly ISurroundWithFormatterService _surroundWithFormatterService; + private SurroundWithModel _model; #endregion public SurroundWithFeature(IServiceProvider serviceProvider, ILog log, IOptionsService optionsService, - IShellHelperService shellHelperService, IShellSelectionService shellSelectionService, - IFileTypeResolver fileTypeResolver, IKeyboardService keyboardService) : base(serviceProvider, log, optionsService) + IShellSelectionService shellSelectionService, IFileTypeResolver fileTypeResolver, + ISurroundWithFormatterService surroundWithFormatterService) : base(serviceProvider, log, optionsService) { - _shellHelperService = shellHelperService; _shellSelectionService = shellSelectionService; _fileTypeResolver = fileTypeResolver; - _keyboardService = keyboardService; + _surroundWithFormatterService = surroundWithFormatterService; } // Test constructor @@ -105,48 +99,78 @@ public override bool IsValidContext(int commandId) /// Execution status. public override bool Execute(int commandId) { + SurroundWithLanguage language = GetSurroundWithLanguage(); + IEnumerable langugeModels = GetSurroundWithModel(language); + if (langugeModels == null) + return false; + + SurroundWithType type; switch (commandId) { case CommandIDs.SW_TRY_CATCH: - return ExecuteSnippet(SNIPPET_TRY_CATCH); + type = SurroundWithType.TryCatch; + break; case CommandIDs.SW_TRY_FINALLY: - return ExecuteSnippet(SNIPPET_TRY_FINALLY); + type = SurroundWithType.TryFinally; + break; case CommandIDs.SW_FOR: - return ExecuteSnippet(SNIPPET_FOR); + type = SurroundWithType.For; + break; case CommandIDs.SW_FOR_EACH: - return ExecuteSnippet(SNIPPET_FOR_EACH); + type = SurroundWithType.ForEach; + break; case CommandIDs.SW_REGION: - return ExecuteSnippet(SNIPPET_REGION); + type = SurroundWithType.Region; + break; default: return base.Execute(commandId); } + + SurroundWithLanguageModel model = langugeModels.FirstOrDefault(m => m.Type == type); + if (model == null) + return base.Execute(commandId); + + _surroundWithFormatterService.Format(model); + return true; } #endregion #region Private Methods - /// - /// Executes a given surround with snippet. - /// - private bool ExecuteSnippet(string snippet) + private SurroundWithLanguage GetSurroundWithLanguage() { var project = _shellSelectionService.GetActiveProject(); var languageSet = _fileTypeResolver.GetCurrentLanguage(project, out _); - if (string.IsNullOrEmpty(languageSet.SurroundWithLanguageName)) + switch (languageSet.Type) { - _shellHelperService.ExecuteCommand(SURROUND_WITH_COMMAND); + case LanguageType.CSharp: + return SurroundWithLanguage.CSharp; + case LanguageType.VB: + return SurroundWithLanguage.VB; + case LanguageType.CPP: + return SurroundWithLanguage.CPP; + default: + return SurroundWithLanguage.None; } - else + } + + private IEnumerable GetSurroundWithModel(SurroundWithLanguage language) + { + if (_model == null) { - _shellHelperService.ExecuteCommand(ISERT_SNIPPET_COMMAND); - _keyboardService.Type(languageSet.SurroundWithLanguageName); - _keyboardService.Type(Key.Enter); + using (Stream stream = GetType().Assembly.GetManifestResourceStream($"{GetType().Namespace}.SurroundWith.json")) + { + using (StreamReader reader = new StreamReader(stream)) + { + string content = reader.ReadToEnd(); + _model = JsonConvert.DeserializeObject(content); + } + } } - _keyboardService.Type(snippet); - _keyboardService.Type(Key.Enter); - return true; + + return _model?.Models.Where(m => m.Language == language); } #endregion diff --git a/DPackRx/Features/SurroundWith/SurroundWithModel.cs b/DPackRx/Features/SurroundWith/SurroundWithModel.cs new file mode 100644 index 0000000..59592b8 --- /dev/null +++ b/DPackRx/Features/SurroundWith/SurroundWithModel.cs @@ -0,0 +1,56 @@ +using System.Collections.Generic; +using System.Diagnostics; + +using DPackRx.Language; + +namespace DPackRx.Features.SurroundWith +{ + /// + /// Surround with model. + /// + [DebuggerDisplay("Count = {Models.Count)")] + public class SurroundWithModel + { + public List Models { get; set; } = new List(); + } + + /// + /// Surround with language model. + /// + [DebuggerDisplay("{Language}, {Type}")] + public class SurroundWithLanguageModel + { + public SurroundWithLanguage Language { get; set; } + + public SurroundWithType Type { get; set; } + + public string StartingCode { get; set; } + + public string EndingCode { get; set; } + + public int WordOffset { get; set; } + } + + /// + /// All surround with supporting languages. + /// + public enum SurroundWithLanguage + { + None, + CSharp = LanguageType.CSharp, + VB = LanguageType.VB, + CPP = LanguageType.CPP + } + + /// + /// Supported surround with types. + /// + public enum SurroundWithType + { + TryCatch, + TryFinally, + For, + ForEach, + Region + } +} \ No newline at end of file diff --git a/DPackRx/Helpers/KeyboardHelper.cs b/DPackRx/Helpers/KeyboardHelper.cs deleted file mode 100644 index c528f61..0000000 --- a/DPackRx/Helpers/KeyboardHelper.cs +++ /dev/null @@ -1,226 +0,0 @@ -using System; -using System.ComponentModel; -using System.Linq; -using System.Runtime.InteropServices; -using System.Security; -using System.Security.Permissions; -using System.Threading; -using System.Windows.Input; - -namespace DPackRx.Helpers -{ - /// - /// Keyboard auto-entry helper. - /// - /// Source: Node.js tools for Visual Studio. - public static class KeyboardHelper - { - #region Fields - - private const int VKEY_SHIFT_MASK = 0x0100; - private const int VKEY_CHAR_MASK = 0x00FF; - - private const int INPUT_KEYBOARD = 1; // INPUT_MOUSE = 0; - - private const int KEY_EVENT_EXTENDED_KEY = 0x0001; - private const int KEY_EVENT_KEY_UP = 0x0002; - private const int KEY_EVENT_SCAN_CODE = 0x0008; - - private const char LEFT = '\u2190'; - private const char RIGHT = '\u2192'; - private const char UP = '\u2191'; - private const char DOWN = '\u2193'; - private const char CTRL_SPACE = '\u266B'; - private const char DELAY = '\u2026'; - - private static readonly Key[] _extendedKeys = new Key[] { - Key.RightAlt, Key.RightCtrl, - Key.NumLock, Key.Insert, Key.Delete, - Key.Home, Key.End, - Key.Prior, Key.Next, - Key.Up, Key.Down, Key.Left, Key.Right, - Key.Apps, Key.RWin, Key.LWin }; - - #endregion - - #region Exports - - [StructLayout(LayoutKind.Sequential)] - private struct INPUT - { - public int type; - public INPUTUNION union; - }; - - [StructLayout(LayoutKind.Explicit)] - private struct INPUTUNION - { - [FieldOffset(0)] - public MOUSEINPUT mouseInput; - [FieldOffset(0)] - public KEYBDINPUT keyboardInput; - }; - - [StructLayout(LayoutKind.Sequential)] - private struct MOUSEINPUT - { - public int dx; - public int dy; - public int mouseData; - public int dwFlags; - public int time; - public IntPtr dwExtraInfo; - }; - - [StructLayout(LayoutKind.Sequential)] - private struct KEYBDINPUT - { - public short wVk; - public short wScan; - public int dwFlags; - public int time; - public IntPtr dwExtraInfo; - }; - - [DllImport("user32.dll", CharSet = CharSet.Auto)] - private static extern int MapVirtualKey(int nVirtKey, int nMapType); - - [DllImport("user32.dll", SetLastError = true)] - private static extern int SendInput(int nInputs, ref INPUT mi, int cbSize); - - [DllImport("user32.dll", CharSet = CharSet.Auto)] - private static extern short VkKeyScan(char ch); - - #endregion - - #region Public Methods - - /// - /// Types the specified text. - /// - public static void Type(string text) - { - if (string.IsNullOrEmpty(text)) - return; - - foreach (var ch in text) - { - switch (ch) - { - case LEFT: - Type(Key.Left); - break; - case RIGHT: - Type(Key.Right); - break; - case UP: - Type(Key.Up); - break; - case DOWN: - Type(Key.Down); - break; - case CTRL_SPACE: - PressAndRelease(Key.Space, Key.LeftCtrl); - break; - case DELAY: - Thread.Sleep(1000); - break; - default: - var keyValue = VkKeyScan(ch); - bool keyIsShifted = (keyValue & VKEY_SHIFT_MASK) == VKEY_SHIFT_MASK; - var key = KeyInterop.KeyFromVirtualKey(keyValue & VKEY_CHAR_MASK); - - if (keyIsShifted) - Type(key, new Key[] { Key.LeftShift }); - else - Type(key); - - break; - } - } - - Thread.Sleep(200); - } - - /// - /// Presses and releases the specified key. - /// - public static void Type(Key key) - { - PressKey(key); - ReleaseKey(key); - } - - #endregion - - #region Private Methods - - private static void PressAndRelease(Key key, params Key[] modifiers) - { - for (var index = 0; index < modifiers.Length; index++) - { - PressKey(modifiers[index]); - } - - PressKey(key); - ReleaseKey(key); - - for (var index = modifiers.Length - 1; index >= 0; index--) - { - ReleaseKey(modifiers[index]); - } - } - - private static void PressKey(Key key) - { - SendKeyboardInput(key, true); - } - - private static void ReleaseKey(Key key) - { - SendKeyboardInput(key, false); - } - - private static void SendKeyboardInput(Key key, bool press) - { - PermissionSet permissions = new PermissionSet(PermissionState.Unrestricted); - permissions.Demand(); - - var input = new INPUT { type = INPUT_KEYBOARD }; - input.union.keyboardInput.wVk = (short)KeyInterop.VirtualKeyFromKey(key); - input.union.keyboardInput.wScan = (short)MapVirtualKey(input.union.keyboardInput.wVk, 0); - int dwFlags = 0; - if (input.union.keyboardInput.wScan > 0) - dwFlags |= KEY_EVENT_SCAN_CODE; - if (!press) - dwFlags |= KEY_EVENT_KEY_UP; - input.union.keyboardInput.dwFlags = dwFlags; - if (_extendedKeys.Contains(key)) - input.union.keyboardInput.dwFlags |= KEY_EVENT_EXTENDED_KEY; - input.union.keyboardInput.time = 0; - input.union.keyboardInput.dwExtraInfo = new IntPtr(0); - - if (SendInput(1, ref input, Marshal.SizeOf(input)) == 0) - throw new Win32Exception(Marshal.GetLastWin32Error()); - - Thread.Sleep(10); - } - - private static void Type(Key key, Key[] modifiers) - { - foreach (var modifier in modifiers) - { - PressKey(modifier); - } - - Type(key); - - foreach (var modifier in modifiers.Reverse()) - { - ReleaseKey(modifier); - } - } - - #endregion - } -} \ No newline at end of file diff --git a/DPackRx/Language/LanguageRegistrationService.cs b/DPackRx/Language/LanguageRegistrationService.cs index cb2e099..e12104d 100644 --- a/DPackRx/Language/LanguageRegistrationService.cs +++ b/DPackRx/Language/LanguageRegistrationService.cs @@ -22,6 +22,7 @@ public class LanguageRegistrationService : ILanguageRegistrationService private const string COMMENTS_KEY = "Comments"; private const string PROJECT_GUID_VALUE = "ProjectGuid"; private const string WEB_LANGUAGE_VALUE = "WebLanguage"; + private const string LANGUAGES_VALUE = "Languages"; private const string SMART_FORMAT_VALUE = "SmartFormat"; private const string XML_DOC = "XmlDoc"; private const string XML_DOC_SURROUND = "XmlDocSurround"; @@ -30,9 +31,6 @@ public class LanguageRegistrationService : ILanguageRegistrationService private const string IGNORE_CODE_TYPE = "IgnoreCodeType"; private const string CHECK_DUPLICATE_NAMES = "CheckDuplicateNames"; private const string PARENTLESS_FULL_NAME = "ParentlessFullName"; - private const string SURROUND_WITH_NAME = "SurroundWith"; - private const string SURROUND_WITH_LANGUAGE_NAME_VALUE = "SurroundWithLanguageName"; - private const string LOG_CATEGORY = "Language Registry"; #endregion @@ -51,7 +49,7 @@ public LanguageRegistrationService(ILog log, IPackageService packageService) /// Languages. public ICollection GetLanguages() { - List languages = new List(10); + List languageSettings = new List(10); RegistryKey dpackKey; try @@ -76,8 +74,8 @@ public ICollection GetLanguages() var friendlyName = string.Empty; var projectGuid = string.Empty; - var surroundWithLanguageName = string.Empty; var webLanguage = string.Empty; + var languages = string.Empty; var smartFormat = true; var xmlDoc = (string)null; var xmlDocSurround = false; @@ -86,7 +84,6 @@ public ICollection GetLanguages() var parentlessFullName = false; var designerFiles = LanguageDesignerFiles.NotSupported; var imports = LanguageImports.NotSupported; - var surroundWith = false; var langKey = dpackKey.OpenSubKey(id); if (langKey != null) @@ -95,8 +92,8 @@ public ICollection GetLanguages() { friendlyName = (string)langKey.GetValue(string.Empty, friendlyName); projectGuid = (string)langKey.GetValue(PROJECT_GUID_VALUE, projectGuid); - surroundWithLanguageName = (string)langKey.GetValue(SURROUND_WITH_LANGUAGE_NAME_VALUE, surroundWithLanguageName); webLanguage = (string)langKey.GetValue(WEB_LANGUAGE_VALUE, webLanguage); + languages = (string)langKey.GetValue(LANGUAGES_VALUE, languages); smartFormat = Convert.ToBoolean( (int)langKey.GetValue(SMART_FORMAT_VALUE, Convert.ToInt32(smartFormat))); xmlDoc = (string)langKey.GetValue(XML_DOC, xmlDoc); @@ -110,8 +107,6 @@ public ICollection GetLanguages() (int)langKey.GetValue(CHECK_DUPLICATE_NAMES, Convert.ToInt32(checkDuplicateNames))); parentlessFullName = Convert.ToBoolean( (int)langKey.GetValue(PARENTLESS_FULL_NAME, Convert.ToInt32(parentlessFullName))); - surroundWith = Convert.ToBoolean( - (int)langKey.GetValue(SURROUND_WITH_NAME, Convert.ToInt32(surroundWith))); } } if (string.IsNullOrEmpty(friendlyName)) @@ -119,12 +114,11 @@ public ICollection GetLanguages() _log.LogMessage($"Skipped language {id} definition w/o a friendly name", LOG_CATEGORY); continue; } - var webNames = !string.IsNullOrEmpty(surroundWithLanguageName) ? surroundWithLanguageName.Split(',') : new string[0]; - var language = new LanguageSettings(id, friendlyName, xmlDoc) + var language = new LanguageSettings(id, friendlyName, xmlDoc) { ProjectGuid = projectGuid, - WebNames = webNames, + WebNames = !string.IsNullOrEmpty(languages) ? languages.Split(',') : new string[0], WebLanguage = webLanguage, SmartFormat = smartFormat, XmlDocSurround = xmlDocSurround, @@ -132,9 +126,7 @@ public ICollection GetLanguages() Imports = imports, IgnoreCodeType = ignoreCodeType, CheckDuplicateNames = checkDuplicateNames, - ParentlessFullName = parentlessFullName, - SurroundWith = surroundWith, - SurroundWithLanguageName = surroundWithLanguageName, + ParentlessFullName = parentlessFullName }; var extKey = dpackKey.OpenSubKey(id + "\\" + EXTENSIONS_KEY); @@ -169,11 +161,11 @@ public ICollection GetLanguages() } } - languages.Add(language); + languageSettings.Add(language); } // for (index) } // using - return languages; + return languageSettings; } #endregion diff --git a/DPackRx/Language/LanguageSettings.cs b/DPackRx/Language/LanguageSettings.cs index d6061d6..acd2362 100644 --- a/DPackRx/Language/LanguageSettings.cs +++ b/DPackRx/Language/LanguageSettings.cs @@ -48,6 +48,9 @@ public LanguageSettings(string language, string friendlyName, string xmlDoc = nu case LanguageConsts.VS_LANGUAGE_SOLUTION_ITEMS: this.Type = LanguageType.SolutionItems; break; + case LanguageConsts.VS_LANGUAGE_SQL: + this.Type = LanguageType.Sql; + break; default: this.Type = LanguageType.Unknown; break; @@ -139,7 +142,7 @@ public LanguageSettings(string language, string friendlyName, string xmlDoc = nu public bool CheckDuplicateNames { get; set; } /// - /// Whether parentless code elements should use their full names in place of parent full names. + /// Whether parent-less code elements should use their full names in place of parent full names. /// public bool ParentlessFullName { get; set; } @@ -163,11 +166,6 @@ public LanguageSettings(string language, string friendlyName, string xmlDoc = nu /// public bool SurroundWith { get; set; } - /// - /// Language optional surround with name. - /// - public string SurroundWithLanguageName { get; set; } = string.Empty; - #endregion } @@ -185,7 +183,8 @@ public enum LanguageType CPP, JavaScript, Xml, - SolutionItems + SolutionItems, + Sql } #endregion diff --git a/DPackRx/Package.cs b/DPackRx/Package.cs index e0ab844..563bff1 100644 --- a/DPackRx/Package.cs +++ b/DPackRx/Package.cs @@ -46,23 +46,22 @@ namespace DPackRx // Languages [ProvideLanguage( "#101", EnvDTE.CodeModelLanguageConstants.vsCMLanguageCSharp, PrjKind.prjKindCSharpProject, "C#", new[] { "cs" }, - WebLanguage = "CSharpCodeProvider", Comments = new[] { "//", "/*" }, XmlDoc = "/doc/summary", - DesignerFiles = LanguageDesignerFiles.FullySupported, Imports = LanguageImports.Supported, - SurroundWith = true, SurroundWithLanguageName = "Visual C#")] + WebLanguage = "CSharpCodeProvider", Languages = "Visual C#", Comments = new[] { "//", "/*" }, XmlDoc = "/doc/summary", + DesignerFiles = LanguageDesignerFiles.FullySupported, Imports = LanguageImports.Supported, SurroundWith = true)] [ProvideLanguage( "#101", EnvDTE.CodeModelLanguageConstants.vsCMLanguageVB, PrjKind.prjKindVBProject, "VB", new[] { "bas", "vb", "frm" }, WebLanguage = "VBCodeProvider", Comments = new[] { "'" }, XmlDoc = "/summary", XmlDocSurround = true, - DesignerFiles = LanguageDesignerFiles.FullySupported, Imports = LanguageImports.Supported)] + DesignerFiles = LanguageDesignerFiles.FullySupported, Imports = LanguageImports.Supported, SurroundWith = true)] [ProvideLanguage( "#101", EnvDTE.CodeModelLanguageConstants.vsCMLanguageVC, "C++", new[] { "c", "cpp", "h", "hpp", "inl", "cc", "hxx", "hh" }, Comments = new[] { "//", "/*" }, XmlDoc = "/summary,", XmlDocSurround = true, - CheckDuplicateNames = true, IgnoreCodeType = true, ParentlessFullName = true, - SurroundWith = true)] + CheckDuplicateNames = true, IgnoreCodeType = true, ParentlessFullName = true, SurroundWith = true)] [ProvideLanguage( "#101", LanguageConsts.VS_LANGUAGE_JAVA_SCRIPT, "JavaScript", new[] { "js", "aspx", "ascx", "html", "htm", "asp", "master", "cshtml", "vbhtml" }, Comments = new[] { "//", "/*" }, SmartFormat = false)] [ProvideLanguage("#101", LanguageConsts.VS_LANGUAGE_XML, "Xml", new[] { "xml", "config", "targets", "vsct" }, Comments = new[] { "!--" })] [ProvideLanguage("#101", LanguageConsts.VS_LANGUAGE_SOLUTION_ITEMS, "Solution Items", new string[0])] + [ProvideLanguage("#101", LanguageConsts.VS_LANGUAGE_SQL, "SQL", new[] { "sql" })] #endregion public sealed class DPackRx : AsyncPackage { @@ -224,7 +223,7 @@ private async Task ConfigureServicesAsync(CancellationToken cancellationTo _container.Register(new PerContainerLifetime()); _container.Register(new PerContainerLifetime()); _container.Register(new PerContainerLifetime()); - _container.Register(new PerContainerLifetime()); + _container.Register(new PerContainerLifetime()); // Per request services _container.Register(); _container.Register(); diff --git a/DPackRx/Package/Beta.cs b/DPackRx/Package/Beta.cs index dc9ad01..19b9845 100644 --- a/DPackRx/Package/Beta.cs +++ b/DPackRx/Package/Beta.cs @@ -8,7 +8,7 @@ namespace DPackRx.Package /// internal static class Beta { - public static readonly DateTime ExpirationDate = new DateTime(2023, 1, 16).AddHours(12); + public static readonly DateTime ExpirationDate = new DateTime(2023, 4, 3).AddHours(12); } #endif } \ No newline at end of file diff --git a/DPackRx/Package/LanguageConsts.cs b/DPackRx/Package/LanguageConsts.cs index 3e43cca..a787df8 100644 --- a/DPackRx/Package/LanguageConsts.cs +++ b/DPackRx/Package/LanguageConsts.cs @@ -34,5 +34,10 @@ public static class LanguageConsts /// Fake solution items project language Guid. /// public const string VS_LANGUAGE_SOLUTION_ITEMS = "{FFFFFFFF-0000-FFFF-FFFF-FFFFFFFFFFFF}"; + + /// + /// VS Sql project language Guid. + /// + public const string VS_LANGUAGE_SQL = "{00D1A9C2-B5F0-4AF3-8072-F6C62B433612}"; } } \ No newline at end of file diff --git a/DPackRx/Package/Registration/ProvideLanguageAttribute.cs b/DPackRx/Package/Registration/ProvideLanguageAttribute.cs index a2e1b93..19c15da 100644 --- a/DPackRx/Package/Registration/ProvideLanguageAttribute.cs +++ b/DPackRx/Package/Registration/ProvideLanguageAttribute.cs @@ -72,6 +72,8 @@ public ProvideLanguageAttribute(string productName, string languageGuid, string public string WebLanguage { get; set; } + public string Languages { get; set; } + public string[] Comments { get; set; } public string XmlDoc { get; set; } @@ -123,6 +125,8 @@ public override void Register(RegistrationContext context) key.SetValue(nameof(this.ProjectGuid), this.ProjectGuid); if (!string.IsNullOrEmpty(this.WebLanguage)) key.SetValue(nameof(this.WebLanguage), this.WebLanguage); + if ((this.Languages != null) && (this.Languages.Length > 0)) + key.SetValue(nameof(this.Languages), string.Join(",", this.Languages)); if (!string.IsNullOrEmpty(this.XmlDoc)) key.SetValue(nameof(this.XmlDoc), this.XmlDoc); if (this.XmlDocSurround) diff --git a/DPackRx/Services/IKeyboardService.cs b/DPackRx/Services/IKeyboardService.cs deleted file mode 100644 index 65508fc..0000000 --- a/DPackRx/Services/IKeyboardService.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.Windows.Input; - -namespace DPackRx.Services -{ - /// - /// Keyboard auto-entry service. - /// - public interface IKeyboardService - { - /// - /// Types the specified text. - /// - void Type(string text); - - /// - /// Presses and releases the specified key. - /// - void Type(Key key); - } -} \ No newline at end of file diff --git a/DPackRx/Services/ISurroundWithFormatterService.cs b/DPackRx/Services/ISurroundWithFormatterService.cs new file mode 100644 index 0000000..71766e7 --- /dev/null +++ b/DPackRx/Services/ISurroundWithFormatterService.cs @@ -0,0 +1,15 @@ +using DPackRx.Features.SurroundWith; + +namespace DPackRx.Services +{ + /// + /// Surround with formatter. + /// + public interface ISurroundWithFormatterService + { + /// + /// Formats surround with according to provided model. + /// + void Format(SurroundWithLanguageModel model); + } +} \ No newline at end of file diff --git a/DPackRx/Services/KeyboardService.cs b/DPackRx/Services/KeyboardService.cs deleted file mode 100644 index e1b833f..0000000 --- a/DPackRx/Services/KeyboardService.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -using System.Windows.Input; - -using DPackRx.Helpers; - -namespace DPackRx.Services -{ - /// - /// Keyboard auto-entry service. - /// - public class KeyboardService : IKeyboardService - { - #region IKeyboardService Members - - /// - /// Types the specified text. - /// - public void Type(string text) - { - if (string.IsNullOrEmpty(text)) - throw new ArgumentNullException(nameof(text)); - - KeyboardHelper.Type(text); - } - - /// - /// Presses and releases the specified key. - /// - public void Type(Key key) - { - KeyboardHelper.Type(key); - } - - #endregion - } -} \ No newline at end of file diff --git a/DPackRx/Services/ShellService.cs b/DPackRx/Services/ShellService.cs index 914288f..acfbebf 100644 --- a/DPackRx/Services/ShellService.cs +++ b/DPackRx/Services/ShellService.cs @@ -1129,9 +1129,12 @@ public string GetNoCodeModelProjectLanguage(object project) var dteProject = GetProjectInternal(project); - // Solution items project has no code model - if (dteProject.Kind.Equals(EnvDTE.Constants.vsProjectKindSolutionItems, StringComparison.OrdinalIgnoreCase)) + // Solution items project has no code model; workaround for other project types - should come up with more generic way of handling that + var kind = dteProject.Kind; + if (kind.Equals(EnvDTE.Constants.vsProjectKindSolutionItems, StringComparison.OrdinalIgnoreCase)) return _languageService.GetLanguage(LanguageType.SolutionItems)?.Language; + else if (kind.Equals(LanguageConsts.VS_LANGUAGE_SQL, StringComparison.OrdinalIgnoreCase)) + return _languageService.GetLanguage(LanguageType.Sql)?.Language; if (IsKnownNoCodeModelProject(dteProject)) return string.Empty; diff --git a/DPackRx/Services/SurroundWithFormatterService.cs b/DPackRx/Services/SurroundWithFormatterService.cs new file mode 100644 index 0000000..f548a81 --- /dev/null +++ b/DPackRx/Services/SurroundWithFormatterService.cs @@ -0,0 +1,134 @@ +using System; +using System.Runtime.InteropServices; + +using DPackRx.Features.SurroundWith; + +using EnvDTE; + +using Microsoft.VisualStudio.Shell; + +namespace DPackRx.Services +{ + /// + /// Surround with formatter. + /// + public class SurroundWithFormatterService : ISurroundWithFormatterService + { + #region Fields + + private readonly IShellSelectionService _shellSelectionService; + private readonly IShellHelperService _shellHelperService; + + private const int E_ABORT = -2147467260; + private const string COMMAND_NEXT_WORD = "Edit.WordNext"; + private const string COMMAND_SELECT_WORD = "Edit.SelectCurrentWord"; + + #endregion + + public SurroundWithFormatterService(IShellSelectionService shellSelectionService, IShellHelperService shellHelperService) + { + _shellSelectionService = shellSelectionService; + _shellHelperService = shellHelperService; + } + + #region ISurroundWithFormatterService Members + + /// + /// Formats surround with according to provided model. + /// + public void Format(SurroundWithLanguageModel model) + { + ThreadHelper.ThrowIfNotOnUIThread(); + + if (model == null) + throw new ArgumentNullException(nameof(model)); + + Document activeDocument = _shellSelectionService.GetActiveDocument() as Document; + if (activeDocument == null) + return; + + TextSelection selection = activeDocument.Selection as TextSelection; + if (selection == null) + return; + + TextRanges colRanges = selection.TextRanges; + TextRange textRangeStart = colRanges.Item(1); + TextRange textRangeEnd = colRanges.Item(colRanges.Count); + EditPoint ptSelectionStart; + EditPoint ptSelectionEnd; + bool addLastLineBreak; + string firstLinePrefix; + + try + { + if ((selection.TopPoint.Line == selection.BottomPoint.Line) && + (selection.TopPoint.LineCharOffset == selection.BottomPoint.LineCharOffset)) + { + // No selection case + firstLinePrefix = string.Empty; + + EditPoint ptWork = textRangeStart.StartPoint.CreateEditPoint(); + ptWork.EndOfLine(); + if (ptWork.LineLength != 0) + ptWork.Insert(Environment.NewLine); + ptSelectionStart = ptWork.CreateEditPoint(); + + ptWork.Insert(model.StartingCode); + if (!model.StartingCode.EndsWith(Environment.NewLine)) + ptWork.Insert(Environment.NewLine); + ptWork.Insert(model.EndingCode); + ptSelectionEnd = ptWork.CreateEditPoint(); + addLastLineBreak = (!ptSelectionEnd.AtEndOfLine); + } + else + { + ptSelectionStart = textRangeStart.StartPoint.CreateEditPoint(); + ptSelectionEnd = textRangeEnd.EndPoint; + if ((textRangeEnd.StartPoint.Line == ptSelectionEnd.Line) && + (textRangeEnd.StartPoint.LineCharOffset == ptSelectionEnd.LineCharOffset)) + addLastLineBreak = true; + else + addLastLineBreak = false; + + textRangeStart.StartPoint.Insert(model.StartingCode); + if (!model.StartingCode.EndsWith(Environment.NewLine)) + textRangeStart.StartPoint.Insert(Environment.NewLine); + if ((ptSelectionEnd.AtEndOfLine) && (!ptSelectionEnd.AtStartOfLine)) + ptSelectionEnd.Insert(Environment.NewLine); + ptSelectionEnd.Insert(model.EndingCode); + } + + if (addLastLineBreak) + ptSelectionEnd.Insert(Environment.NewLine); + } + catch (Exception ex) + { + if ((ex is COMException comEx) && (comEx.ErrorCode == E_ABORT)) + return; + else + throw; + } + + selection.MoveToPoint(ptSelectionStart, false); + selection.MoveToPoint(ptSelectionEnd, true); + selection.SmartFormat(); + + // Jump to the element first line/column + selection.MoveToPoint(ptSelectionStart, false); + selection.StartOfLine(vsStartOfLineOptions.vsStartOfLineOptionsFirstText, false); + + // Select a word to be filled out + if (model.WordOffset > 0) + { + for (int i = 1; i <= model.WordOffset; i++) + { + _shellHelperService.ExecuteCommand(COMMAND_NEXT_WORD, string.Empty); + } + + _shellHelperService.ExecuteCommand(COMMAND_SELECT_WORD, string.Empty); + } + } + + #endregion + } +} \ No newline at end of file diff --git a/DPackRx/source.extension.vsixmanifest b/DPackRx/source.extension.vsixmanifest index de749a0..11fafdd 100644 --- a/DPackRx/source.extension.vsixmanifest +++ b/DPackRx/source.extension.vsixmanifest @@ -1,13 +1,14 @@  - + DPack Rx 2022 FREE tools collection designed to greatly increase developer's productivity, automate repetitive processes and expand upon some of Microsoft Visual Studio features. https://github.com/sergey-visual-studio/dpack Resources\Package.ico Resources\Preview.png Code, Coding, Tools, Productivity, DPack + true