├── RhinoPythonForVisualStudio ├── Key.snk ├── logo.png ├── Resources │ ├── SendCode.png │ ├── ResetEngine.png │ └── SendCodePackage.ico ├── RhinoPythonApplication.zip ├── ProjectTemplates │ └── RhinoPythonApplication.zip ├── app.config ├── Properties │ └── AssemblyInfo.cs ├── source.extension.vsixmanifest ├── packages.config ├── PythonTemplate.cs ├── stylesheet.css ├── ProjectFileEditor.cs ├── SendCodePackage.cs ├── index.html ├── ImportManager.resx ├── VSPackage.resx ├── SendCodePackage.vsct ├── ImportManager.cs ├── ImportManager.Designer.cs ├── SendCode.cs ├── RhinoPythonForVisualStudio.csproj └── RunFunctionCommand.cs ├── template └── RhinoPythonApplication.zip ├── RhinoPythonForVisualStudio.sln ├── .gitattributes ├── README.md └── .gitignore /RhinoPythonForVisualStudio/Key.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jingcheng-chen/RhinoPythonForVS/HEAD/RhinoPythonForVisualStudio/Key.snk -------------------------------------------------------------------------------- /RhinoPythonForVisualStudio/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jingcheng-chen/RhinoPythonForVS/HEAD/RhinoPythonForVisualStudio/logo.png -------------------------------------------------------------------------------- /template/RhinoPythonApplication.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jingcheng-chen/RhinoPythonForVS/HEAD/template/RhinoPythonApplication.zip -------------------------------------------------------------------------------- /RhinoPythonForVisualStudio/Resources/SendCode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jingcheng-chen/RhinoPythonForVS/HEAD/RhinoPythonForVisualStudio/Resources/SendCode.png -------------------------------------------------------------------------------- /RhinoPythonForVisualStudio/Resources/ResetEngine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jingcheng-chen/RhinoPythonForVS/HEAD/RhinoPythonForVisualStudio/Resources/ResetEngine.png -------------------------------------------------------------------------------- /RhinoPythonForVisualStudio/RhinoPythonApplication.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jingcheng-chen/RhinoPythonForVS/HEAD/RhinoPythonForVisualStudio/RhinoPythonApplication.zip -------------------------------------------------------------------------------- /RhinoPythonForVisualStudio/Resources/SendCodePackage.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jingcheng-chen/RhinoPythonForVS/HEAD/RhinoPythonForVisualStudio/Resources/SendCodePackage.ico -------------------------------------------------------------------------------- /RhinoPythonForVisualStudio/ProjectTemplates/RhinoPythonApplication.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jingcheng-chen/RhinoPythonForVS/HEAD/RhinoPythonForVisualStudio/ProjectTemplates/RhinoPythonApplication.zip -------------------------------------------------------------------------------- /RhinoPythonForVisualStudio/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /RhinoPythonForVisualStudio.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RhinoPythonForVisualStudio", "RhinoPythonForVisualStudio\RhinoPythonForVisualStudio.csproj", "{EA5BA645-A8BC-4F50-B91F-223B7DD48CBB}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {EA5BA645-A8BC-4F50-B91F-223B7DD48CBB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {EA5BA645-A8BC-4F50-B91F-223B7DD48CBB}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {EA5BA645-A8BC-4F50-B91F-223B7DD48CBB}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {EA5BA645-A8BC-4F50-B91F-223B7DD48CBB}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /RhinoPythonForVisualStudio/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("RhinoPythonForVisualStudio")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("RhinoPythonForVisualStudio")] 13 | [assembly: AssemblyCopyright("")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 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 Build and Revision Numbers 30 | // by using the '*' as shown below: 31 | // [assembly: AssemblyVersion("1.0.*")] 32 | [assembly: AssemblyVersion("1.0.0.0")] 33 | [assembly: AssemblyFileVersion("1.0.0.0")] 34 | -------------------------------------------------------------------------------- /RhinoPythonForVisualStudio/source.extension.vsixmanifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | RhinoPythonForVisualStudio 6 | RhinoPythonForVisualStudio allows you to code RhinoPython in Visual Studio. 7 | 8 | https://github.com/ccc159/RhinoPythonForVS 9 | logo.png 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /RhinoPythonForVisualStudio/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /RhinoPythonForVisualStudio/PythonTemplate.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace RhinoPythonForVisualStudio 8 | { 9 | public static class PythonTemplate 10 | { 11 | /// 12 | /// The run-function python template string. 13 | /// 14 | public static string RunFunctionTemplate = @"import classes as MOD 15 | import scriptcontext 16 | import rhinoscriptsyntax as rs 17 | 18 | def main(): 19 | try: 20 | name = MOD.CLASSPATHCLASSNAME.DESCRIPTION 21 | except: 22 | name = 'CLASSNAME' 23 | try: 24 | items = MOD.CLASSPATHCLASSNAME.GetInstances('Select [%s]s to process' % name) 25 | bRedraw = rs.EnableRedraw(False) 26 | if not items: return 27 | customFunction(items, name) 28 | finally: 29 | rs.EnableRedraw(bRedraw) 30 | 31 | def customFunction(items, name): 32 | length = len(items) 33 | for i, item in enumerate(items): 34 | rs.Prompt('Processing [%s]s %d/%d' % (name, i+1, length)) 35 | scriptcontext.escape_test() 36 | item.FUNCTIONNAME() 37 | 38 | 39 | if __name__ == '__main__': 40 | main() 41 | "; 42 | /// 43 | /// The print property python template string. 44 | /// 45 | public static string PrintProertyTemplate = @"import classes as MOD 46 | import scriptcontext 47 | import rhinoscriptsyntax as rs 48 | 49 | def main(): 50 | try: 51 | name = MOD.CLASSPATHCLASSNAME.DESCRIPTION 52 | except: 53 | name = 'CLASSNAME' 54 | try: 55 | items = MOD.CLASSPATHCLASSNAME.GetInstances('Select [%s]s to process' % name) 56 | bRedraw = rs.EnableRedraw(False) 57 | if not items: return 58 | customFunction(items, name) 59 | finally: 60 | rs.EnableRedraw(bRedraw) 61 | 62 | def customFunction(items, name): 63 | length = len(items) 64 | for i, item in enumerate(items): 65 | rs.Prompt('Processing [%s]s %d/%d' % (name, i+1, length)) 66 | scriptcontext.escape_test() 67 | print item.FUNCTIONNAME 68 | 69 | 70 | if __name__ == '__main__': 71 | main() 72 | "; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /RhinoPythonForVisualStudio/stylesheet.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | border: 0; 5 | color: #1E1E1E; 6 | font-size: 13px; 7 | font-family: "Segoe UI", Helvetica, Arial, sans-serif; 8 | line-height: 1.45; 9 | word-wrap: break-word; 10 | } 11 | 12 | /* General & 'Reset' Stuff */ 13 | 14 | 15 | .container { 16 | width: 980px; 17 | margin: 0 auto; 18 | } 19 | 20 | section { 21 | display: block; 22 | margin: 0; 23 | } 24 | 25 | h1, h2, h3, h4, h5, h6 { 26 | margin: 0; 27 | } 28 | 29 | /* Header,
30 | header - container 31 | h1 - project name 32 | h2 - project description 33 | */ 34 | 35 | #header { 36 | color: #FFF; 37 | background: #68217a; 38 | position:relative; 39 | } 40 | #hangcloud { 41 | width: 190px; 42 | height: 160px; 43 | background: url("../images/bannerart03.png"); 44 | position: absolute; 45 | top: 0; 46 | right: -30px; 47 | } 48 | h1, h2 { 49 | font-family: "Segoe UI Light", "Segoe UI", Helvetica, Arial, sans-serif; 50 | line-height: 1; 51 | margin: 0 18px; 52 | padding: 0; 53 | } 54 | #header h1 { 55 | font-size: 3.4em; 56 | padding-top: 18px; 57 | font-weight: normal; 58 | margin-left: 15px; 59 | } 60 | 61 | #header h2 { 62 | font-size: 1.5em; 63 | margin-top: 10px; 64 | padding-bottom: 18px; 65 | font-weight: normal; 66 | } 67 | 68 | 69 | #main_content { 70 | width: 100%; 71 | display: flex; 72 | flex-direction: row; 73 | } 74 | 75 | 76 | h1, h2, h3, h4, h5, h6 { 77 | font-weight: bolder; 78 | } 79 | 80 | #main_content h1 { 81 | font-size: 1.8em; 82 | margin-top: 34px; 83 | } 84 | 85 | #main_content h1:first-child { 86 | margin-top: 30px; 87 | } 88 | 89 | #main_content h2 { 90 | font-size: 1.4em; 91 | font-weight: bold; 92 | } 93 | p, ul { 94 | margin: 11px 18px; 95 | } 96 | 97 | #main_content a { 98 | color: #06C; 99 | text-decoration: none; 100 | } 101 | ul { 102 | margin-top: 13px; 103 | margin-left: 18px; 104 | padding-left: 0; 105 | } 106 | ul li { 107 | margin-left: 18px; 108 | padding-left: 0; 109 | } 110 | #lpanel { 111 | width: 620px; 112 | float: left; 113 | } 114 | #rpanel ul { 115 | list-style-type: none; 116 | width: 300px; 117 | } 118 | #rpanel ul li { 119 | line-height: 1.8em; 120 | } 121 | #rpanel { 122 | background: #e7e7e7; 123 | width: 360px; 124 | float: right; 125 | } 126 | 127 | #rpanel div { 128 | width: 300px; 129 | } 130 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /RhinoPythonForVisualStudio/ProjectFileEditor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows.Forms; 7 | using System.Xml; 8 | using Microsoft.VisualStudio.OLE.Interop; 9 | 10 | namespace RhinoPythonForVisualStudio 11 | { 12 | public static class ProjectFileEditor 13 | { 14 | private static bool searchRemoved = false; 15 | private static bool importAdded = false; 16 | /// 17 | /// Edit project file to guarantee there's no search path in the file, and add our import.targets into the file 18 | /// 19 | /// 20 | public static void EditProjectFile(string filePath) 21 | { 22 | XmlDocument Doc = new XmlDocument(); 23 | Doc.Load(filePath); 24 | 25 | // read search path nodes and check if the node is empty 26 | var searchPathNodes = Doc.DocumentElement?.GetElementsByTagName("SearchPath"); 27 | if (searchPathNodes?.Count == 0) searchRemoved = true; 28 | 29 | // read import path nodes and check if import is there 30 | var importPathNodes = Doc.DocumentElement?.GetElementsByTagName("Import"); 31 | if (importPathNodes?.Count > 0) 32 | { 33 | for (int i = 0; i < importPathNodes.Count; i++) 34 | { 35 | var node = importPathNodes[i]; 36 | if (node.Attributes?["Project"].Value == "import.targets") importAdded = true; 37 | } 38 | } 39 | 40 | // either false situation triggers a warning box or return 41 | if (searchRemoved && importAdded) return; 42 | 43 | // show warning box 44 | DialogResult result = LoadReloadingWarningBox(); 45 | if (result == DialogResult.Cancel) return; 46 | 47 | // remove search nodes 48 | if (!searchRemoved) 49 | { 50 | for (int i = 0; i < searchPathNodes?.Count; i++) 51 | { 52 | var node = searchPathNodes[i]; 53 | node.ParentNode?.RemoveChild(node); 54 | } 55 | Doc.Save(filePath); 56 | } 57 | 58 | if (!importAdded) 59 | { 60 | var root = Doc.DocumentElement; 61 | var importNode = Doc.CreateElement("Import", root?.NamespaceURI); 62 | 63 | importNode.SetAttribute("Condition", "Exists('import.targets')"); 64 | importNode.SetAttribute("Project", "import.targets"); 65 | 66 | root?.InsertBefore(importNode,root.FirstChild); 67 | Doc.Save(filePath); 68 | } 69 | 70 | } 71 | 72 | private static DialogResult LoadReloadingWarningBox() 73 | { 74 | var messageBody = "Your project file is not properly configured. This would lead to an import malfunction.\n\n* Click OK to automatically update project configuration.\n (A dialog of 'File Modification Detected' will popup, please click 'Reload' or 'Reload All')\n\n* Click Cancel to change it by yourself."; 75 | var title = "Project Configuration"; 76 | var result = MessageBox.Show(messageBody, title,MessageBoxButtons.OKCancel,MessageBoxIcon.Warning); 77 | return result; 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /RhinoPythonForVisualStudio/SendCodePackage.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // Copyright (c) Company. All rights reserved. 4 | // 5 | //------------------------------------------------------------------------------ 6 | 7 | using System; 8 | using System.ComponentModel.Design; 9 | using System.Diagnostics; 10 | using System.Diagnostics.CodeAnalysis; 11 | using System.Globalization; 12 | using System.Runtime.InteropServices; 13 | using Microsoft.VisualStudio; 14 | using Microsoft.VisualStudio.OLE.Interop; 15 | using Microsoft.VisualStudio.Shell; 16 | using Microsoft.VisualStudio.Shell.Interop; 17 | using Microsoft.Win32; 18 | 19 | namespace RhinoPythonForVisualStudio 20 | { 21 | /// 22 | /// This is the class that implements the package exposed by this assembly. 23 | /// 24 | /// 25 | /// 26 | /// The minimum requirement for a class to be considered a valid package for Visual Studio 27 | /// is to implement the IVsPackage interface and register itself with the shell. 28 | /// This package uses the helper classes defined inside the Managed Package Framework (MPF) 29 | /// to do it: it derives from the Package class that provides the implementation of the 30 | /// IVsPackage interface and uses the registration attributes defined in the framework to 31 | /// register itself and its components with the shell. These attributes tell the pkgdef creation 32 | /// utility what data to put into .pkgdef file. 33 | /// 34 | /// 35 | /// To get loaded into VS, the package must be referred by <Asset Type="Microsoft.VisualStudio.VsPackage" ...> in .vsixmanifest file. 36 | /// 37 | /// 38 | [PackageRegistration(UseManagedResourcesOnly = true)] 39 | [InstalledProductRegistration("#110", "#112", "1.0", IconResourceID = 400)] // Info on this package for Help/About 40 | [ProvideMenuResource("Menus.ctmenu", 1)] 41 | [Guid(SendCodePackage.PackageGuidString)] 42 | [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "pkgdef, VS and vsixmanifest are valid VS terms")] 43 | public sealed class SendCodePackage : Package 44 | { 45 | /// 46 | /// SendCodePackage GUID string. 47 | /// 48 | public const string PackageGuidString = "849c4358-7f18-41aa-9e04-fc50509118b4"; 49 | 50 | /// 51 | /// Initializes a new instance of the class. 52 | /// 53 | public SendCodePackage() 54 | { 55 | // Inside this method you can place any initialization code that does not require 56 | // any Visual Studio service because at this point the package object is created but 57 | // not sited yet inside Visual Studio environment. The place to do all the other 58 | // initialization is the Initialize method. 59 | } 60 | 61 | #region Package Members 62 | 63 | /// 64 | /// Initialization of the package; this method is called right after the package is sited, so this is the place 65 | /// where you can put all the initialization code that rely on services provided by VisualStudio. 66 | /// 67 | protected override void Initialize() 68 | { 69 | SendCode.Initialize(this); 70 | base.Initialize(); 71 | RunFunctionCommand.Initialize(this); 72 | 73 | } 74 | 75 | #endregion 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /RhinoPythonForVisualStudio/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Getting Started 9 | 10 | 11 | 12 |
13 | 17 | 18 |
19 |
20 |

Creating a Visual Studio Extension

21 | 22 |

This project enables developers to create an extension for Visual Studio. The solution contains a VSIX project that packages the extension into a VSIX file. This file is used to install an extension for Visual Studio.

23 |

Add new features

24 | 25 |
    26 |
  1. Right-click the project node in Solution Explorer and select Add>New Item.
  2. 27 |
  3. In the Add New Item dialog box, expand the Extensibility node under Visual C# or Visual Basic.
  4. 28 |
  5. Choose from the available item templates: Visual Studio Package, Editor Items (Classifier, Margin, Text Adornment, Viewport Adornment), Command, Tool Window, Toolbox Control, and then click Add.
  6. 29 |
30 | 31 |

The files for the template that you selected are added to the project. You can start adding functionality to your item template, press F5 to run the project, or add additional item templates.

32 | 33 |

Run and debug

34 |

To run the project, press F5. Visual Studio will:

35 | 36 |
    37 |
  • Build the extension from the VSIX project.
  • 38 |
  • Create a VSIX package from the VSIX project.
  • 39 |
  • When debugging, start an experimental instance of Visual Studio with the VSIX package installed.
  • 40 |
41 | 42 |

In the experimental instance of Visual Studio you can test out the functionality of your extension without affecting your Visual Studio installation.

43 | 44 |
45 |
46 |
47 |

Visual Studio Extensibility Resources

48 | 49 |
    50 |
  1. MSDN documentation
    Detailed documentation and API reference material for building extensions.
  2. 51 |
  3. Extension samples on GitHub
    Use a sample project to kickstart your development.
  4. 52 |
  5. Extensibility chat room on Gitter
    Meet other extension developers and exchange tips and tricks for extension development.
  6. 53 |
  7. Channel 9 videos on extensibility
    Watch videos from the product team on Visual Studio extensibility.
  8. 54 |
  9. Extensibility Tools 2015
    Install an optional helper tool that adds extra IDE support for extension authors.
  10. 55 |
56 |

Give us feedback

57 | 60 |
61 |
62 |
63 |
64 | 65 | 66 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RhinoPythonForVS 2 | 3 | RhinoPythonForVS is a plugin to allow you to code Rhino python script in Visual Studio, the world's best IDE! 4 | 5 | However, if you prefer VS Code, there is a **[RhinoPythonForVSCode](https://github.com/ccc159/RhinoPythonForVscode)** plugin. 6 | 7 | It is a **[DesignToProduction](http://designtoproduction.com/)** open source project, programmed initially for internal use. 8 | 9 | 10 | ## Features 11 | 12 | **Super powerful IntelliSense** in realtime. **Much faster** and responsive than the 13 | [VS Code Demo](https://www.youtube.com/watch?v=QbmnKFIKBYs&feature=youtu.be) 14 | 15 | ## Requirements 16 | 17 | This is the client side of RhinoPython editor. To bridge it to Rhino you need a server to listen to Visual Studio, which is another plugin called [CodeListener](https://github.com/ccc159/CodeListener). 18 | 19 | ## Installation 20 | 21 | 22 | + Make sure you have **[IronPyton 2.7.5](https://github.com/IronLanguages/main/releases/tag/ipy-2.7.5)** installed (.msi file). Note: The latest version 2.7.9 is buggy, tested by author. 23 | + Make sure you have **Visual Studio 2015** installed. Note: VS2017, VS2019 are not tested yet. 24 | + Make sure you have **[PTVS(Python Tools for Visual Studio)](https://docs.microsoft.com/en-us/visualstudio/python/installing-python-support-in-visual-studio?view=vs-2017#visual-studio-2015)** installed. 25 | + Make sure you have **CodeListener** plugin for Rhino in [CodeListener latest release](https://github.com/ccc159/CodeListener/releases) installed. Note: You have to download the **CodeListener.rhi** file for the first time installation. 26 | + Install latest **RhinoPythonForVisualStudio** from **[VisualStudio Market Place](https://marketplace.visualstudio.com/items?itemName=jingchengchen.RhinoPythonForVisualStudio)** or **[Github](https://github.com/ccc159/RhinoPythonForVS/releases)** 27 | 28 | ## Preparation 29 | ### OBSOLETE (No need to prepare this from Version 0.0.3) 30 | + Copy desired **.dll** libraries under `C:\Program Files (x86)\IronPython 2.7\DLLs` folder. *e.g.* Copy **RhinoCommon.dll** from `C:\Program Files\Rhinoceros 5 (64-bit)\System\` and **System.Drawings.dll** from `C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\XXX` 31 | + Copy **all files** from `C:\Users\jch\AppData\Roaming\McNeel\Rhinoceros\5.0\Plug-ins\IronPython (814d908a-e25c-493d-97e9-ee3861957f49)\settings\lib` into `C:\Program Files (x86)\IronPython 2.7\Lib\` folder. Note: the **site-packages** folder seems can't invoke intellisense, tested only by author. 32 | + Start Visual Studio, click **Tools -> Python Tools -> Python Environments**, from the *Python Environments* window on the right side, select **IronPython 2.7**, switch from **Overview** to **IntelliSense**, click **Refresh DB** 33 | 34 | 35 | 36 | ## Usage 37 | 38 | ### OBSOLETE ( SendCode/SendCodeWithoutReset** or **Shift + F2** to run code in Rhino. 43 | + Feel free if you want to [custom you own shortcut for the commands](https://docs.microsoft.com/en-us/visualstudio/ide/identifying-and-customizing-keyboard-shortcuts-in-visual-studio?view=vs-2017). 44 | 45 | ### from Version 0.0.3 46 | + Start Rhino, type command `CodeListener` 47 | + Start Visual Studio, create a new **RhinoPythonApplication** project under **Python**. 48 | + Click **RhinoPython -> ImportManager**, here you can set python lib path, and check referenced DLL. (see help on that window) 49 | + Remember to `Reload Project` from **ImportManager** after first init of project or changed module search paths. 50 | + When finished coding, click **RhinoPython -> SendCode/SendCodeWithoutReset** or **Shift + F2** to run code in Rhino. 51 | 52 | 53 | 54 | 55 | ## Known Issues 56 | 57 | - The debugger has not implemented yet. 58 | - It only supports one Rhino instance at a time. If you want to switch Rhino instance, either close the former Rhino instance or command `StopCodeListener` on the former one. 59 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | [Xx]64/ 19 | [Xx]86/ 20 | [Bb]uild/ 21 | bld/ 22 | [Bb]in/ 23 | [Oo]bj/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | artifacts/ 46 | 47 | *_i.c 48 | *_p.c 49 | *_i.h 50 | *.ilk 51 | *.meta 52 | *.obj 53 | *.pch 54 | *.pdb 55 | *.pgc 56 | *.pgd 57 | *.rsp 58 | *.sbr 59 | *.tlb 60 | *.tli 61 | *.tlh 62 | *.tmp 63 | *.tmp_proj 64 | *.log 65 | *.vspscc 66 | *.vssscc 67 | .builds 68 | *.pidb 69 | *.svclog 70 | *.scc 71 | 72 | # Chutzpah Test files 73 | _Chutzpah* 74 | 75 | # Visual C++ cache files 76 | ipch/ 77 | *.aps 78 | *.ncb 79 | *.opendb 80 | *.opensdf 81 | *.sdf 82 | *.cachefile 83 | *.VC.db 84 | 85 | # Visual Studio profiler 86 | *.psess 87 | *.vsp 88 | *.vspx 89 | *.sap 90 | 91 | # TFS 2012 Local Workspace 92 | $tf/ 93 | 94 | # Guidance Automation Toolkit 95 | *.gpState 96 | 97 | # ReSharper is a .NET coding add-in 98 | _ReSharper*/ 99 | *.[Rr]e[Ss]harper 100 | *.DotSettings.user 101 | 102 | # JustCode is a .NET coding add-in 103 | .JustCode 104 | 105 | # TeamCity is a build add-in 106 | _TeamCity* 107 | 108 | # DotCover is a Code Coverage Tool 109 | *.dotCover 110 | 111 | # NCrunch 112 | _NCrunch_* 113 | .*crunch*.local.xml 114 | nCrunchTemp_* 115 | 116 | # MightyMoose 117 | *.mm.* 118 | AutoTest.Net/ 119 | 120 | # Web workbench (sass) 121 | .sass-cache/ 122 | 123 | # Installshield output folder 124 | [Ee]xpress/ 125 | 126 | # DocProject is a documentation generator add-in 127 | DocProject/buildhelp/ 128 | DocProject/Help/*.HxT 129 | DocProject/Help/*.HxC 130 | DocProject/Help/*.hhc 131 | DocProject/Help/*.hhk 132 | DocProject/Help/*.hhp 133 | DocProject/Help/Html2 134 | DocProject/Help/html 135 | 136 | # Click-Once directory 137 | publish/ 138 | 139 | # Publish Web Output 140 | *.[Pp]ublish.xml 141 | *.azurePubxml 142 | 143 | # TODO: Un-comment the next line if you do not want to checkin 144 | # your web deploy settings because they may include unencrypted 145 | # passwords 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # NuGet Packages 150 | *.nupkg 151 | # The packages folder can be ignored because of Package Restore 152 | **/packages/* 153 | # except build/, which is used as an MSBuild target. 154 | !**/packages/build/ 155 | # Uncomment if necessary however generally it will be regenerated when needed 156 | #!**/packages/repositories.config 157 | # NuGet v3's project.json files produces more ignoreable files 158 | *.nuget.props 159 | *.nuget.targets 160 | 161 | # Microsoft Azure Build Output 162 | csx/ 163 | *.build.csdef 164 | 165 | # Microsoft Azure Emulator 166 | ecf/ 167 | rcf/ 168 | 169 | # Windows Store app package directory 170 | AppPackages/ 171 | BundleArtifacts/ 172 | 173 | # Visual Studio cache files 174 | # files ending in .cache can be ignored 175 | *.[Cc]ache 176 | # but keep track of directories ending in .cache 177 | !*.[Cc]ache/ 178 | 179 | # Others 180 | ClientBin/ 181 | [Ss]tyle[Cc]op.* 182 | ~$* 183 | *~ 184 | *.dbmdl 185 | *.dbproj.schemaview 186 | *.pfx 187 | *.publishsettings 188 | node_modules/ 189 | orleans.codegen.cs 190 | 191 | # RIA/Silverlight projects 192 | Generated_Code/ 193 | 194 | # Backup & report files from converting an old project file 195 | # to a newer Visual Studio version. Backup files are not needed, 196 | # because we have git ;-) 197 | _UpgradeReport_Files/ 198 | Backup*/ 199 | UpgradeLog*.XML 200 | UpgradeLog*.htm 201 | 202 | # SQL Server files 203 | *.mdf 204 | *.ldf 205 | 206 | # Business Intelligence projects 207 | *.rdl.data 208 | *.bim.layout 209 | *.bim_*.settings 210 | 211 | # Microsoft Fakes 212 | FakesAssemblies/ 213 | 214 | # GhostDoc plugin setting file 215 | *.GhostDoc.xml 216 | 217 | # Node.js Tools for Visual Studio 218 | .ntvs_analysis.dat 219 | 220 | # Visual Studio 6 build log 221 | *.plg 222 | 223 | # Visual Studio 6 workspace options file 224 | *.opt 225 | 226 | # Visual Studio LightSwitch build output 227 | **/*.HTMLClient/GeneratedArtifacts 228 | **/*.DesktopClient/GeneratedArtifacts 229 | **/*.DesktopClient/ModelManifest.xml 230 | **/*.Server/GeneratedArtifacts 231 | **/*.Server/ModelManifest.xml 232 | _Pvt_Extensions 233 | 234 | # LightSwitch generated files 235 | GeneratedArtifacts/ 236 | ModelManifest.xml 237 | 238 | # Paket dependency manager 239 | .paket/paket.exe 240 | 241 | # FAKE - F# Make 242 | .fake/ 243 | -------------------------------------------------------------------------------- /RhinoPythonForVisualStudio/ImportManager.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 | 17, 17 122 | 123 | 124 | 149, 17 125 | 126 | -------------------------------------------------------------------------------- /RhinoPythonForVisualStudio/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 | SendCode Extension 133 | 134 | 135 | SendCode Visual Studio Extension Detailed Info 136 | 137 | 138 | Resources\SendCodePackage.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 139 | 140 | -------------------------------------------------------------------------------- /RhinoPythonForVisualStudio/SendCodePackage.vsct: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 25 | 26 | 27 | 28 | 29 | 30 | RhinoPython 31 | RhinoPython 32 | 33 | 34 | 35 | 42 | 43 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 59 | 60 | 67 | 73 | 79 | 85 | 86 | 92 | 98 | 104 | 110 | 111 | 112 | 113 | 114 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | -------------------------------------------------------------------------------- /RhinoPythonForVisualStudio/ImportManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Data; 5 | using System.Drawing; 6 | using System.IO; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Text.RegularExpressions; 10 | using System.Threading.Tasks; 11 | using System.Windows.Forms; 12 | using System.Xml; 13 | using EnvDTE; 14 | using EnvDTE80; 15 | using String = System.String; 16 | 17 | namespace RhinoPythonForVisualStudio 18 | { 19 | public partial class ImportManager : Form 20 | { 21 | public ImportManagerModel ImportModel; 22 | public DLLManagerModel DllModel; 23 | public _DTE dte; 24 | 25 | public ImportManager(string ProjectPath,_DTE _dte) 26 | { 27 | InitializeComponent(); 28 | ImportModel = new ImportManagerModel(ProjectPath); 29 | DllModel = new DLLManagerModel(ProjectPath); 30 | dte = _dte; 31 | } 32 | 33 | private void ImportManager_Load(object sender, EventArgs e) 34 | { 35 | updateListBoxContent(); 36 | updateDllBoxContent(); 37 | } 38 | 39 | private void buttonSave_Click(object sender, EventArgs e) 40 | { 41 | // switch from text box to list box 42 | listBoxPath.Visible = true; 43 | textBoxPath.Visible = false; 44 | // get raw path 45 | var rawText = textBoxPath.Text; 46 | // read its inner text 47 | string[] paths = rawText.Replace("\n","").Replace("\r", "").Split(';'); 48 | // sanitize paths 49 | paths = paths.Where(x => !string.IsNullOrEmpty(x)).ToArray(); 50 | // set path to model 51 | ImportModel.Paths = paths; 52 | // update listbox 53 | updateListBoxContent(); 54 | // save the file 55 | ImportModel.SaveImportFile(); 56 | // disable itself 57 | buttonSave.Enabled = false; 58 | } 59 | 60 | private void updateListBoxContent() 61 | { 62 | listBoxPath.Items.Clear(); 63 | foreach (var path in ImportModel.Paths) listBoxPath.Items.Add(path); 64 | } 65 | 66 | private void updateDllBoxContent() 67 | { 68 | listBoxDll.Items.Clear(); 69 | foreach (var path in DllModel.Paths) listBoxDll.Items.Add(path); 70 | } 71 | 72 | private void buttonEdit_Click(object sender, EventArgs e) 73 | { 74 | // switch from listbox to text box 75 | listBoxPath.Visible = false; 76 | textBoxPath.Visible = true; 77 | // load paths into textbox 78 | textBoxPath.Clear(); 79 | textBoxPath.Text = String.Join(";\r\n", ImportModel.Paths) + ";"; 80 | textBoxPath.Focus(); 81 | // enable save button 82 | buttonSave.Enabled = true; 83 | } 84 | 85 | private void buttonCancel_Click(object sender, EventArgs e) 86 | { 87 | // close the import manager 88 | Close(); 89 | Dispose(); 90 | } 91 | 92 | private void textBoxPath_KeyDown(object sender, KeyEventArgs e) 93 | { 94 | if (e.KeyCode == Keys.Escape) 95 | { 96 | // switch from text box to list box 97 | listBoxPath.Visible = true; 98 | textBoxPath.Visible = false; 99 | } 100 | } 101 | 102 | private void buttonDefault_Click(object sender, EventArgs e) 103 | { 104 | ImportModel.ResetToDefaultPaths(); 105 | // switch from text box to list box 106 | listBoxPath.Visible = true; 107 | textBoxPath.Visible = false; 108 | // update list box 109 | updateListBoxContent(); 110 | // save the file 111 | ImportModel.SaveImportFile(); 112 | } 113 | 114 | private void FortoopTip_MouseHover(object sender, EventArgs e) 115 | { 116 | string tooltip = "This contains user-specific import search paths for IronPython.\n\n"; 117 | tooltip += "For a project, usually:\n"; 118 | tooltip += "* your own python libraries folder.\n"; 119 | tooltip += "* Rhino System folder (for RhinoCommon).\n"; 120 | tooltip += "* rhinoscriptsyntax package (have a look in Rhino's Python Editor (Tools -> Options) to find it.\n"; 121 | tooltip += "* Path of .NET Reference Assemblies.\n\n"; 122 | tooltip += "Be aware that every path has to end with a ';'\n"; 123 | tooltip += "THIS FILE IS PER-USER! Please make sure '*.targets' is in your git ignore list."; 124 | toolTipModule.SetToolTip(FortoopTip, tooltip); 125 | } 126 | 127 | private void forDLLTooltip_MouseHover(object sender, EventArgs e) 128 | { 129 | string tooltip = "Please edit DLL references directly in '__init__.py' file\n"; 130 | tooltip += "under your project root."; 131 | toolTipModule.SetToolTip(forDLLTooltip, tooltip); 132 | } 133 | 134 | private void buttonReloadProject_Click(object sender, EventArgs e) 135 | { 136 | // reload project 137 | 138 | string solutionPath = dte.Solution.FullName; 139 | 140 | dte.ExecuteCommand("File.CloseSolution"); 141 | dte.Solution.Open(solutionPath); 142 | } 143 | } 144 | 145 | /// 146 | /// This is the import manager model that deal with import data 147 | /// 148 | public class ImportManagerModel 149 | { 150 | private static readonly string FileName = @"import.targets"; 151 | private static readonly string TemplateDoc = "\n"+ 152 | "\n"+ 153 | "\n"+ 154 | "\n"+ 155 | "C:\\Program Files\\Rhinoceros 5 (64-bit)\\Plug-ins\\IronPython\\Lib;\n"+ 156 | "\n"+ 157 | "\n"+ 158 | ""; 159 | 160 | private static readonly string[] defaultPaths = {"C:\\Program Files\\Rhinoceros 5 (64-bit)\\Plug-ins\\IronPython\\Lib"}; 161 | public string ImportFilePath; 162 | public string[] Paths; 163 | public XmlDocument Doc; 164 | /// 165 | /// Load import file from target.import 166 | /// 167 | /// a list from path read from target.import 168 | public string[] LoadImportFile() 169 | { 170 | // read import.targets xml file 171 | try 172 | { 173 | Doc.Load(ImportFilePath); 174 | } 175 | catch (System.IO.FileNotFoundException) 176 | { 177 | Doc.LoadXml(TemplateDoc); 178 | Doc.Save(ImportFilePath); 179 | } 180 | 181 | // read search path nodes 182 | XmlNode node = Doc.DocumentElement?.GetElementsByTagName("SearchPath")[0]; 183 | // read its inner text 184 | string[] paths = node?.InnerText.Replace("\n", "").Replace("\r", "").Split(';'); 185 | 186 | // sanitize path 187 | paths = paths?.Where(x => !string.IsNullOrEmpty(x)).ToArray(); 188 | 189 | return paths; 190 | } 191 | 192 | /// 193 | /// Save the file into target.import 194 | /// 195 | public void SaveImportFile() 196 | { 197 | // put new paths into doc 198 | XmlNode node = Doc.DocumentElement?.GetElementsByTagName("SearchPath")[0]; 199 | node.InnerText = String.Join(";\n",Paths) + ";"; 200 | // save 201 | Doc.Save(ImportFilePath); 202 | } 203 | 204 | /// 205 | /// Reset model path to default path 206 | /// 207 | public void ResetToDefaultPaths() 208 | { 209 | Paths = defaultPaths; 210 | } 211 | 212 | 213 | /// 214 | /// Constructor 215 | /// 216 | public ImportManagerModel(string projectPath) 217 | { 218 | 219 | ImportFilePath = Path.Combine(projectPath, FileName); 220 | Doc = new XmlDocument(); 221 | Paths = LoadImportFile(); 222 | 223 | } 224 | } 225 | 226 | public class DLLManagerModel 227 | { 228 | private static readonly string FileName = @"__init__.py"; 229 | 230 | private static readonly string templateDoc = "\"\"\"\n" + 231 | "This file contains references to external DLLs.\n" + 232 | "Feel free to add as required.\n" + 233 | "Please do not put any paths in here, use RhinoPython -> ImportManager instead!\n" + 234 | "\"\"\"\n\n" + 235 | "import clr\n" + 236 | "clr.AddReference(\"RhinoCommon.dll\")"; 237 | 238 | public string Doc; 239 | public string[] Paths; 240 | 241 | /// 242 | /// Load import file from target.import 243 | /// 244 | /// a list from path read from target.import 245 | public DLLManagerModel(string projectPath) 246 | { 247 | var filePath = Path.Combine(projectPath, FileName); 248 | try 249 | { 250 | // read __init__ file 251 | Doc = File.ReadAllText(filePath, Encoding.UTF8); 252 | } 253 | catch (System.IO.FileNotFoundException) 254 | { 255 | // create a init file 256 | Doc = templateDoc; 257 | File.WriteAllText(filePath, Doc); 258 | } 259 | 260 | 261 | // parse the file into paths 262 | var matchedGroups = Regex.Matches(Doc, @"AddReference\(([^)]*)\)"); 263 | 264 | string[] paths = new string[matchedGroups.Count]; 265 | 266 | for (int i = 0; i < matchedGroups.Count; i++) 267 | { 268 | var value = matchedGroups[i].Value; 269 | paths[i] = value.Substring(14, value.Length - 16); 270 | } 271 | 272 | Paths = paths; 273 | } 274 | 275 | } 276 | } 277 | -------------------------------------------------------------------------------- /RhinoPythonForVisualStudio/ImportManager.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace RhinoPythonForVisualStudio 2 | { 3 | partial class ImportManager 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.components = new System.ComponentModel.Container(); 32 | this.label2 = new System.Windows.Forms.Label(); 33 | this.listBoxPath = new System.Windows.Forms.ListBox(); 34 | this.textBoxPath = new System.Windows.Forms.TextBox(); 35 | this.buttonSave = new System.Windows.Forms.Button(); 36 | this.buttonEdit = new System.Windows.Forms.Button(); 37 | this.buttonCancel = new System.Windows.Forms.Button(); 38 | this.buttonDefault = new System.Windows.Forms.Button(); 39 | this.toolTipModule = new System.Windows.Forms.ToolTip(this.components); 40 | this.FortoopTip = new System.Windows.Forms.Label(); 41 | this.listBoxDll = new System.Windows.Forms.ListBox(); 42 | this.label3 = new System.Windows.Forms.Label(); 43 | this.forDLLTooltip = new System.Windows.Forms.Label(); 44 | this.toolTipDll = new System.Windows.Forms.ToolTip(this.components); 45 | this.buttonReloadProject = new System.Windows.Forms.Button(); 46 | this.SuspendLayout(); 47 | // 48 | // label2 49 | // 50 | this.label2.AutoSize = true; 51 | this.label2.Location = new System.Drawing.Point(12, 9); 52 | this.label2.Name = "label2"; 53 | this.label2.Size = new System.Drawing.Size(109, 13); 54 | this.label2.TabIndex = 0; 55 | this.label2.Text = "Module Search Paths"; 56 | // 57 | // listBoxPath 58 | // 59 | this.listBoxPath.FormattingEnabled = true; 60 | this.listBoxPath.HorizontalScrollbar = true; 61 | this.listBoxPath.Items.AddRange(new object[] { 62 | "C:\\Program Files\\Rhinoceros 5 (64-bit)\\Plug-ins\\IronPython\\Lib", 63 | "C:\\Users\\jch\\AppData\\Roaming\\McNeel\\Rhinoceros\\5.0\\Plug-ins\\IronPython (814d908a-" + 64 | "e25c-493d-97e9-ee3861957f49)\\settings\\lib", 65 | "C:\\Users\\jch\\AppData\\Roaming\\McNeel\\Rhinoceros\\5.0\\scripts", 66 | "C:\\Users\\jch\\Documents\\Projects\\D2P_Library"}); 67 | this.listBoxPath.Location = new System.Drawing.Point(15, 34); 68 | this.listBoxPath.Name = "listBoxPath"; 69 | this.listBoxPath.Size = new System.Drawing.Size(472, 212); 70 | this.listBoxPath.TabIndex = 1; 71 | this.listBoxPath.DoubleClick += new System.EventHandler(this.buttonEdit_Click); 72 | // 73 | // textBoxPath 74 | // 75 | this.textBoxPath.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 76 | this.textBoxPath.Location = new System.Drawing.Point(15, 34); 77 | this.textBoxPath.Multiline = true; 78 | this.textBoxPath.Name = "textBoxPath"; 79 | this.textBoxPath.Size = new System.Drawing.Size(472, 212); 80 | this.textBoxPath.TabIndex = 2; 81 | this.textBoxPath.Visible = false; 82 | this.textBoxPath.KeyDown += new System.Windows.Forms.KeyEventHandler(this.textBoxPath_KeyDown); 83 | // 84 | // buttonSave 85 | // 86 | this.buttonSave.Enabled = false; 87 | this.buttonSave.Location = new System.Drawing.Point(376, 262); 88 | this.buttonSave.Name = "buttonSave"; 89 | this.buttonSave.Size = new System.Drawing.Size(54, 28); 90 | this.buttonSave.TabIndex = 3; 91 | this.buttonSave.Text = "Save"; 92 | this.buttonSave.UseVisualStyleBackColor = true; 93 | this.buttonSave.Click += new System.EventHandler(this.buttonSave_Click); 94 | // 95 | // buttonEdit 96 | // 97 | this.buttonEdit.Location = new System.Drawing.Point(319, 262); 98 | this.buttonEdit.Name = "buttonEdit"; 99 | this.buttonEdit.Size = new System.Drawing.Size(54, 28); 100 | this.buttonEdit.TabIndex = 4; 101 | this.buttonEdit.Text = "Edit"; 102 | this.buttonEdit.UseVisualStyleBackColor = true; 103 | this.buttonEdit.Click += new System.EventHandler(this.buttonEdit_Click); 104 | // 105 | // buttonCancel 106 | // 107 | this.buttonCancel.Location = new System.Drawing.Point(433, 262); 108 | this.buttonCancel.Name = "buttonCancel"; 109 | this.buttonCancel.Size = new System.Drawing.Size(54, 28); 110 | this.buttonCancel.TabIndex = 5; 111 | this.buttonCancel.Text = "Cancel"; 112 | this.buttonCancel.UseVisualStyleBackColor = true; 113 | this.buttonCancel.Click += new System.EventHandler(this.buttonCancel_Click); 114 | // 115 | // buttonDefault 116 | // 117 | this.buttonDefault.Location = new System.Drawing.Point(16, 262); 118 | this.buttonDefault.Name = "buttonDefault"; 119 | this.buttonDefault.Size = new System.Drawing.Size(94, 28); 120 | this.buttonDefault.TabIndex = 6; 121 | this.buttonDefault.Text = "Restore Defaults"; 122 | this.buttonDefault.UseVisualStyleBackColor = true; 123 | this.buttonDefault.Click += new System.EventHandler(this.buttonDefault_Click); 124 | // 125 | // toolTipModule 126 | // 127 | this.toolTipModule.AutoPopDelay = 20000; 128 | this.toolTipModule.InitialDelay = 100; 129 | this.toolTipModule.ReshowDelay = 100; 130 | // 131 | // FortoopTip 132 | // 133 | this.FortoopTip.AutoSize = true; 134 | this.FortoopTip.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 135 | this.FortoopTip.ForeColor = System.Drawing.SystemColors.Highlight; 136 | this.FortoopTip.Location = new System.Drawing.Point(116, 9); 137 | this.FortoopTip.Name = "FortoopTip"; 138 | this.FortoopTip.Size = new System.Drawing.Size(13, 13); 139 | this.FortoopTip.TabIndex = 7; 140 | this.FortoopTip.Text = "?"; 141 | this.FortoopTip.MouseHover += new System.EventHandler(this.FortoopTip_MouseHover); 142 | // 143 | // listBoxDll 144 | // 145 | this.listBoxDll.BackColor = System.Drawing.Color.Gainsboro; 146 | this.listBoxDll.FormattingEnabled = true; 147 | this.listBoxDll.HorizontalScrollbar = true; 148 | this.listBoxDll.Location = new System.Drawing.Point(496, 34); 149 | this.listBoxDll.Name = "listBoxDll"; 150 | this.listBoxDll.Size = new System.Drawing.Size(171, 212); 151 | this.listBoxDll.TabIndex = 8; 152 | // 153 | // label3 154 | // 155 | this.label3.AutoSize = true; 156 | this.label3.Location = new System.Drawing.Point(493, 9); 157 | this.label3.Name = "label3"; 158 | this.label3.Size = new System.Drawing.Size(86, 13); 159 | this.label3.TabIndex = 9; 160 | this.label3.Text = "Referenced DLL"; 161 | // 162 | // forDLLTooltip 163 | // 164 | this.forDLLTooltip.AutoSize = true; 165 | this.forDLLTooltip.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 166 | this.forDLLTooltip.ForeColor = System.Drawing.SystemColors.Highlight; 167 | this.forDLLTooltip.Location = new System.Drawing.Point(574, 9); 168 | this.forDLLTooltip.Name = "forDLLTooltip"; 169 | this.forDLLTooltip.Size = new System.Drawing.Size(13, 13); 170 | this.forDLLTooltip.TabIndex = 10; 171 | this.forDLLTooltip.Text = "?"; 172 | this.forDLLTooltip.MouseHover += new System.EventHandler(this.forDLLTooltip_MouseHover); 173 | // 174 | // buttonReloadProject 175 | // 176 | this.buttonReloadProject.Location = new System.Drawing.Point(116, 262); 177 | this.buttonReloadProject.Name = "buttonReloadProject"; 178 | this.buttonReloadProject.Size = new System.Drawing.Size(94, 28); 179 | this.buttonReloadProject.TabIndex = 11; 180 | this.buttonReloadProject.Text = "Reload Project"; 181 | this.buttonReloadProject.UseVisualStyleBackColor = true; 182 | this.buttonReloadProject.Click += new System.EventHandler(this.buttonReloadProject_Click); 183 | // 184 | // ImportManager 185 | // 186 | this.ClientSize = new System.Drawing.Size(682, 309); 187 | this.Controls.Add(this.buttonReloadProject); 188 | this.Controls.Add(this.forDLLTooltip); 189 | this.Controls.Add(this.label3); 190 | this.Controls.Add(this.listBoxDll); 191 | this.Controls.Add(this.FortoopTip); 192 | this.Controls.Add(this.buttonDefault); 193 | this.Controls.Add(this.buttonCancel); 194 | this.Controls.Add(this.buttonEdit); 195 | this.Controls.Add(this.buttonSave); 196 | this.Controls.Add(this.textBoxPath); 197 | this.Controls.Add(this.listBoxPath); 198 | this.Controls.Add(this.label2); 199 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow; 200 | this.Name = "ImportManager"; 201 | this.ShowIcon = false; 202 | this.Text = "Import Manager"; 203 | this.Load += new System.EventHandler(this.ImportManager_Load); 204 | this.ResumeLayout(false); 205 | this.PerformLayout(); 206 | 207 | } 208 | 209 | #endregion 210 | 211 | private System.Windows.Forms.Label label1; 212 | private System.Windows.Forms.Label label2; 213 | private System.Windows.Forms.ListBox listBoxPath; 214 | private System.Windows.Forms.TextBox textBoxPath; 215 | private System.Windows.Forms.Button buttonSave; 216 | private System.Windows.Forms.Button buttonEdit; 217 | private System.Windows.Forms.Button buttonCancel; 218 | private System.Windows.Forms.Button buttonDefault; 219 | private System.Windows.Forms.ToolTip toolTipModule; 220 | private System.Windows.Forms.Label FortoopTip; 221 | private System.Windows.Forms.ListBox listBoxDll; 222 | private System.Windows.Forms.Label label3; 223 | private System.Windows.Forms.Label forDLLTooltip; 224 | private System.Windows.Forms.ToolTip toolTipDll; 225 | private System.Windows.Forms.Button buttonReloadProject; 226 | } 227 | } -------------------------------------------------------------------------------- /RhinoPythonForVisualStudio/SendCode.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // Copyright (c) Company. All rights reserved. 4 | // 5 | //------------------------------------------------------------------------------ 6 | 7 | using System; 8 | using System.ComponentModel.Design; 9 | using System.Globalization; 10 | using System.IO; 11 | using System.Linq; 12 | using System.Net.Mime; 13 | using System.Net.Sockets; 14 | using System.Runtime.Serialization; 15 | using System.Windows.Forms; 16 | using EnvDTE; 17 | using Microsoft.VisualStudio.Shell; 18 | using Microsoft.VisualStudio.Shell.Interop; 19 | using Newtonsoft.Json; 20 | 21 | 22 | namespace RhinoPythonForVisualStudio 23 | { 24 | /// 25 | /// Defines the sending message object structure 26 | /// 27 | [DataContract] 28 | public class msgObject 29 | { 30 | [DataMember] 31 | internal bool run; 32 | [DataMember] 33 | internal bool temp; 34 | [DataMember] 35 | internal bool reset; 36 | [DataMember] 37 | internal string filename; 38 | } 39 | /// 40 | /// Command handler 41 | /// 42 | internal sealed class SendCode 43 | { 44 | /// 45 | /// Command Send Code ID. 46 | /// 47 | public const int CommandSendCodeID = 0x0100; 48 | 49 | /// 50 | /// Command Send Code Without Reset ID. 51 | /// 52 | public const int CommandSendCodeWRID = 0x0101; 53 | 54 | /// 55 | /// Command Import Manager ID. 56 | /// 57 | public const int CommandImportManagerID = 0x0102; 58 | 59 | /// 60 | /// Command menu group (command set GUID). 61 | /// 62 | public static readonly Guid CommandSet = new Guid("14cdd510-7d18-400c-b709-7dd8d532a781"); 63 | /// 64 | /// Output Pane GUID. 65 | /// 66 | public static Guid PaneGuid = new Guid("82BF54A1-9EF7-42E0-9842-0E1D16FF6B8C"); 67 | 68 | /// 69 | /// isRunning Flag 70 | /// 71 | public static bool IsSending = false; 72 | /// 73 | /// VS Package that provides this command, not null. 74 | /// 75 | private readonly Package package; 76 | 77 | private ImportManager manager = null; 78 | 79 | /// 80 | /// Initializes a new instance of the class. 81 | /// Adds our command handlers for menu (commands must exist in the command table file) 82 | /// 83 | /// Owner package, not null. 84 | private SendCode(Package package) 85 | { 86 | if (package == null) 87 | { 88 | throw new ArgumentNullException("package"); 89 | } 90 | 91 | this.package = package; 92 | 93 | OleMenuCommandService commandService = this.ServiceProvider.GetService(typeof(IMenuCommandService)) as OleMenuCommandService; 94 | if (commandService != null) 95 | { 96 | // SendCode 97 | var menuCommandSendCodeID = new CommandID(CommandSet, CommandSendCodeID); 98 | var menuItemSendCode = new MenuCommand( (sender, args) => SendCodeToRhino(sender,true), menuCommandSendCodeID); 99 | commandService.AddCommand(menuItemSendCode); 100 | // SendCodeWithoutReset 101 | var menuCommandSendCodeWRID = new CommandID(CommandSet, CommandSendCodeWRID); 102 | var menuItemSendCodeWR = new MenuCommand((sender, args) => SendCodeToRhino(sender, false), menuCommandSendCodeWRID); 103 | commandService.AddCommand(menuItemSendCodeWR); 104 | // Open ImportManager dialog 105 | var menuCommandImportManagerID = new CommandID(CommandSet, CommandImportManagerID); 106 | var menuItemImportManager = new MenuCommand((sender, args) => OpenImportManagerDialog(), menuCommandImportManagerID); 107 | commandService.AddCommand(menuItemImportManager); 108 | } 109 | } 110 | 111 | /// 112 | /// Gets the instance of the command. 113 | /// 114 | public static SendCode Instance 115 | { 116 | get; 117 | private set; 118 | } 119 | 120 | /// 121 | /// Gets the service provider from the owner package. 122 | /// 123 | private IServiceProvider ServiceProvider 124 | { 125 | get 126 | { 127 | return this.package; 128 | } 129 | } 130 | 131 | /// 132 | /// Initializes the singleton instance of the command. 133 | /// 134 | /// Owner package, not null. 135 | public static void Initialize(Package package) 136 | { 137 | Instance = new SendCode(package); 138 | } 139 | 140 | /// 141 | /// This function is the callback used to execute the command when the menu item is clicked. 142 | /// See the constructor to see how the menu item is associated with this function using 143 | /// OleMenuCommandService service and MenuCommand class. 144 | /// 145 | /// Event sender. 146 | /// Event args. 147 | private void SendCodeToRhino(object sender, bool resetEngine) 148 | { 149 | // bypass the function if the code is running 150 | if (IsSending) 151 | { 152 | Alert("Cannot send code.\nAn existing code is still running."); 153 | return; 154 | } 155 | // run in another thread to send the code 156 | System.Threading.Tasks.Task.Run(() => 157 | { 158 | // set running flag 159 | IsSending = true; 160 | // get the top level vs instance object 161 | var dte = this.ServiceProvider.GetService(typeof(_DTE)) as _DTE; 162 | 163 | if (dte == null) 164 | { 165 | string message = "Cannot init RhinoPython extension.\nTry to restart visual studio."; 166 | Alert(message); 167 | IsSending = false; 168 | return; 169 | } 170 | // save all the documents 171 | dte.Documents.SaveAll(); 172 | 173 | // get the filename with absolute path 174 | var activeDocument = dte.ActiveDocument; 175 | if (activeDocument == null) 176 | { 177 | string message = "Cannot read current document.\nMake sure you have the code opening."; 178 | Alert(message); 179 | IsSending = false; 180 | return; 181 | } 182 | var activeDocumentName = dte.ActiveDocument.FullName; 183 | // check if the file is temp file by checking the if the path of file is inside temp folder 184 | var tempPath = System.IO.Path.GetTempPath(); 185 | var activeDocumentPath = dte.ActiveDocument.Path; 186 | var isTempFile = tempPath == activeDocumentPath; 187 | // compose the message 188 | msgObject objMsg = new msgObject(); 189 | objMsg.filename = activeDocumentName; 190 | objMsg.temp = isTempFile; 191 | objMsg.reset = resetEngine; 192 | objMsg.run = true; 193 | 194 | string sendingMessage = JsonConvert.SerializeObject(objMsg); 195 | Byte[] sendingBytes = System.Text.Encoding.ASCII.GetBytes(sendingMessage); 196 | 197 | // get output panel 198 | var outputPane = GetOutputPane(); 199 | outputPane.Clear(); 200 | outputPane.OutputString($"====== {DateTime.Now.ToString(CultureInfo.CurrentCulture)} ======\n"); 201 | 202 | // init tcp connection 203 | const int portNo = 614; 204 | const string serverIp = "127.0.0.1"; 205 | try 206 | { 207 | TcpClient client = new TcpClient(serverIp, portNo); 208 | 209 | // Get a client stream for reading and writing. 210 | NetworkStream stream = client.GetStream(); 211 | 212 | // Send the message to the connected TcpServer. 213 | stream.Write(sendingBytes, 0, sendingBytes.Length); 214 | 215 | // Receive the TcpServer.response. 216 | bool isConnected = true; 217 | while (isConnected) 218 | { 219 | // Buffer to store the response bytes. 220 | var data = new Byte[256]; 221 | 222 | // Read the the TcpServer response bytes. 223 | Int32 bytes = stream.Read(data, 0, data.Length); 224 | 225 | // String to store the response ASCII representation. 226 | string responseData = System.Text.Encoding.ASCII.GetString(data, 0, bytes); 227 | 228 | // display message 229 | 230 | outputPane.OutputString(responseData); 231 | 232 | // Detect if client disconnected 233 | if (client.Client.Poll(0, SelectMode.SelectRead)) 234 | { 235 | byte[] buff = new byte[1]; 236 | // Client disconnected 237 | isConnected = client.Client.Receive(buff, SocketFlags.Peek) != 0; 238 | } 239 | } 240 | 241 | // Close everything. 242 | stream.Close(); 243 | client.Close(); 244 | } 245 | 246 | catch (SocketException ex) 247 | { 248 | Alert("Cannot connect Rhino.\nPlease make sure Rhino is running CodeListener."); 249 | outputPane.OutputString("Failed:\n" + ex.Message); 250 | } 251 | catch (Exception ex) 252 | { 253 | Alert("Unexpected Error.\\Please see the error message in the output panel."); 254 | outputPane.OutputString("Failed:\n" + ex.Message); 255 | } 256 | finally 257 | { 258 | // set running flag 259 | IsSending = false; 260 | } 261 | }); 262 | 263 | } 264 | 265 | 266 | /// 267 | /// This opens the import manager 268 | /// 269 | private void OpenImportManagerDialog() 270 | { 271 | // if there is a manager, show it. 272 | if (manager != null && !manager.IsDisposed) 273 | { 274 | manager.Show(); 275 | manager.Focus(); 276 | return; 277 | } 278 | // get the top level vs instance object 279 | var dte = this.ServiceProvider.GetService(typeof(_DTE)) as _DTE; 280 | var projects = dte?.Solution.Projects; 281 | if (projects?.Count > 0) 282 | { 283 | // get current project path 284 | Project activeProject = projects.Item(1); 285 | 286 | // project full path 287 | var projectName = activeProject.FullName; 288 | 289 | // update project file 290 | ProjectFileEditor.EditProjectFile(projectName); 291 | 292 | // run import manager 293 | try 294 | { 295 | manager = new ImportManager(Path.GetDirectoryName(projectName), dte); 296 | manager.Show(); 297 | } 298 | catch (Exception e) 299 | { 300 | Alert("Fatal Error: " + e.Message); 301 | } 302 | 303 | } 304 | else 305 | { 306 | Alert("No active project detected."); 307 | } 308 | } 309 | 310 | /// 311 | /// This is a wrapper function to alert 312 | /// 313 | private void Alert(string message) 314 | { 315 | VsShellUtilities.ShowMessageBox( 316 | this.ServiceProvider, 317 | message, 318 | "", 319 | OLEMSGICON.OLEMSGICON_NOICON, 320 | OLEMSGBUTTON.OLEMSGBUTTON_OK, 321 | OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST); 322 | } 323 | 324 | /// 325 | /// This function gets the RhinoPython OutputPane, if not, then create a new one. 326 | /// 327 | private IVsOutputWindowPane GetOutputPane() 328 | { 329 | // get output window 330 | IVsOutputWindow outWindow = Package.GetGlobalService(typeof(SVsOutputWindow)) as IVsOutputWindow; 331 | if (outWindow == null) return null; 332 | 333 | // get output pane 334 | IVsOutputWindowPane rhinoPythonPane; 335 | outWindow.GetPane(ref PaneGuid, out rhinoPythonPane); 336 | 337 | if (rhinoPythonPane == null) 338 | { 339 | outWindow.CreatePane(ref PaneGuid, "RhinoPython", 1, 1); 340 | } 341 | 342 | outWindow.GetPane(ref PaneGuid, out rhinoPythonPane); 343 | // activate pane 344 | rhinoPythonPane.Activate(); 345 | // return pane 346 | return rhinoPythonPane; 347 | } 348 | } 349 | } 350 | -------------------------------------------------------------------------------- /RhinoPythonForVisualStudio/RhinoPythonForVisualStudio.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 14.0 6 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 7 | 8 | 9 | true 10 | 11 | 12 | true 13 | 14 | 15 | Key.snk 16 | 17 | 18 | 19 | Debug 20 | AnyCPU 21 | 2.0 22 | {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 23 | {EA5BA645-A8BC-4F50-B91F-223B7DD48CBB} 24 | Library 25 | Properties 26 | RhinoPythonForVisualStudio 27 | RhinoPythonForVisualStudio 28 | v4.5.2 29 | true 30 | true 31 | true 32 | true 33 | true 34 | false 35 | 36 | 37 | true 38 | full 39 | false 40 | bin\Debug\ 41 | DEBUG;TRACE 42 | prompt 43 | 4 44 | 45 | 46 | pdbonly 47 | true 48 | bin\Release\ 49 | TRACE 50 | prompt 51 | 4 52 | 53 | 54 | 55 | Form 56 | 57 | 58 | ImportManager.cs 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | Always 73 | true 74 | 75 | 76 | Always 77 | 78 | 79 | Designer 80 | 81 | 82 | 83 | 84 | 85 | Menus.ctmenu 86 | Designer 87 | 88 | 89 | Always 90 | true 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | False 101 | 102 | 103 | False 104 | 105 | 106 | False 107 | 108 | 109 | False 110 | 111 | 112 | 113 | 114 | False 115 | 116 | 117 | ..\packages\Microsoft.VisualStudio.Imaging.14.3.25407\lib\net45\Microsoft.VisualStudio.Imaging.dll 118 | True 119 | 120 | 121 | ..\packages\Microsoft.VisualStudio.OLE.Interop.7.10.6070\lib\Microsoft.VisualStudio.OLE.Interop.dll 122 | True 123 | 124 | 125 | ..\packages\Microsoft.VisualStudio.Shell.14.0.14.3.25407\lib\Microsoft.VisualStudio.Shell.14.0.dll 126 | True 127 | 128 | 129 | ..\packages\Microsoft.VisualStudio.Shell.Immutable.10.0.10.0.30319\lib\net40\Microsoft.VisualStudio.Shell.Immutable.10.0.dll 130 | True 131 | 132 | 133 | ..\packages\Microsoft.VisualStudio.Shell.Immutable.11.0.11.0.50727\lib\net45\Microsoft.VisualStudio.Shell.Immutable.11.0.dll 134 | True 135 | 136 | 137 | ..\packages\Microsoft.VisualStudio.Shell.Immutable.12.0.12.0.21003\lib\net45\Microsoft.VisualStudio.Shell.Immutable.12.0.dll 138 | True 139 | 140 | 141 | ..\packages\Microsoft.VisualStudio.Shell.Immutable.14.0.14.3.25407\lib\net45\Microsoft.VisualStudio.Shell.Immutable.14.0.dll 142 | True 143 | 144 | 145 | ..\packages\Microsoft.VisualStudio.Shell.Interop.7.10.6071\lib\Microsoft.VisualStudio.Shell.Interop.dll 146 | True 147 | 148 | 149 | True 150 | ..\packages\Microsoft.VisualStudio.Shell.Interop.10.0.10.0.30319\lib\Microsoft.VisualStudio.Shell.Interop.10.0.dll 151 | True 152 | 153 | 154 | True 155 | ..\packages\Microsoft.VisualStudio.Shell.Interop.11.0.11.0.61030\lib\Microsoft.VisualStudio.Shell.Interop.11.0.dll 156 | True 157 | 158 | 159 | True 160 | ..\packages\Microsoft.VisualStudio.Shell.Interop.12.0.12.0.30110\lib\Microsoft.VisualStudio.Shell.Interop.12.0.dll 161 | True 162 | 163 | 164 | ..\packages\Microsoft.VisualStudio.Shell.Interop.8.0.8.0.50727\lib\Microsoft.VisualStudio.Shell.Interop.8.0.dll 165 | True 166 | 167 | 168 | ..\packages\Microsoft.VisualStudio.Shell.Interop.9.0.9.0.30729\lib\Microsoft.VisualStudio.Shell.Interop.9.0.dll 169 | True 170 | 171 | 172 | ..\packages\Microsoft.VisualStudio.TextManager.Interop.7.10.6070\lib\Microsoft.VisualStudio.TextManager.Interop.dll 173 | True 174 | 175 | 176 | ..\packages\Microsoft.VisualStudio.TextManager.Interop.8.0.8.0.50727\lib\Microsoft.VisualStudio.TextManager.Interop.8.0.dll 177 | True 178 | 179 | 180 | ..\packages\Microsoft.VisualStudio.Threading.14.1.111\lib\net45\Microsoft.VisualStudio.Threading.dll 181 | True 182 | 183 | 184 | ..\packages\Microsoft.VisualStudio.Utilities.14.3.25407\lib\net45\Microsoft.VisualStudio.Utilities.dll 185 | True 186 | 187 | 188 | ..\packages\Microsoft.VisualStudio.Validation.14.1.111\lib\net45\Microsoft.VisualStudio.Validation.dll 189 | True 190 | 191 | 192 | ..\packages\Newtonsoft.Json.12.0.1\lib\net45\Newtonsoft.Json.dll 193 | True 194 | 195 | 196 | False 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | ImportManager.cs 209 | 210 | 211 | true 212 | VSPackage 213 | 214 | 215 | 216 | 217 | 218 | 219 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 220 | 221 | 222 | 223 | 224 | 225 | 232 | -------------------------------------------------------------------------------- /RhinoPythonForVisualStudio/RunFunctionCommand.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // Copyright (c) Company. All rights reserved. 4 | // 5 | //------------------------------------------------------------------------------ 6 | 7 | using System; 8 | using System.Collections.Generic; 9 | using System.ComponentModel.Design; 10 | using System.Globalization; 11 | using System.IO; 12 | using System.Linq; 13 | using System.Net.Sockets; 14 | using System.Text; 15 | using System.Text.RegularExpressions; 16 | using EnvDTE; 17 | using Microsoft.VisualStudio.Shell; 18 | using Microsoft.VisualStudio.Shell.Interop; 19 | using Newtonsoft.Json; 20 | 21 | namespace RhinoPythonForVisualStudio 22 | { 23 | /// 24 | /// Command handler 25 | /// 26 | internal sealed class RunFunctionCommand 27 | { 28 | /// 29 | /// Command Run function ID. 30 | /// 31 | public const int CommandRunFunctionId = 256; 32 | 33 | /// 34 | /// Command Print Property ID. 35 | /// 36 | public const int CommandPrintPropertyId = 257; 37 | 38 | /// 39 | /// Command Reset and Run Function ID. 40 | /// 41 | public const int CommandResetRunFunctionId = 258; 42 | 43 | /// 44 | /// Command Reset and Print Property ID. 45 | /// 46 | public const int CommandResetPrintPropertyId = 259; 47 | 48 | /// 49 | /// Command menu group (command set GUID). 50 | /// 51 | public static readonly Guid CommandSet = new Guid("16554048-73ad-4f0d-921a-d42c45514a41"); 52 | 53 | /// 54 | /// VS Package that provides this command, not null. 55 | /// 56 | private readonly Package package; 57 | 58 | /// 59 | /// isRunning Flag 60 | /// 61 | public static bool IsSending = false; 62 | 63 | private bool isOldPropertySyntax = false; 64 | 65 | /// 66 | /// Output Pane GUID. 67 | /// 68 | public static Guid PaneGuid = new Guid("82BF54A1-9EF7-42E0-9842-0E1D16FF6B8C"); 69 | 70 | private int initLineIndex; 71 | private string tempFileName = "_temp_ui_script.py"; 72 | 73 | /// 74 | /// Initializes a new instance of the class. 75 | /// Adds our command handlers for menu (commands must exist in the command table file) 76 | /// 77 | /// Owner package, not null. 78 | private RunFunctionCommand(Package package) 79 | { 80 | if (package == null) 81 | { 82 | throw new ArgumentNullException("package"); 83 | } 84 | 85 | this.package = package; 86 | 87 | OleMenuCommandService commandService = this.ServiceProvider.GetService(typeof(IMenuCommandService)) as OleMenuCommandService; 88 | if (commandService != null) 89 | { 90 | // ====== no reset ===== 91 | // run function 92 | var menuCommandRunFunctionID = new CommandID(CommandSet, CommandRunFunctionId); 93 | var menuRunFunctionItem = new MenuCommand((sender, args) => RunFunctionCallback(sender, false, false), menuCommandRunFunctionID); 94 | commandService.AddCommand(menuRunFunctionItem); 95 | // print property 96 | var menuCommandPrintPropertyID = new CommandID(CommandSet, CommandPrintPropertyId); 97 | var menuPrintPropertyItem = new MenuCommand((sender, args) => RunFunctionCallback(sender, true, false), menuCommandPrintPropertyID); 98 | commandService.AddCommand(menuPrintPropertyItem); 99 | 100 | // ====== reset ====== 101 | // run function 102 | var menuCommandResetRunFunctionID = new CommandID(CommandSet, CommandResetRunFunctionId); 103 | var menuResetRunFunctionItem = new MenuCommand((sender, args) => RunFunctionCallback(sender, false, true), menuCommandResetRunFunctionID); 104 | commandService.AddCommand(menuResetRunFunctionItem); 105 | // print property 106 | var menuCommandResetPrintPropertyID = new CommandID(CommandSet, CommandResetPrintPropertyId); 107 | var menuResetPrintPropertyItem = new MenuCommand((sender, args) => RunFunctionCallback(sender, true, true), menuCommandResetPrintPropertyID); 108 | commandService.AddCommand(menuResetPrintPropertyItem); 109 | 110 | } 111 | } 112 | 113 | /// 114 | /// Gets the instance of the command. 115 | /// 116 | public static RunFunctionCommand Instance 117 | { 118 | get; 119 | private set; 120 | } 121 | 122 | /// 123 | /// Gets the service provider from the owner package. 124 | /// 125 | private IServiceProvider ServiceProvider 126 | { 127 | get 128 | { 129 | return this.package; 130 | } 131 | } 132 | 133 | /// 134 | /// Initializes the singleton instance of the command. 135 | /// 136 | /// Owner package, not null. 137 | public static void Initialize(Package package) 138 | { 139 | Instance = new RunFunctionCommand(package); 140 | } 141 | 142 | /// 143 | /// This function is the callback used to execute the command when the menu item is clicked. 144 | /// See the constructor to see how the menu item is associated with this function using 145 | /// OleMenuCommandService service and MenuCommand class. 146 | /// 147 | /// Event sender. 148 | /// Event args. 149 | private void RunFunctionCallback(object sender, bool isProperty, bool reset) 150 | { 151 | // get function name, class name, class path 152 | var funcName = GetFunctionName(isProperty); 153 | if (funcName == null) return; 154 | var className = GetClassName(); 155 | if (className == null) return; 156 | var classPath = GetClassPath(); 157 | string pythonTemplate; 158 | 159 | // print property 160 | if (isProperty) 161 | { 162 | pythonTemplate = PythonTemplate.PrintProertyTemplate 163 | .Replace("CLASSPATH", classPath) 164 | .Replace("CLASSNAME", className) 165 | .Replace("FUNCTIONNAME", funcName); 166 | } 167 | // run function 168 | else 169 | { 170 | pythonTemplate = PythonTemplate.RunFunctionTemplate 171 | .Replace("CLASSPATH", classPath) 172 | .Replace("CLASSNAME", className) 173 | .Replace("FUNCTIONNAME", funcName); 174 | } 175 | 176 | // save python file to root folder of the project 177 | saveTempFile(pythonTemplate); 178 | 179 | // run the file through code listener 180 | SendCodeToRhino(reset); 181 | 182 | } 183 | 184 | /// 185 | /// This is a wrapper function to alert 186 | /// 187 | private void Alert(string message) 188 | { 189 | VsShellUtilities.ShowMessageBox( 190 | this.ServiceProvider, 191 | message, 192 | "", 193 | OLEMSGICON.OLEMSGICON_NOICON, 194 | OLEMSGBUTTON.OLEMSGBUTTON_OK, 195 | OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST); 196 | } 197 | 198 | /// 199 | /// Return current function name at the right click point 200 | /// 201 | /// 202 | internal string GetFunctionName(bool isProperty) 203 | { 204 | // set old property syntax default to false 205 | isOldPropertySyntax = false; 206 | // get the top level vs instance object 207 | var dte = this.ServiceProvider.GetService(typeof(_DTE)) as _DTE; 208 | 209 | if (dte == null) 210 | { 211 | string message = "Cannot init RhinoPython extension.\nTry to restart visual studio."; 212 | Alert(message); 213 | return null; 214 | } 215 | // save all the documents 216 | dte.Documents.SaveAll(); 217 | 218 | // get current line text 219 | var selection = (TextSelection)dte.ActiveDocument.Selection; 220 | initLineIndex = selection.CurrentLine; 221 | 222 | selection.SelectLine(); 223 | string text = selection.Text.Trim(); 224 | while (text.EndsWith(",")) 225 | { 226 | selection.SelectLine(); // select line automatically moves the cursor to next line 227 | text += selection.Text.Trim(); 228 | } 229 | 230 | selection.GotoLine(initLineIndex); 231 | 232 | // get function name if it matches the pattern, otherwise null 233 | var funcName = matchFunctionPatternName(text); 234 | if (funcName == null) 235 | { 236 | if (isProperty) funcName = matchProperyPatternName(text); 237 | if (funcName == null) 238 | { 239 | Alert($"This is not a valid python {(isProperty ? "property" : "function")}"); 240 | return null; 241 | } 242 | } 243 | 244 | // check function parameters 245 | if (!isOldPropertySyntax) 246 | { 247 | if (CheckExtraParams(text)) return null; 248 | } 249 | 250 | 251 | return funcName; 252 | 253 | } 254 | 255 | /// 256 | /// Return matched function name given a text. Null if no match found. 257 | /// 258 | /// 259 | /// 260 | internal string matchFunctionPatternName(string text) 261 | { 262 | Regex rx = new Regex(@"def\s+\w+\(.*\)\:\s*\#*.*", RegexOptions.Compiled); 263 | // Find matches. 264 | MatchCollection matches = rx.Matches(text); 265 | if (matches.Count == 0) 266 | { 267 | return null; 268 | } 269 | 270 | // get function name 271 | rx = new Regex(@"def\s+\w+\(", RegexOptions.Compiled); 272 | matches = rx.Matches(text); 273 | var value = matches[0].Groups[0].Value; 274 | var funcName = value.Remove(value.Length - 1).Remove(0, 3).Trim(); 275 | 276 | return funcName; 277 | } 278 | 279 | internal string matchProperyPatternName(string text) 280 | { 281 | Regex rx = new Regex(@"\w+\s*=\s*property\(", RegexOptions.Compiled); 282 | // Find matches. 283 | MatchCollection matches = rx.Matches(text); 284 | if (matches.Count == 0) 285 | { 286 | return null; 287 | } 288 | // set old property syntax flag to true 289 | isOldPropertySyntax = true; 290 | // get property name 291 | rx = new Regex(@"\w+\s*=", RegexOptions.Compiled); 292 | matches = rx.Matches(text); 293 | var value = matches[0].Groups[0].Value; 294 | var propName = value.Remove(value.Length - 1).Trim(); 295 | 296 | return propName; 297 | } 298 | 299 | /// 300 | /// Return matched class name given a text. Null if no match found. 301 | /// 302 | /// 303 | /// 304 | internal string matchClassPatternName(string text) 305 | { 306 | Regex rx = new Regex(@"class\s+\w+\(.*\)\:\s*\#*.*", RegexOptions.Compiled); 307 | // Find matches. 308 | MatchCollection matches = rx.Matches(text); 309 | if (matches.Count == 0) 310 | { 311 | return null; 312 | } 313 | // get function name 314 | rx = new Regex(@"class\s+\w+\(", RegexOptions.Compiled); 315 | matches = rx.Matches(text); 316 | var value = matches[0].Groups[0].Value; 317 | var className = value.Remove(value.Length - 1).Remove(0, 5).Trim(); 318 | 319 | return className; 320 | } 321 | 322 | internal bool CheckExtraParams(string text) 323 | { 324 | var rx = new Regex(@"\(.*\)", RegexOptions.Compiled); 325 | var matches = rx.Matches(text); 326 | var value = matches[0].Groups[0].Value; 327 | // remove brackets 328 | value = value.Remove(value.Length - 1, 1).Remove(0, 1); 329 | var splits = value.Split(','); 330 | var parameters = splits.ToList(); 331 | parameters.RemoveAt(0); 332 | 333 | var extraParams = parameters.Where(p => !p.Contains("=")).ToList(); 334 | if (extraParams.Count <= 0) return false; 335 | Alert( 336 | $"Run Function only support optional parameters.\n\n[{string.Join(",", extraParams).Trim()}] don't have default values."); 337 | return true; 338 | 339 | } 340 | 341 | 342 | /// 343 | /// Get current class name by looking upwards in the document 344 | /// 345 | internal string GetClassName() 346 | { 347 | // get the top level vs instance object 348 | var dte = this.ServiceProvider.GetService(typeof(_DTE)) as _DTE; 349 | // get current line text 350 | var selection = (TextSelection)dte.ActiveDocument.Selection; 351 | 352 | while (true) 353 | { 354 | selection.GotoLine(selection.CurrentLine-1, true); 355 | string text = selection.Text.Trim(); 356 | text = matchClassPatternName(text); 357 | if (text != null) 358 | { 359 | selection.GotoLine(initLineIndex); 360 | return text; 361 | }; 362 | if (selection.CurrentLine == 1) 363 | { 364 | Alert("Cannot find the class name."); 365 | selection.GotoLine(initLineIndex); 366 | return null; 367 | } 368 | } 369 | } 370 | 371 | /// 372 | /// Return chained class path string for composing python script 373 | /// 374 | /// 375 | internal string GetClassPath() 376 | { 377 | var absolutePath = GetFileAbsolutePath(); 378 | var splits = absolutePath.Split(new String[] { "\\classes\\" }, StringSplitOptions.None); 379 | var pathes = new List(); 380 | if (splits.Length == 0) 381 | { 382 | Alert("This file does not reside in classes folder."); 383 | return ""; 384 | } 385 | 386 | var folders = splits[1]; 387 | splits = folders.Split('\\'); 388 | foreach (var split in splits) 389 | { 390 | if (split != "") pathes.Add(split); 391 | } 392 | string chainedPath = string.Join(".",pathes); 393 | if (chainedPath != "") return chainedPath + "."; 394 | return ""; 395 | } 396 | 397 | /// 398 | /// Return temp file full path 399 | /// 400 | /// 401 | internal string GetTempFilePath() 402 | { 403 | var absolutePath = GetFileAbsolutePath(); 404 | var splits = absolutePath.Split(new String[] { "\\classes\\" }, StringSplitOptions.None); 405 | if (splits.Length == 0) 406 | { 407 | Alert("This file does not reside in classes folder."); 408 | return absolutePath; 409 | } 410 | 411 | return splits[0] + "\\" + tempFileName; 412 | } 413 | 414 | /// 415 | /// Return project absolute path 416 | /// 417 | /// 418 | internal string GetFileAbsolutePath() 419 | { 420 | // get the top level vs instance object 421 | var dte = this.ServiceProvider.GetService(typeof(_DTE)) as _DTE; 422 | 423 | return dte.ActiveDocument.Path; 424 | } 425 | 426 | /// 427 | /// Save given string to temp file 428 | /// 429 | /// 430 | internal void saveTempFile(string text) 431 | { 432 | try 433 | { 434 | System.IO.File.WriteAllText(GetTempFilePath(), text); 435 | } 436 | catch (Exception e) 437 | { 438 | Alert("Run Function failed. Are you running another function at the same time?"); 439 | } 440 | 441 | } 442 | 443 | /// 444 | /// Delete temp file. 445 | /// 446 | internal void deleteTempFile() 447 | { 448 | try 449 | { 450 | File.Delete(GetTempFilePath()); 451 | } 452 | catch (Exception e) 453 | { 454 | Alert("Temp file didn't clean up successfully."); 455 | } 456 | } 457 | 458 | /// 459 | /// NEED TO REFINE. 460 | /// Function to send code to Rhino. 461 | /// 462 | /// 463 | private void SendCodeToRhino(bool resetEngine) 464 | { 465 | // bypass the function if the code is running 466 | if (IsSending) 467 | { 468 | Alert("Cannot send code.\nAn existing code is still running."); 469 | return; 470 | } 471 | // run in another thread to send the code 472 | System.Threading.Tasks.Task.Run(() => 473 | { 474 | // set running flag 475 | IsSending = true; 476 | 477 | // compose the message 478 | msgObject objMsg = new msgObject(); 479 | objMsg.filename = GetTempFilePath(); 480 | objMsg.temp = false; 481 | objMsg.reset = resetEngine; 482 | objMsg.run = true; 483 | 484 | string sendingMessage = JsonConvert.SerializeObject(objMsg); 485 | Byte[] sendingBytes = System.Text.Encoding.ASCII.GetBytes(sendingMessage); 486 | 487 | // get output panel 488 | var outputPane = GetOutputPane(); 489 | outputPane.Clear(); 490 | outputPane.OutputString($"====== {DateTime.Now.ToString(CultureInfo.CurrentCulture)} ======\n"); 491 | 492 | // init tcp connection 493 | const int portNo = 614; 494 | const string serverIp = "127.0.0.1"; 495 | try 496 | { 497 | TcpClient client = new TcpClient(serverIp, portNo); 498 | 499 | // Get a client stream for reading and writing. 500 | NetworkStream stream = client.GetStream(); 501 | 502 | // Send the message to the connected TcpServer. 503 | stream.Write(sendingBytes, 0, sendingBytes.Length); 504 | 505 | // Receive the TcpServer.response. 506 | bool isConnected = true; 507 | while (isConnected) 508 | { 509 | // Buffer to store the response bytes. 510 | var data = new Byte[256]; 511 | 512 | // Read the the TcpServer response bytes. 513 | Int32 bytes = stream.Read(data, 0, data.Length); 514 | 515 | // String to store the response ASCII representation. 516 | string responseData = System.Text.Encoding.ASCII.GetString(data, 0, bytes); 517 | 518 | // display message 519 | 520 | outputPane.OutputString(responseData); 521 | 522 | // Detect if client disconnected 523 | if (client.Client.Poll(0, SelectMode.SelectRead)) 524 | { 525 | byte[] buff = new byte[1]; 526 | // Client disconnected 527 | isConnected = client.Client.Receive(buff, SocketFlags.Peek) != 0; 528 | } 529 | } 530 | 531 | // Close everything. 532 | stream.Close(); 533 | client.Close(); 534 | 535 | // delete the file after it's done. 536 | deleteTempFile(); 537 | } 538 | 539 | catch (SocketException ex) 540 | { 541 | Alert("Cannot connect Rhino.\nPlease make sure Rhino is running CodeListener."); 542 | outputPane.OutputString("Failed:\n" + ex.Message); 543 | } 544 | catch (Exception ex) 545 | { 546 | Alert("Unexpected Error.\\Please see the error message in the output panel."); 547 | outputPane.OutputString("Failed:\n" + ex.Message); 548 | } 549 | finally 550 | { 551 | // set running flag 552 | IsSending = false; 553 | } 554 | }); 555 | 556 | } 557 | 558 | /// 559 | /// NEED TO REFINE. 560 | /// This function gets the RhinoPython OutputPane, if not, then create a new one. 561 | /// 562 | private IVsOutputWindowPane GetOutputPane() 563 | { 564 | // get output window 565 | IVsOutputWindow outWindow = Package.GetGlobalService(typeof(SVsOutputWindow)) as IVsOutputWindow; 566 | if (outWindow == null) return null; 567 | 568 | // get output pane 569 | IVsOutputWindowPane rhinoPythonPane; 570 | outWindow.GetPane(ref PaneGuid, out rhinoPythonPane); 571 | 572 | if (rhinoPythonPane == null) 573 | { 574 | outWindow.CreatePane(ref PaneGuid, "RhinoPython", 1, 1); 575 | } 576 | 577 | outWindow.GetPane(ref PaneGuid, out rhinoPythonPane); 578 | // activate pane 579 | rhinoPythonPane.Activate(); 580 | // return pane 581 | return rhinoPythonPane; 582 | } 583 | 584 | } 585 | } 586 | --------------------------------------------------------------------------------