├── .nuget ├── NuGet.exe ├── NuGet.Config └── NuGet.targets ├── GruntLauncher ├── Key.snk ├── Resources │ ├── icon.png │ ├── Images.png │ ├── Package.ico │ ├── preview.png │ ├── gulpInit.txt │ └── Init.txt ├── packages.config ├── Properties │ ├── Settings.settings │ ├── Settings.Designer.cs │ └── AssemblyInfo.cs ├── PkgCmdID.cs ├── GlobalSuppressions.cs ├── OptionPage.cs ├── Guids.cs ├── Helpers │ ├── ProcessHelpers.cs │ ├── OutputHelpers.cs │ └── SolutionHelpers.cs ├── source.extension.vsixmanifest ├── GulpParser.cs ├── Resources.Designer.cs ├── GruntParser.cs ├── Resources.resx ├── VSPackage.resx ├── GruntLauncher.vsct ├── GruntLauncher.csproj └── GruntLauncherPackage.cs ├── appveyor.yml ├── .gitattributes ├── README.md ├── LICENSE.txt ├── GruntLauncher.sln ├── Settings.StyleCop └── .gitignore /.nuget/NuGet.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/GruntLauncher/master/.nuget/NuGet.exe -------------------------------------------------------------------------------- /GruntLauncher/Key.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/GruntLauncher/master/GruntLauncher/Key.snk -------------------------------------------------------------------------------- /GruntLauncher/Resources/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/GruntLauncher/master/GruntLauncher/Resources/icon.png -------------------------------------------------------------------------------- /GruntLauncher/Resources/Images.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/GruntLauncher/master/GruntLauncher/Resources/Images.png -------------------------------------------------------------------------------- /GruntLauncher/Resources/Package.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/GruntLauncher/master/GruntLauncher/Resources/Package.ico -------------------------------------------------------------------------------- /GruntLauncher/Resources/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/GruntLauncher/master/GruntLauncher/Resources/preview.png -------------------------------------------------------------------------------- /.nuget/NuGet.Config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /GruntLauncher/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /GruntLauncher/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 1.0.{build} 2 | 3 | install: 4 | - ps: (new-object Net.WebClient).DownloadString("https://raw.github.com/madskristensen/ExtensionScripts/master/AppVeyor/vsix.ps1") | iex 5 | 6 | before_build: 7 | - ps: Vsix-IncrementVsixVersion | Vsix-UpdateBuildVersion 8 | 9 | build_script: 10 | - msbuild /p:configuration=Release /p:DeployExtension=false /p:ZipPackageCompressionLevel=normal /v:m 11 | 12 | after_test: 13 | - ps: Vsix-PushArtifacts | Vsix-PublishToGallery -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /GruntLauncher/Resources/gulpInit.txt: -------------------------------------------------------------------------------- 1 | var tasks = []; 2 | 3 | var gulpFN = {}; 4 | 5 | gulpFN.task = function(task, func){ 6 | tasks.push(task); 7 | } 8 | 9 | gulpFN.src = function(){ 10 | return gulpFN; 11 | } 12 | 13 | gulpFN.dest = function(){ 14 | return gulpFN; 15 | } 16 | 17 | gulpFN.pipe = function(){ 18 | return gulpFN; 19 | } 20 | 21 | gulpFN.watch= function(){ 22 | 23 | } 24 | 25 | 26 | var require = function(param){ 27 | if(param==="gulp"){ 28 | return gulpFN; 29 | }else{ 30 | var f = function(){}; 31 | f.task= function(){}; 32 | 33 | return f; 34 | } 35 | }; 36 | 37 | 38 | 39 | var __dirname=""; -------------------------------------------------------------------------------- /GruntLauncher/PkgCmdID.cs: -------------------------------------------------------------------------------- 1 | namespace Bjornej.GruntLauncher 2 | { 3 | using System; 4 | 5 | /// 6 | /// List of Command IDs 7 | /// 8 | public static class PkgCmdIDList 9 | { 10 | #region Visual Studio generated code 11 | 12 | /// 13 | /// Command id 14 | /// 15 | public const uint cmdidGruntLauncher = 0x100; 16 | public const uint cmdidGulpLauncher = 0x200; 17 | public const uint cmdidBowerUpdater = 0x1050; 18 | public const uint cmdidBowerInstaller = 0x1150; 19 | public const uint cmdidNpmUpdater = 0x1090; 20 | 21 | #endregion 22 | } 23 | } -------------------------------------------------------------------------------- /GruntLauncher/GlobalSuppressions.cs: -------------------------------------------------------------------------------- 1 | // This file is used by Code Analysis to maintain SuppressMessage 2 | // attributes that are applied to this project. Project-level 3 | // suppressions either have no target or are given a specific target 4 | // and scoped to a namespace, type, member, etc. 5 | // 6 | // To add a suppression to this file, right-click the message in the 7 | // Error List, point to "Suppress Message(s)", and click "In Project 8 | // Suppression File". You do not need to add suppressions to this 9 | // file manually. 10 | 11 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1017:MarkAssembliesWithComVisible", Justification = "Default suppression inserted by visual studio")] 12 | -------------------------------------------------------------------------------- /GruntLauncher/OptionPage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Linq; 5 | using System.Runtime.InteropServices; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using Microsoft.VisualStudio.Shell; 9 | 10 | namespace Bjornej.GruntLauncher 11 | { 12 | [ClassInterface(ClassInterfaceType.AutoDual)] 13 | [CLSCompliant(false), ComVisible(true)] 14 | public class OptionPage : DialogPage 15 | { 16 | 17 | [Category("Tasks Parser")] 18 | [DisplayName("Exclusion")] 19 | [Description("Specify a Regex pattern to exclude some unwanted tasks from list.")] 20 | public string TaskRegex 21 | { 22 | get; set; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /GruntLauncher/Guids.cs: -------------------------------------------------------------------------------- 1 | namespace Bjornej.GruntLauncher 2 | { 3 | using System; 4 | 5 | /// 6 | /// List of giud used by the plugind 7 | /// 8 | public static class GuidList 9 | { 10 | #region Visual Studio generated code 11 | 12 | /// 13 | /// Package Guid 14 | /// 15 | public const string guidGruntLauncherPkgString = "cced4e72-2f8c-4458-b8df-4934677e4bf3"; 16 | 17 | /// 18 | /// Command Guid as string 19 | /// 20 | public const string guidGruntLauncherCmdSetString = "59ce41a7-3da6-41c2-bf26-48dac265cbae"; 21 | 22 | /// 23 | /// Command Guid 24 | /// 25 | public static readonly Guid guidGruntLauncherCmdSet = new Guid(guidGruntLauncherCmdSetString); 26 | 27 | #endregion 28 | } 29 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### GruntLauncher ### 2 | 3 | Originally a plugin made to launch grunt taasks from inside Visual studio by right-clicking your gruntfile in the solution explorer it has now been extended with new functionality: 4 | 5 | - Launch grunt tasks from the solution solution explorer. When right clicking on a gruntfile you will see a new submenu listing all your options 6 | 7 | ![grunt](http://bjornej.github.io/images/grunt.png) 8 | 9 | - Execute bower updates when right clicking on the bower folder or on a plugin folder (thanks to Mads Kristensen for this feature) 10 | 11 | ![bowerall](http://bjornej.github.io/images/bowerall.png) 12 | ![bower](http://bjornej.github.io/images/bower.png) 13 | 14 | - Launch gulp tasks by right clicking on your gulpfile 15 | 16 | ![gulp](http://bjornej.github.io/images/gulp.png) 17 | 18 | #### Attention 19 | 20 | To work well this plugin needs a recent version of node.js installed on yout system due to a bug where output is not correclty redirected. If you don't see any output try to update node.js. 21 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 "Bjornej" Paolo Nicodemo 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /GruntLauncher/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // Il codice è stato generato da uno strumento. 4 | // Versione runtime:4.0.30319.34003 5 | // 6 | // Le modifiche apportate a questo file possono provocare un comportamento non corretto e andranno perse se 7 | // il codice viene rigenerato. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace Bjornej.GruntLauncher.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "12.0.0.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /GruntLauncher/Helpers/ProcessHelpers.cs: -------------------------------------------------------------------------------- 1 | namespace Bjornej.GruntLauncher 2 | { 3 | using System; 4 | using System.Diagnostics; 5 | using System.Management; 6 | 7 | /// 8 | /// Utility methods to manage the running processes 9 | /// 10 | public static class ProcessHelpers 11 | { 12 | /// 13 | /// Kill a process, and all of its children. 14 | /// 15 | /// Process ID 16 | public static void KillProcessAndChildren(int pid) 17 | { 18 | ManagementObjectSearcher searcher = new ManagementObjectSearcher("Select * From Win32_Process Where ParentProcessID=" + pid); 19 | ManagementObjectCollection moc = searcher.Get(); 20 | foreach (ManagementObject mo in moc) 21 | { 22 | KillProcessAndChildren(Convert.ToInt32(mo["ProcessID"])); 23 | } 24 | 25 | try 26 | { 27 | Process proc = Process.GetProcessById(pid); 28 | proc.Kill(); 29 | } 30 | catch (ArgumentException) 31 | { 32 | // Process already exited. 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /GruntLauncher/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using System.Resources; 4 | using System.Runtime.CompilerServices; 5 | using System.Runtime.InteropServices; 6 | 7 | // General Information about an assembly is controlled through the following 8 | // set of attributes. Change these attribute values to modify the information 9 | // associated with an assembly. 10 | [assembly: AssemblyTitle("GruntLauncher")] 11 | [assembly: AssemblyDescription("")] 12 | [assembly: AssemblyConfiguration("")] 13 | [assembly: AssemblyCompany("Bjornej")] 14 | [assembly: AssemblyProduct("GruntLauncher")] 15 | [assembly: AssemblyCopyright("")] 16 | [assembly: AssemblyTrademark("")] 17 | [assembly: AssemblyCulture("")] 18 | [assembly: ComVisible(false)] 19 | [assembly: CLSCompliant(false)] 20 | [assembly: NeutralResourcesLanguage("en-US")] 21 | 22 | // Version information for an assembly consists of the following four values: 23 | // 24 | // Major Version 25 | // Minor Version 26 | // Build Number 27 | // Revision 28 | // 29 | // You can specify all the values or you can default the Revision and Build Numbers 30 | // by using the '*' as shown below: 31 | [assembly: AssemblyVersion("1.1.0")] 32 | [assembly: AssemblyFileVersion("1.1.0")] 33 | -------------------------------------------------------------------------------- /GruntLauncher/source.extension.vsixmanifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Grunt Launcher 6 | Right click extension to launch Grunt, Gulp and Bower commands in Visual Studio. 7 | https://github.com/Bjornej/GruntLauncher 8 | Resources\LICENSE.txt 9 | https://github.com/Bjornej/GruntLauncher/releases 10 | Resources\icon.png 11 | Resources\preview.png 12 | grunt, gulp, bower, npm 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /GruntLauncher/GulpParser.cs: -------------------------------------------------------------------------------- 1 | namespace Bjornej.GruntLauncher 2 | { 3 | using Jurassic; 4 | using Jurassic.Library; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.IO; 9 | 10 | public static class GulpParser 11 | { 12 | public static ICollection ReadAllTasks(string path) 13 | { 14 | var list = new List(); 15 | 16 | if (path.EndsWith(".ts")) 17 | { 18 | path = path.Replace(".ts", ".js").Replace(".coffee",".js"); 19 | } 20 | 21 | if (!File.Exists(path)) 22 | { 23 | return list; 24 | } 25 | 26 | // executes the gulpfile with some little additions :) 27 | try 28 | { 29 | var engine = new ScriptEngine(); 30 | engine.Execute(Resources.gulpInit); 31 | engine.ExecuteFile(path); 32 | 33 | // reads the evaluated tasks 34 | ArrayInstance names = (ArrayInstance)engine.Evaluate("tasks"); 35 | foreach (var elem in names.ElementValues) 36 | { 37 | list.Add((string)elem); 38 | } 39 | } 40 | catch (Exception) 41 | { 42 | list.Add("Cannot parse gulpfile"); 43 | } 44 | 45 | return list.Distinct().ToList(); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /GruntLauncher.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.21005.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{43F51224-07C7-48E2-943E-59539AD01D6A}" 7 | ProjectSection(SolutionItems) = preProject 8 | .nuget\NuGet.Config = .nuget\NuGet.Config 9 | .nuget\NuGet.exe = .nuget\NuGet.exe 10 | .nuget\NuGet.targets = .nuget\NuGet.targets 11 | EndProjectSection 12 | EndProject 13 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{9A01155A-417E-4624-B9B9-E44E9C47745B}" 14 | ProjectSection(SolutionItems) = preProject 15 | Settings.StyleCop = Settings.StyleCop 16 | EndProjectSection 17 | EndProject 18 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GruntLauncher", "GruntLauncher\GruntLauncher.csproj", "{431382D5-7949-4C6E-9211-79D48E38AA5E}" 19 | EndProject 20 | Global 21 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 22 | Debug|Any CPU = Debug|Any CPU 23 | Release|Any CPU = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 26 | {431382D5-7949-4C6E-9211-79D48E38AA5E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {431382D5-7949-4C6E-9211-79D48E38AA5E}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {431382D5-7949-4C6E-9211-79D48E38AA5E}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {431382D5-7949-4C6E-9211-79D48E38AA5E}.Release|Any CPU.Build.0 = Release|Any CPU 30 | EndGlobalSection 31 | GlobalSection(SolutionProperties) = preSolution 32 | HideSolutionNode = FALSE 33 | EndGlobalSection 34 | EndGlobal 35 | -------------------------------------------------------------------------------- /GruntLauncher/Resources/Init.txt: -------------------------------------------------------------------------------- 1 | var names = []; 2 | var module = {}; 3 | var grunt = {}; 4 | 5 | var require = function(){ 6 | var a = function(){}; 7 | a.filterDev = function(){ 8 | return { 9 | forEach: function(){} 10 | }; 11 | }; 12 | 13 | a.resolve = function(){ 14 | return ""; 15 | }; 16 | 17 | return a; 18 | }; 19 | 20 | grunt.initConfig=function(param){ 21 | for(var prop in param){ 22 | if(param.hasOwnProperty(prop)){ 23 | names.push(prop); 24 | if(param[prop] === Object(param[prop])){ 25 | var arr=[]; 26 | for(var innerProp in param[prop]){ 27 | if(param[prop].hasOwnProperty(innerProp)){ 28 | if(innerProp!=='options'){ 29 | arr.push(prop+":"+innerProp); 30 | } 31 | } 32 | } 33 | if(arr.length > 1){ 34 | for (var i=0;i< arr.length;i++){ 35 | names.push(arr[i]); 36 | } 37 | } 38 | } 39 | } 40 | } 41 | }; 42 | grunt.file = { 43 | match: function(){return {};}, 44 | isMatch: function(){return {};}, 45 | expand: function(){return {};}, 46 | expandMapping: function(){return {};}, 47 | mkdir: function(){return {};}, 48 | recurse: function(){return {};}, 49 | readYAML: function(){return {};}, 50 | write: function(){return {};}, 51 | copy: function(){return {};}, 52 | exists: function(){return {};}, 53 | isFile: function(){return {};}, 54 | isPathAbsolute: function(){return {};}, 55 | arePathsEquivalent: function(){return {};}, 56 | doesPathContain: function(){return {};}, 57 | isPathCwd: function(){return {};}, 58 | isPathInCwd: function(){return {};}, 59 | read: function(){return {};}, 60 | readJSON: function(){return {};} 61 | } 62 | grunt.registerTask= function(name){names.push(name);}; 63 | grunt.registerMultiTask= function(name){names.push(name);}; 64 | grunt.loadNpmTasks = function(){}; 65 | grunt.template = { 66 | today:function(){}, 67 | addDelimiters:function(){}, 68 | setDelimiters:function(){}, 69 | process:function(){} 70 | }; 71 | grunt.renameTask = function(a,b){ names.push(b);} 72 | grunt.option = function(){}; 73 | 74 | var __dirname = ""; -------------------------------------------------------------------------------- /Settings.StyleCop: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | False 8 | 9 | 10 | 11 | 12 | False 13 | 14 | 15 | 16 | 17 | False 18 | 19 | 20 | 21 | 22 | False 23 | 24 | 25 | 26 | 27 | False 28 | 29 | 30 | 31 | 32 | False 33 | 34 | 35 | 36 | 37 | False 38 | 39 | 40 | 41 | 42 | False 43 | 44 | 45 | 46 | 47 | False 48 | 49 | 50 | 51 | 52 | False 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /GruntLauncher/Helpers/OutputHelpers.cs: -------------------------------------------------------------------------------- 1 | namespace Bjornej.GruntLauncher.Helpers 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using EnvDTE; 9 | using Microsoft.VisualStudio.Shell; 10 | using Microsoft.VisualStudio.Shell.Interop; 11 | 12 | /// 13 | /// Helper methods to write to the VS output window 14 | /// 15 | public static class OutputHelpers 16 | { 17 | /// 18 | /// Window pane used to show grunt output 19 | /// 20 | private static IVsOutputWindowPane outputWindowPane; 21 | 22 | /// 23 | /// Prints a string to the Output window in a custom pane 24 | /// 25 | /// The string to print 26 | /// Decides if the output pane should be focused 27 | public static void Output(string msg, bool focus = false) 28 | { 29 | if (outputWindowPane == null) 30 | { 31 | Init(); 32 | } 33 | 34 | if (focus) 35 | { 36 | var outputWindow = Package.GetGlobalService(typeof(SVsOutputWindow)) as IVsOutputWindow; 37 | var dte = (DTE)Package.GetGlobalService(typeof(DTE)); 38 | Window window = (Window)dte.Windows.Item(EnvDTE.Constants.vsWindowKindOutput); 39 | window.Visible = true; 40 | window.Activate(); 41 | outputWindowPane.Activate(); 42 | } 43 | 44 | // Output the message 45 | outputWindowPane.OutputString(msg); 46 | } 47 | 48 | /// 49 | /// Initializes the output window 50 | /// 51 | private static void Init() 52 | { 53 | var outputWindow = Package.GetGlobalService(typeof(SVsOutputWindow)) as IVsOutputWindow; 54 | var dte = (DTE)Package.GetGlobalService(typeof(DTE)); 55 | Window window = (Window)dte.Windows.Item(EnvDTE.Constants.vsWindowKindOutput); 56 | window.Visible = true; 57 | 58 | // Ensure that the desired pane is visible 59 | var paneGuid = Microsoft.VisualStudio.VSConstants.OutputWindowPaneGuid.GeneralPane_guid; 60 | 61 | outputWindow.CreatePane(paneGuid, "Grunt execution", 1, 0); 62 | outputWindow.GetPane(paneGuid, out outputWindowPane); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /GruntLauncher/Helpers/SolutionHelpers.cs: -------------------------------------------------------------------------------- 1 | namespace Bjornej.GruntLauncher.Helpers 2 | { 3 | using System; 4 | using EnvDTE; 5 | using Microsoft.VisualStudio.Shell; 6 | using EnvDTE80; 7 | using System.IO; 8 | 9 | /// 10 | /// Static helper methods to interact with the visual studio open solution 11 | /// 12 | public static class SolutionHelpers 13 | { 14 | /// 15 | /// Gets the extensibility object 16 | /// 17 | /// The estenzibility object 18 | public static EnvDTE80.DTE2 GetDTE2() 19 | { 20 | return Package.GetGlobalService(typeof(DTE)) as EnvDTE80.DTE2; 21 | } 22 | 23 | /// 24 | /// Get the full path of the file clicked upon when opening the contextual menu 25 | /// 26 | /// The full path of the current file 27 | public static string GetSourceFilePath() 28 | { 29 | EnvDTE80.DTE2 applicationObject = GetDTE2(); 30 | UIHierarchy uih = applicationObject.ToolWindows.SolutionExplorer; 31 | Array selectedItems = (Array)uih.SelectedItems; 32 | 33 | if (null != selectedItems) 34 | { 35 | foreach (UIHierarchyItem selItem in selectedItems) 36 | { 37 | ProjectItem prjItem = selItem.Object as ProjectItem; 38 | string filePath; 39 | if (prjItem.Properties != null) 40 | { 41 | filePath = prjItem.Properties.Item("FullPath").Value.ToString(); 42 | } 43 | else 44 | { 45 | filePath = prjItem.FileNames[1]; 46 | } 47 | 48 | return filePath; 49 | } 50 | } 51 | 52 | return string.Empty; 53 | } 54 | 55 | public static string GetRootFolder(DTE2 dte) 56 | { 57 | Project project = GetActiveProject(dte); 58 | 59 | if (project == null) 60 | return null; 61 | 62 | string path = project.Properties.Item("FullPath").Value.ToString(); 63 | 64 | if (Directory.Exists(path)) 65 | return path; 66 | 67 | return Path.GetDirectoryName(path); 68 | } 69 | 70 | private static Project GetActiveProject(DTE2 dte) 71 | { 72 | try 73 | { 74 | Array activeSolutionProjects = dte.ActiveSolutionProjects as Array; 75 | 76 | if (activeSolutionProjects != null && activeSolutionProjects.Length > 0) 77 | return activeSolutionProjects.GetValue(0) as Project; 78 | } 79 | catch (Exception) 80 | { 81 | } 82 | 83 | return null; 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ################# 2 | ## Eclipse 3 | ################# 4 | 5 | *.pydevproject 6 | .project 7 | .metadata 8 | bin/ 9 | tmp/ 10 | *.tmp 11 | *.bak 12 | *.swp 13 | *~.nib 14 | local.properties 15 | .classpath 16 | .settings/ 17 | .loadpath 18 | 19 | # External tool builders 20 | .externalToolBuilders/ 21 | 22 | # Locally stored "Eclipse launch configurations" 23 | *.launch 24 | 25 | # CDT-specific 26 | .cproject 27 | 28 | # PDT-specific 29 | .buildpath 30 | 31 | 32 | ################# 33 | ## Visual Studio 34 | ################# 35 | 36 | ## Ignore Visual Studio temporary files, build results, and 37 | ## files generated by popular Visual Studio add-ons. 38 | 39 | # User-specific files 40 | *.suo 41 | *.user 42 | *.sln.docstates 43 | *.sln.ide 44 | 45 | # Build results 46 | 47 | [Dd]ebug/ 48 | [Rr]elease/ 49 | x64/ 50 | build/ 51 | [Bb]in/ 52 | [Oo]bj/ 53 | 54 | # MSTest test Results 55 | [Tt]est[Rr]esult*/ 56 | [Bb]uild[Ll]og.* 57 | 58 | *_i.c 59 | *_p.c 60 | *.ilk 61 | *.meta 62 | *.obj 63 | *.pch 64 | *.pdb 65 | *.pgc 66 | *.pgd 67 | *.rsp 68 | *.sbr 69 | *.tlb 70 | *.tli 71 | *.tlh 72 | *.tmp 73 | *.tmp_proj 74 | *.log 75 | *.vspscc 76 | *.vssscc 77 | .builds 78 | *.pidb 79 | *.log 80 | *.scc 81 | 82 | # Visual C++ cache files 83 | ipch/ 84 | *.aps 85 | *.ncb 86 | *.opensdf 87 | *.sdf 88 | *.cachefile 89 | 90 | # Visual Studio profiler 91 | *.psess 92 | *.vsp 93 | *.vspx 94 | 95 | # Guidance Automation Toolkit 96 | *.gpState 97 | 98 | # ReSharper is a .NET coding add-in 99 | _ReSharper*/ 100 | *.[Rr]e[Ss]harper 101 | 102 | # TeamCity is a build add-in 103 | _TeamCity* 104 | 105 | # DotCover is a Code Coverage Tool 106 | *.dotCover 107 | 108 | # NCrunch 109 | *.ncrunch* 110 | .*crunch*.local.xml 111 | 112 | # Installshield output folder 113 | [Ee]xpress/ 114 | 115 | # DocProject is a documentation generator add-in 116 | DocProject/buildhelp/ 117 | DocProject/Help/*.HxT 118 | DocProject/Help/*.HxC 119 | DocProject/Help/*.hhc 120 | DocProject/Help/*.hhk 121 | DocProject/Help/*.hhp 122 | DocProject/Help/Html2 123 | DocProject/Help/html 124 | 125 | # Click-Once directory 126 | publish/ 127 | 128 | # Publish Web Output 129 | *.Publish.xml 130 | *.pubxml 131 | 132 | # NuGet Packages Directory 133 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line 134 | packages/ 135 | 136 | # Windows Azure Build Output 137 | csx 138 | *.build.csdef 139 | 140 | # Windows Store app package directory 141 | AppPackages/ 142 | 143 | # Others 144 | sql/ 145 | *.Cache 146 | ClientBin/ 147 | [Ss]tyle[Cc]op.* 148 | ~$* 149 | *~ 150 | *.dbmdl 151 | *.[Pp]ublish.xml 152 | *.pfx 153 | *.publishsettings 154 | 155 | # RIA/Silverlight projects 156 | Generated_Code/ 157 | 158 | # Backup & report files from converting an old project file to a newer 159 | # Visual Studio version. Backup files are not needed, because we have git ;-) 160 | _UpgradeReport_Files/ 161 | Backup*/ 162 | UpgradeLog*.XML 163 | UpgradeLog*.htm 164 | 165 | # SQL Server files 166 | App_Data/*.mdf 167 | App_Data/*.ldf 168 | 169 | ############# 170 | ## Windows detritus 171 | ############# 172 | 173 | # Windows image file caches 174 | Thumbs.db 175 | ehthumbs.db 176 | 177 | # Folder config file 178 | Desktop.ini 179 | 180 | # Recycle Bin used on file shares 181 | $RECYCLE.BIN/ 182 | 183 | # Mac crap 184 | .DS_Store 185 | 186 | 187 | ############# 188 | ## Python 189 | ############# 190 | 191 | *.py[co] 192 | 193 | # Packages 194 | *.egg 195 | *.egg-info 196 | dist/ 197 | build/ 198 | eggs/ 199 | parts/ 200 | var/ 201 | sdist/ 202 | develop-eggs/ 203 | .installed.cfg 204 | 205 | # Installer logs 206 | pip-log.txt 207 | 208 | # Unit test / coverage reports 209 | .coverage 210 | .tox 211 | 212 | #Translations 213 | *.mo 214 | 215 | #Mr Developer 216 | .mr.developer.cfg 217 | -------------------------------------------------------------------------------- /GruntLauncher/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // Il codice è stato generato da uno strumento. 4 | // Versione runtime:4.0.30319.34011 5 | // 6 | // Le modifiche apportate a questo file possono provocare un comportamento non corretto e andranno perse se 7 | // il codice viene rigenerato. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace Bjornej.GruntLauncher { 12 | using System; 13 | 14 | 15 | /// 16 | /// Classe di risorse fortemente tipizzata per la ricerca di stringhe localizzate e così via. 17 | /// 18 | // Questa classe è stata generata automaticamente dalla classe StronglyTypedResourceBuilder. 19 | // tramite uno strumento quale ResGen o Visual Studio. 20 | // Per aggiungere o rimuovere un membro, modificare il file con estensione ResX ed eseguire nuovamente ResGen 21 | // con l'opzione /str oppure ricompilare il progetto VS. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Restituisce l'istanza di ResourceManager nella cache utilizzata da questa classe. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Bjornej.GruntLauncher.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Esegue l'override della proprietà CurrentUICulture del thread corrente per tutte le 51 | /// ricerche di risorse eseguite utilizzando questa classe di risorse fortemente tipizzata. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | 63 | /// 64 | /// Cerca una stringa localizzata simile a var tasks = []; 65 | /// 66 | ///var gulp = {}; 67 | /// 68 | ///gulp.task = function(task, func){ 69 | /// tasks.push(task); 70 | ///} 71 | /// 72 | ///gulp.src = function(){ 73 | /// return gulp; 74 | ///} 75 | /// 76 | ///gulp.dest = function(){ 77 | /// return gulp; 78 | ///} 79 | /// 80 | ///gulp.pipe = function(){ 81 | /// return gulp; 82 | ///} 83 | /// 84 | ///gulp.watch= function(){ 85 | /// 86 | ///} 87 | /// 88 | ///var require = function(param){ 89 | /// if(param==="gulp"){ 90 | /// return gulp; 91 | /// }else{ 92 | /// var f = function(){} 93 | /// return f; 94 | /// } 95 | ///}; 96 | /// 97 | ///var __dirname="";. 98 | /// 99 | internal static string gulpInit { 100 | get { 101 | return ResourceManager.GetString("gulpInit", resourceCulture); 102 | } 103 | } 104 | 105 | /// 106 | /// Cerca una stringa localizzata simile a var names = []; 107 | ///var module = {}; 108 | ///var grunt = {}; 109 | /// 110 | ///var require = function(){ 111 | /// var a = function(){}; 112 | /// a.filterDev = function(){ 113 | /// return { 114 | /// forEach: function(){} 115 | /// }; 116 | /// } 117 | /// return a; 118 | ///}; 119 | /// 120 | ///grunt.initConfig=function(param){ 121 | /// for(var prop in param){ 122 | /// if(param.hasOwnProperty(prop)){ 123 | /// names.push(prop); 124 | /// } 125 | /// } 126 | ///}; 127 | ///grunt.file = { readJSON: function(){return {};}} 128 | ///grunt.registerTask= function(name){names.push(name);}; 129 | ///grunt.loadNpmTasks = function(){}; 130 | ///grunt.template = {today:function(){}}; [stringa troncata]";. 131 | /// 132 | internal static string Init { 133 | get { 134 | return ResourceManager.GetString("Init", resourceCulture); 135 | } 136 | } 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /GruntLauncher/GruntParser.cs: -------------------------------------------------------------------------------- 1 | namespace Bjornej.GruntLauncher 2 | { 3 | using Jurassic; 4 | using Jurassic.Library; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Diagnostics; 8 | using System.IO; 9 | using System.Linq; 10 | 11 | /// 12 | /// Parses a Gruntfile to extract all the contained tasks by executing it and reading them trough a javascript hack. 13 | /// See the Init file in the resources. 14 | /// 15 | public static class GruntParser 16 | { 17 | private static List _list = null; 18 | private static bool _valid = false; 19 | 20 | private static IDictionary>> cache = new Dictionary>>(); 21 | 22 | /// 23 | /// Reads all the defined tasks in a Gruntfile whose path is passed as parameter 24 | /// 25 | /// The path of the file to examine 26 | /// A list of tasks 27 | public static ICollection ReadAllTasks(string path) 28 | { 29 | _list = new List(); 30 | 31 | if (path.EndsWith(".ts")) 32 | { 33 | path = path.Replace(".ts", ".js").Replace(".coffee", ".js"); 34 | } 35 | 36 | if (!File.Exists(path)) 37 | { 38 | return _list; 39 | } 40 | 41 | var lastModifiedDate = File.GetLastWriteTime(path); 42 | 43 | if (cache.ContainsKey(path)) 44 | { 45 | var data = cache[path]; 46 | if (lastModifiedDate == data.Item1) { 47 | return data.Item2; 48 | } 49 | } 50 | 51 | if (!ParseFromScriptEngine(path)) 52 | { 53 | ParseFromProcess(path); 54 | } 55 | 56 | if (_list.Count == 0) 57 | { 58 | _list.Add("Cannot parse Gruntfile"); 59 | }else 60 | { 61 | cache[path]= new Tuple>(lastModifiedDate, _list.Distinct().ToList()); 62 | } 63 | 64 | return _list.Distinct().ToList(); 65 | } 66 | 67 | /// 68 | /// Parses task names with help of Jurassic script engine. 69 | /// 70 | private static bool ParseFromScriptEngine(string path) 71 | { 72 | // executes the gruntfile with some little additions :) 73 | try 74 | { 75 | var engine = new ScriptEngine(); 76 | engine.Execute(Resources.Init); 77 | engine.ExecuteFile(path); 78 | engine.Execute("module.exports(grunt);"); 79 | 80 | // reads the evaluated tasks 81 | ArrayInstance names = (ArrayInstance)engine.Evaluate("names"); 82 | foreach (var elem in names.ElementValues) 83 | { 84 | _list.Add(elem.ToString()); 85 | } 86 | } 87 | catch (Exception) 88 | { 89 | return false; 90 | } 91 | return true; 92 | } 93 | 94 | /// 95 | /// Parses task names with help of grunt process execution. 96 | /// 97 | private static bool ParseFromProcess(string path) 98 | { 99 | _valid = false; 100 | try 101 | { 102 | System.Diagnostics.ProcessStartInfo procStartInfo = new ProcessStartInfo() 103 | { 104 | RedirectStandardOutput = true, 105 | StandardOutputEncoding = System.Text.Encoding.UTF8, 106 | UseShellExecute = false, 107 | CreateNoWindow = true, 108 | WorkingDirectory = Path.GetDirectoryName(path), 109 | FileName = "cmd", 110 | Arguments = " /c \"grunt --help 2>&1\"", 111 | }; 112 | 113 | System.Diagnostics.Process proc = new System.Diagnostics.Process() 114 | { 115 | StartInfo = procStartInfo, 116 | EnableRaisingEvents = true 117 | }; 118 | 119 | proc.OutputDataReceived += (object sendingProcess, DataReceivedEventArgs outLine) => GruntParser.OutputHandler(outLine.Data); 120 | proc.Start(); 121 | proc.BeginOutputReadLine(); 122 | proc.WaitForExit(); 123 | proc.Close(); 124 | } 125 | catch (Exception) 126 | { 127 | return false; 128 | } 129 | return true; 130 | } 131 | 132 | /// 133 | /// Reads output stream from process execution and builds up task name list. 134 | /// 135 | private static void OutputHandler(string message) 136 | { 137 | if (String.IsNullOrEmpty(message)) 138 | { 139 | if (_valid) 140 | { 141 | // Stop to read from now on. 142 | _valid = false; 143 | } 144 | // Skip null strings. 145 | return; 146 | } 147 | 148 | message = message.Trim(); 149 | if (message.StartsWith("Available tasks")) 150 | { 151 | // Start to read from now on. 152 | _valid = true; 153 | return; 154 | } 155 | 156 | if (!_valid || message.EndsWith("*") || message.StartsWith("\"")) 157 | { 158 | // Skip invalid parts and internal tasks of output. 159 | return; 160 | } 161 | 162 | int index = message.IndexOf(' '); 163 | if (index > 0 && index < message.Length) 164 | { 165 | // Cut out the task name. 166 | _list.Add(message.Substring(0, index)); 167 | } 168 | } 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /GruntLauncher/Resources.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | 122 | Resources\gulpInit.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252 123 | 124 | 125 | Resources\Init.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252 126 | 127 | -------------------------------------------------------------------------------- /.nuget/NuGet.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(MSBuildProjectDirectory)\..\ 5 | 6 | 7 | false 8 | 9 | 10 | false 11 | 12 | 13 | true 14 | 15 | 16 | false 17 | 18 | 19 | 20 | 21 | 22 | 26 | 27 | 28 | 29 | 30 | $([System.IO.Path]::Combine($(SolutionDir), ".nuget")) 31 | $([System.IO.Path]::Combine($(ProjectDir), "packages.config")) 32 | 33 | 34 | 35 | 36 | $(SolutionDir).nuget 37 | packages.config 38 | 39 | 40 | 41 | 42 | $(NuGetToolsPath)\NuGet.exe 43 | @(PackageSource) 44 | 45 | "$(NuGetExePath)" 46 | mono --runtime=v4.0.30319 $(NuGetExePath) 47 | 48 | $(TargetDir.Trim('\\')) 49 | 50 | -RequireConsent 51 | -NonInteractive 52 | 53 | 54 | $(NuGetCommand) install "$(PackagesConfig)" -source "$(PackageSources)" $(NonInteractiveSwitch) $(RequireConsentSwitch) -solutionDir "$(SolutionDir) " 55 | $(NuGetCommand) pack "$(ProjectPath)" -Properties Configuration=$(Configuration) $(NonInteractiveSwitch) -OutputDirectory "$(PackageOutputDir)" -symbols 56 | 57 | 58 | 59 | RestorePackages; 60 | $(BuildDependsOn); 61 | 62 | 63 | 64 | 65 | $(BuildDependsOn); 66 | BuildPackage; 67 | 68 | 69 | 70 | 71 | 72 | 73 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 88 | 89 | 92 | 93 | 94 | 95 | 97 | 98 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 130 | 131 | 132 | 133 | -------------------------------------------------------------------------------- /GruntLauncher/VSPackage.resx: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | text/microsoft-resx 120 | 121 | 122 | 2.0 123 | 124 | 125 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 126 | 127 | 128 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 129 | 130 | 131 | 132 | GruntLauncher 133 | 134 | 135 | Right click extension to launch Grunt commands 136 | 137 | 138 | Resources\Package.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 139 | 140 | -------------------------------------------------------------------------------- /GruntLauncher/GruntLauncher.vsct: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 28 | 29 | 36 | 37 | 38 | 42 | 43 | 44 | 45 | 46 | 47 | Grunt 48 | 49 | 50 | 51 | 52 | 53 | 54 | Gulp 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | Dynamic 72 | 73 | 74 | 75 | 76 | Dynamic 77 | 78 | 79 | 80 | 81 | 82 | 84 | 85 | 92 | 93 | 102 | 103 | 112 | 113 | 124 | 125 | 135 | 136 | 146 | 147 | 148 | 149 | 150 | 151 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | -------------------------------------------------------------------------------- /GruntLauncher/GruntLauncher.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(VisualStudioVersion) 5 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 6 | ..\ 7 | true 8 | 9 | 10 | 11 | 12 | 4.0 13 | Program 14 | $(DevEnvDir)\devenv.exe 15 | /rootsuffix Exp 16 | 17 | 18 | 19 | Debug 20 | AnyCPU 21 | 2.0 22 | {431382D5-7949-4C6E-9211-79D48E38AA5E} 23 | {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 24 | Library 25 | Properties 26 | Bjornej.GruntLauncher 27 | GruntLauncher 28 | false 29 | Key.snk 30 | v4.5 31 | 32 | 33 | true 34 | full 35 | false 36 | bin\Debug\ 37 | DEBUG;TRACE 38 | prompt 39 | 4 40 | True 41 | False 42 | 43 | 44 | pdbonly 45 | true 46 | bin\Release\ 47 | TRACE 48 | prompt 49 | 4 50 | true 51 | 52 | 53 | 54 | False 55 | ..\packages\Jurassic.2.1.1\lib\Jurassic.dll 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | true 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | {80CC9F66-E7D8-4DDD-85B6-D9E6CD0E93E2} 82 | 8 83 | 0 84 | 0 85 | primary 86 | False 87 | False 88 | 89 | 90 | {26AD1324-4B7C-44BC-84F8-B86AED45729F} 91 | 10 92 | 0 93 | 0 94 | primary 95 | False 96 | False 97 | 98 | 99 | {1A31287A-4D7D-413E-8E32-3B374931BD89} 100 | 8 101 | 0 102 | 0 103 | primary 104 | False 105 | False 106 | 107 | 108 | {2CE2370E-D744-4936-A090-3FFFE667B0E1} 109 | 9 110 | 0 111 | 0 112 | primary 113 | False 114 | False 115 | 116 | 117 | {1CBA492E-7263-47BB-87FE-639000619B15} 118 | 8 119 | 0 120 | 0 121 | primary 122 | False 123 | False 124 | 125 | 126 | {00020430-0000-0000-C000-000000000046} 127 | 2 128 | 0 129 | 0 130 | primary 131 | False 132 | False 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | component 144 | 145 | 146 | True 147 | True 148 | Settings.settings 149 | 150 | 151 | True 152 | True 153 | Resources.resx 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | ResXFileCodeGenerator 163 | Resources.Designer.cs 164 | Designer 165 | 166 | 167 | true 168 | VSPackage 169 | 170 | 171 | 172 | 173 | 174 | SettingsSingleFileGenerator 175 | Settings.Designer.cs 176 | 177 | 178 | Designer 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | Menus.ctmenu 187 | Designer 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | Resources\LICENSE.txt 196 | true 197 | 198 | 199 | true 200 | 201 | 202 | 203 | 204 | true 205 | 206 | 207 | 208 | 209 | true 210 | 211 | 212 | 213 | 214 | 221 | 222 | ..\packages\StyleCop.MSBuild.4.7.47.0\tools\StyleCop.targets 223 | 224 | 225 | 226 | Failed to import StyleCop.MSBuild targets from '$(StyleCopMSBuildTargetsFile)'. The StyleCop.MSBuild package was either missing or incomplete when the project was loaded. Ensure that the package is present and then restart the build. If you are using an IDE (e.g. Visual Studio), reload the project before restarting the build. 227 | Failed to import StyleCop.MSBuild targets from '$(StyleCopMSBuildTargetsFile)'. The StyleCop.MSBuild package was either missing or incomplete when the project was loaded (but is now present). To fix this, restart the build. If you are using an IDE (e.g. Visual Studio), reload the project before restarting the build. 228 | Failed to import StyleCop.MSBuild targets from '$(StyleCopMSBuildTargetsFile)'. The StyleCop.MSBuild package was either missing or incomplete when the project was loaded. To fix this, restore the package and then restart the build. If you are using an IDE (e.g. Visual Studio), you may need to reload the project before restarting the build. Note that regular NuGet package restore (during build) does not work with this package because the package needs to be present before the project is loaded. If this is an automated build (e.g. CI server), you may want to ensure that the build process restores the StyleCop.MSBuild package before the project is built. 229 | Failed to import StyleCop.MSBuild targets from '$(StyleCopMSBuildTargetsFile)'. The StyleCop.MSBuild package was either missing or incomplete when the project was loaded (but is now present). To fix this, restart the build. If you are using an IDE (e.g. Visual Studio), reload the project before restarting the build. Note that when using regular NuGet package restore (during build) the package will not be available for the initial build because the package needs to be present before the project is loaded. If package restore executes successfully in the initial build then the package will be available for subsequent builds. If this is an automated build (e.g. CI server), you may want to ensure that the build process restores the StyleCop.MSBuild package before the initial build. 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | StyleCopMSBuildTargetsNotFound;$(PrepareForBuildDependsOn) 243 | 244 | -------------------------------------------------------------------------------- /GruntLauncher/GruntLauncherPackage.cs: -------------------------------------------------------------------------------- 1 | namespace Bjornej.GruntLauncher 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.ComponentModel.Design; 6 | using System.Diagnostics; 7 | using System.IO; 8 | using System.Runtime.InteropServices; 9 | using Bjornej.GruntLauncher.Helpers; 10 | using Microsoft.VisualStudio.Shell; 11 | using EnvDTE80; 12 | using EnvDTE; 13 | using System.Text.RegularExpressions; 14 | using System.Text; 15 | 16 | 17 | 18 | /// 19 | /// Main class that implements the gruntLauncher packages 20 | /// 21 | [PackageRegistration(UseManagedResourcesOnly = true)] 22 | [InstalledProductRegistration("#110", "#112", "1.1", IconResourceID = 400)] 23 | [ProvideMenuResource("Menus.ctmenu", 1)] 24 | [Guid(GuidList.guidGruntLauncherPkgString)] 25 | [ProvideAutoLoad("{f1536ef8-92ec-443c-9ed7-fdadf150da82}")] 26 | [ProvideOptionPage(typeof(OptionPage), "Grunt Launcher", "General", 0, 0, true)] 27 | public sealed class GruntLauncherPackage : Package 28 | { 29 | /// 30 | /// List of dynamic commands 31 | /// 32 | private static List commands; 33 | 34 | /// 35 | /// Base Grunt command 36 | /// 37 | private static OleMenuCommand baseCommand; 38 | 39 | /// 40 | /// Dictionary of currently running processes 41 | /// 42 | private static Dictionary processes; 43 | 44 | /// 45 | /// Last clicked file. Used to avoid reevaluating continuosly the same file 46 | /// 47 | private string lastFile; 48 | 49 | /// 50 | /// The DTE object of Visual Studio 51 | /// 52 | private static DTE2 dte; 53 | 54 | private static string exclusionRegex { get; set; } 55 | 56 | /// 57 | /// Default constructor of the package. 58 | /// Inside this method you can place any initialization code that does not require 59 | /// any Visual Studio service because at this point the package object is created but 60 | /// not sited yet inside Visual Studio environment. The place to do all the other 61 | /// initialization is the Initialize method. 62 | /// 63 | public GruntLauncherPackage() 64 | { 65 | processes = new Dictionary(); 66 | } 67 | 68 | ///////////////////////////////////////////////////////////////////////////// 69 | // Overridden Package Implementation 70 | #region Package Members 71 | 72 | /// 73 | /// Initialization of the package; this method is called right after the package is sited, so this is the place 74 | /// where you can put all the initialization code that rely on services provided by VisualStudio. 75 | /// 76 | protected override void Initialize() 77 | { 78 | base.Initialize(); 79 | dte = GetService(typeof(DTE)) as DTE2; 80 | 81 | DTE env = (DTE)GetService(typeof(DTE)); 82 | 83 | EnvDTE.Properties props = env.get_Properties("Grunt Launcher", "General"); 84 | 85 | exclusionRegex = (string)props.Item("TaskRegex").Value; 86 | 87 | // Add our command handlers for menu (commands must exist in the .vsct file) 88 | OleMenuCommandService mcs = GetService(typeof(IMenuCommandService)) as OleMenuCommandService; 89 | if (null != mcs) 90 | { 91 | // Create the command for the menu item. 92 | CommandID cmdGrunt = new CommandID(GuidList.guidGruntLauncherCmdSet, (int)PkgCmdIDList.cmdidGruntLauncher); 93 | OleMenuCommand gruntCommand = new OleMenuCommand(this.GruntCallback, cmdGrunt); 94 | gruntCommand.Visible = false; 95 | gruntCommand.BeforeQueryStatus += GruntBeforeQueryStatus; 96 | baseCommand = gruntCommand; 97 | mcs.AddCommand(gruntCommand); 98 | 99 | CommandID gulpMenuCommandID = new CommandID(GuidList.guidGruntLauncherCmdSet, (int)PkgCmdIDList.cmdidGulpLauncher); 100 | OleMenuCommand gulpCommand = new OleMenuCommand(this.GulpCallback, gulpMenuCommandID); 101 | gulpCommand.Visible = false; 102 | gulpCommand.BeforeQueryStatus += GulpBeforeQueryStatus; 103 | mcs.AddCommand(gulpCommand); 104 | 105 | CommandID cmdBower = new CommandID(GuidList.guidGruntLauncherCmdSet, (int)PkgCmdIDList.cmdidBowerUpdater); 106 | OleMenuCommand bower = new OleMenuCommand(this.UpdateBower, cmdBower); 107 | bower.BeforeQueryStatus += BowerBeforeQueryStatus; 108 | mcs.AddCommand(bower); 109 | 110 | CommandID cmdNpm = new CommandID(GuidList.guidGruntLauncherCmdSet, (int)PkgCmdIDList.cmdidNpmUpdater); 111 | OleMenuCommand npm = new OleMenuCommand(this.UpdateNpm, cmdNpm); 112 | npm.BeforeQueryStatus += NpmBeforeQueryStatus; 113 | mcs.AddCommand(npm); 114 | 115 | CommandID cmdBowerInstall = new CommandID(GuidList.guidGruntLauncherCmdSet, (int)PkgCmdIDList.cmdidBowerInstaller); 116 | OleMenuCommand bow = new OleMenuCommand(this.InstallBower, cmdBowerInstall); 117 | bow.BeforeQueryStatus += BowerInstallBeforeQueryStatus; 118 | mcs.AddCommand(bow); 119 | } 120 | } 121 | 122 | private void BowerInstallBeforeQueryStatus(object sender, EventArgs e) 123 | { 124 | OleMenuCommand button = (OleMenuCommand)sender; 125 | packageFile = SolutionHelpers.GetSourceFilePath(); 126 | bool isPackage = Path.GetFileName(packageFile).Equals("bower.json", StringComparison.OrdinalIgnoreCase); 127 | button.Visible = isPackage; 128 | } 129 | 130 | private void InstallBower(object sender, EventArgs e) 131 | { 132 | OleMenuCommand button = (OleMenuCommand)sender; 133 | string rootDir = new DirectoryInfo(packageFile).Name; 134 | RunProcess(button, " /c \"bower install 2>&1 \" ", false); 135 | } 136 | 137 | #endregion 138 | 139 | #region NPM 140 | 141 | string packageFile; 142 | 143 | private void NpmBeforeQueryStatus(object sender, EventArgs e) 144 | { 145 | OleMenuCommand button = (OleMenuCommand)sender; 146 | packageFile = SolutionHelpers.GetSourceFilePath(); 147 | bool isPackage = Path.GetFileName(packageFile).Equals("package.json", StringComparison.OrdinalIgnoreCase); 148 | button.Visible = isPackage; 149 | } 150 | 151 | private void UpdateNpm(object sender, EventArgs e) 152 | { 153 | OleMenuCommand button = (OleMenuCommand)sender; 154 | string rootDir = new DirectoryInfo(packageFile).Name; 155 | RunProcess(button, " /c \"npm install 2>&1 \" ", false); 156 | } 157 | 158 | #endregion 159 | 160 | #region Bower 161 | 162 | private bool isParent, isChild; 163 | 164 | private void BowerBeforeQueryStatus(object sender, EventArgs e) 165 | { 166 | OleMenuCommand button = (OleMenuCommand)sender; 167 | string path = SolutionHelpers.GetSourceFilePath(); 168 | 169 | isParent = path.EndsWith("bower_components\\", StringComparison.OrdinalIgnoreCase); 170 | 171 | if (isParent) 172 | { 173 | button.Text = "Bower: Update all packages"; 174 | } 175 | else 176 | { 177 | isChild = Directory.GetParent(path).Parent.Name.EndsWith("bower_components", StringComparison.OrdinalIgnoreCase); 178 | button.Text = "Bower: Update " + Directory.GetParent(path).Name; 179 | } 180 | 181 | button.Visible = isParent || isChild; 182 | } 183 | 184 | private void UpdateBower(object sender, EventArgs e) 185 | { 186 | string path = SolutionHelpers.GetSourceFilePath(); 187 | OleMenuCommand button = (OleMenuCommand)sender; 188 | 189 | if (isParent) 190 | { 191 | button.Text = "Update Bower Packages"; 192 | RunProcess(button, " /c \"bower update 2>&1 \" ", true); 193 | } 194 | else if (isChild) 195 | { 196 | string bowerPackage = new DirectoryInfo(path).Name; 197 | RunProcess(button, " /c \"bower update " + bowerPackage + " 2>&1 \" ", true); 198 | } 199 | } 200 | 201 | #endregion 202 | 203 | #region Grunt 204 | 205 | /// 206 | /// Determines if the current file is a gruntfile 207 | /// 208 | /// Boolean that indicates if the clicked file was a gruntfile 209 | private bool IsGruntFile() 210 | { 211 | // gets the full path of the clicked file 212 | var path = SolutionHelpers.GetSourceFilePath(); 213 | 214 | return ((path.ToLower().IndexOf("gruntfile.js") != -1) ||(path.ToLower().IndexOf("gruntfile.ts") != -1) || (path.ToLower().IndexOf("gruntfile.coffee") != -1)); 215 | } 216 | 217 | /// 218 | /// Sets the visibility of the command and creates the dynamic list of commands 219 | /// 220 | /// Sender of the event 221 | /// Event arguments 222 | private void GruntBeforeQueryStatus(object sender, EventArgs e) 223 | { 224 | // gets the full path of the clicked file 225 | var path = SolutionHelpers.GetSourceFilePath(); 226 | 227 | var myCommand = sender as OleMenuCommand; 228 | 229 | // if the currently selected file is a Gruntfile set the command to visible 230 | myCommand.Visible = this.IsGruntFile(); 231 | 232 | 233 | if (!this.IsGruntFile() && !this.IsGulpFile()) 234 | { 235 | this.lastFile = path; 236 | } 237 | 238 | if (!this.IsNewFile()) 239 | { 240 | return; 241 | } 242 | 243 | 244 | 245 | 246 | OleMenuCommandService mcs = GetService(typeof(IMenuCommandService)) as OleMenuCommandService; 247 | 248 | // delete the old command list 249 | if (commands == null) 250 | { 251 | commands = new List(); 252 | } 253 | 254 | foreach (var cmd in commands) 255 | { 256 | mcs.RemoveCommand(cmd); 257 | } 258 | 259 | if (myCommand.Visible) 260 | { 261 | this.lastFile = path; 262 | var list = GruntParser.ReadAllTasks(path); 263 | 264 | myCommand.Text = "Grunt"; 265 | myCommand.Enabled = true; 266 | 267 | if (list.Count == 0) { 268 | myCommand.Enabled = false; 269 | myCommand.Text = "Gruntfile.js not found"; 270 | } 271 | 272 | if (list.Contains("default")) 273 | { 274 | list.Remove("default"); 275 | } 276 | 277 | string n = exclusionRegex; 278 | 279 | Regex a = null; 280 | 281 | if (!string.IsNullOrEmpty(n)) { 282 | try { 283 | a = new Regex(n); 284 | } 285 | catch (Exception) 286 | { 287 | // invalid regex -> ignore 288 | } 289 | 290 | } 291 | 292 | // creates the list of commands 293 | int j = 1; 294 | foreach (var ele in list) 295 | { 296 | if (a != null) 297 | { 298 | if (a.Match(ele).Success) 299 | { 300 | continue; 301 | } 302 | } 303 | 304 | CommandID menuCommandID = new CommandID(GuidList.guidGruntLauncherCmdSet, (int)PkgCmdIDList.cmdidGruntLauncher + j); 305 | j++; 306 | OleMenuCommand command = new OleMenuCommand(this.GruntCallback, menuCommandID); 307 | command.Text = "Grunt: " + ele; 308 | command.BeforeQueryStatus += (x, y) => { (x as OleMenuCommand).Visible = true; }; 309 | commands.Add(command); 310 | mcs.AddCommand(command); 311 | } 312 | } 313 | } 314 | 315 | /// 316 | /// This function is the callback used to execute a command when the a menu item is clicked. 317 | /// See the Initialize method to see how the menu item is associated to this function using 318 | /// the OleMenuCommandService service and the MenuCommand class. 319 | /// 320 | /// Sender of the event 321 | /// Event arguments 322 | private void GruntCallback(object sender, EventArgs e) 323 | { 324 | var cmd = (OleMenuCommand)sender; 325 | var text = cmd.Text; 326 | var task = text.Substring(text.IndexOf(':') + 1).Trim(); 327 | if (task == "Grunt") { task = ""; } 328 | 329 | // if the command is checked it means that there is a running grunt task associated 330 | // so we kill it 331 | if (cmd.Checked) 332 | { 333 | System.Diagnostics.Process pro; 334 | processes.TryGetValue(cmd, out pro); 335 | if (pro != null) 336 | { 337 | OutputHelpers.Output("Stopping process " + cmd.Text); 338 | ProcessHelpers.KillProcessAndChildren(pro.Id); 339 | processes.Remove(cmd); 340 | } 341 | } 342 | 343 | if (!cmd.Checked) 344 | { 345 | // launches the grunt process and redirects the output to the output window 346 | RunProcess(cmd, " /c \"grunt --no-color " + task + " 2>&1 \" ", false); 347 | } 348 | else 349 | { 350 | cmd.Checked = false; 351 | } 352 | } 353 | 354 | #endregion 355 | 356 | #region Gulp 357 | 358 | private bool IsGulpFile() 359 | { 360 | // gets the full path of the clicked file 361 | var path = SolutionHelpers.GetSourceFilePath(); 362 | 363 | return ((path.ToLower().IndexOf("gulpfile.js") != -1) || (path.ToLower().IndexOf("gulpfile.ts") != -1) || (path.ToLower().IndexOf("gulpfile.coffee") != -1)); 364 | } 365 | 366 | private void GulpBeforeQueryStatus(object sender, EventArgs e) 367 | { 368 | // gets the full path of the clicked file 369 | var path = SolutionHelpers.GetSourceFilePath(); 370 | 371 | var myCommand = sender as OleMenuCommand; 372 | myCommand.Visible = this.IsGulpFile(); 373 | 374 | 375 | if (!this.IsNewFile()) 376 | { 377 | return; 378 | } 379 | 380 | OleMenuCommandService mcs = GetService(typeof(IMenuCommandService)) as OleMenuCommandService; 381 | 382 | // delete the old command list 383 | if (commands == null) 384 | { 385 | commands = new List(); 386 | } 387 | 388 | foreach (var cmd in commands) 389 | { 390 | mcs.RemoveCommand(cmd); 391 | } 392 | 393 | if (myCommand.Visible) 394 | { 395 | this.lastFile = path; 396 | 397 | var list = GulpParser.ReadAllTasks(path); 398 | 399 | myCommand.Text = "Gulp"; 400 | myCommand.Enabled = true; 401 | 402 | if (list.Count == 0) 403 | { 404 | myCommand.Enabled = false; 405 | myCommand.Text = "Gulpfile.js not found"; 406 | } 407 | 408 | if (list.Contains("default")) 409 | { 410 | list.Remove("default"); 411 | } 412 | 413 | string n = exclusionRegex; 414 | 415 | Regex a = null; 416 | 417 | if (!string.IsNullOrEmpty(n)) 418 | { 419 | try 420 | { 421 | a = new Regex(n); 422 | } 423 | catch (Exception) 424 | { 425 | // invalid regex -> ignore 426 | } 427 | 428 | } 429 | 430 | // creates the list of commands 431 | int j = 1; 432 | foreach (var ele in list) 433 | { 434 | if (a != null) 435 | { 436 | if (a.Match(ele).Success) 437 | { 438 | continue; 439 | } 440 | } 441 | 442 | 443 | CommandID menuCommandID = new CommandID(GuidList.guidGruntLauncherCmdSet, (int)PkgCmdIDList.cmdidGulpLauncher + j); 444 | j++; 445 | OleMenuCommand command = new OleMenuCommand(this.GulpCallback, menuCommandID); 446 | command.Text = "Gulp: " + ele; 447 | command.BeforeQueryStatus += (x, y) => { (x as OleMenuCommand).Visible = true; }; 448 | commands.Add(command); 449 | mcs.AddCommand(command); 450 | } 451 | } 452 | } 453 | 454 | 455 | private void GulpCallback(object sender, EventArgs e) 456 | { 457 | var cmd = (OleMenuCommand)sender; 458 | var text = cmd.Text; 459 | var task = text.Substring(text.IndexOf(':') + 1).Trim(); 460 | 461 | if (task == "Gulp") { task = ""; } 462 | 463 | // if the command is checked it means that there is a running grunt task associated 464 | // so we kill it 465 | if (cmd.Checked) 466 | { 467 | System.Diagnostics.Process pro; 468 | processes.TryGetValue(cmd, out pro); 469 | if (pro != null) 470 | { 471 | OutputHelpers.Output("Stopping process " + cmd.Text); 472 | ProcessHelpers.KillProcessAndChildren(pro.Id); 473 | processes.Remove(cmd); 474 | } 475 | } 476 | 477 | if (!cmd.Checked) 478 | { 479 | // launches the grunt process and redirects the output to the output window 480 | RunProcess(cmd, " /c \"gulp --no-color " + task + " 2>&1 \" ", false); 481 | } 482 | else 483 | { 484 | cmd.Checked = false; 485 | } 486 | } 487 | 488 | #endregion 489 | 490 | /// 491 | /// Determines if the solution explorer context menu has been opened on a new file since 492 | /// last time 493 | /// 494 | /// Boolean that indicates if a new file was clicked 495 | private bool IsNewFile() 496 | { 497 | // gets the full path of the clicked file 498 | var path = SolutionHelpers.GetSourceFilePath(); 499 | 500 | // optimization to avoid parsing the file again if the clicked file has not changed since last time 501 | if (path == this.lastFile) 502 | { 503 | return false; 504 | } 505 | 506 | return true; 507 | } 508 | 509 | private static void RunProcess(OleMenuCommand cmd, string argument, bool fromRoot) 510 | { 511 | dte.StatusBar.Animate(true, vsStatusAnimation.vsStatusAnimationBuild); 512 | 513 | try 514 | { 515 | System.Diagnostics.ProcessStartInfo procStartInfo = new ProcessStartInfo() 516 | { 517 | RedirectStandardOutput = true, 518 | RedirectStandardError = true, 519 | StandardOutputEncoding = Encoding.UTF8, 520 | StandardErrorEncoding = Encoding.UTF8, 521 | UseShellExecute = false, 522 | CreateNoWindow = true, 523 | WorkingDirectory = fromRoot ? SolutionHelpers.GetRootFolder(dte) : Path.GetDirectoryName(SolutionHelpers.GetSourceFilePath()), 524 | FileName = "cmd", 525 | Arguments = argument, 526 | }; 527 | 528 | System.Diagnostics.Process proc = new System.Diagnostics.Process() 529 | { 530 | StartInfo = procStartInfo, 531 | EnableRaisingEvents = true 532 | }; 533 | 534 | OutputHelpers.Output("Executing " + cmd.Text + " \r\n\r\n", true); 535 | 536 | proc.OutputDataReceived += (object sendingProcess, DataReceivedEventArgs outLine) => OutputHelpers.Output(outLine.Data + "\r\n"); 537 | proc.ErrorDataReceived += (object sendingProcess, DataReceivedEventArgs outLine) => OutputHelpers.Output(outLine.Data + "\r\n"); 538 | proc.Exited += (x, y) => 539 | { 540 | processes.Remove(cmd); 541 | cmd.Checked = false; 542 | dte.StatusBar.Animate(false, vsStatusAnimation.vsStatusAnimationBuild); 543 | }; 544 | 545 | proc.Start(); 546 | 547 | proc.BeginOutputReadLine(); 548 | proc.BeginErrorReadLine(); 549 | 550 | cmd.Checked = true; 551 | 552 | processes.Add(cmd, proc); 553 | } 554 | catch (Exception ex) 555 | { 556 | OutputHelpers.Output(ex.Message); 557 | } 558 | } 559 | } 560 | } 561 | --------------------------------------------------------------------------------