├── .gitignore ├── CodeListener.sln ├── CodeListener ├── CodeListener.csproj ├── CodeListenerCommand.cs ├── CodeListenerExecute.cs ├── CodeListenerPlugIn.cs ├── CodeListenerVersion.cs ├── EmbeddedResources │ └── plugin-utility.ico ├── FeedbackSender.cs ├── Properties │ └── AssemblyInfo.cs ├── ResetScriptEngine.cs ├── StopCodeListener.cs └── packages.config └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015/2017 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # Visual Studio 2017 auto generated files 33 | Generated\ Files/ 34 | 35 | # MSTest test Results 36 | [Tt]est[Rr]esult*/ 37 | [Bb]uild[Ll]og.* 38 | 39 | # NUNIT 40 | *.VisualState.xml 41 | TestResult.xml 42 | 43 | # Build Results of an ATL Project 44 | [Dd]ebugPS/ 45 | [Rr]eleasePS/ 46 | dlldata.c 47 | 48 | # Benchmark Results 49 | BenchmarkDotNet.Artifacts/ 50 | 51 | # .NET Core 52 | project.lock.json 53 | project.fragment.lock.json 54 | artifacts/ 55 | **/Properties/launchSettings.json 56 | 57 | # StyleCop 58 | StyleCopReport.xml 59 | 60 | # Files built by Visual Studio 61 | *_i.c 62 | *_p.c 63 | *_i.h 64 | *.ilk 65 | *.meta 66 | *.obj 67 | *.iobj 68 | *.pch 69 | *.pdb 70 | *.ipdb 71 | *.pgc 72 | *.pgd 73 | *.rsp 74 | *.sbr 75 | *.tlb 76 | *.tli 77 | *.tlh 78 | *.tmp 79 | *.tmp_proj 80 | *.log 81 | *.vspscc 82 | *.vssscc 83 | .builds 84 | *.pidb 85 | *.svclog 86 | *.scc 87 | 88 | # Chutzpah Test files 89 | _Chutzpah* 90 | 91 | # Visual C++ cache files 92 | ipch/ 93 | *.aps 94 | *.ncb 95 | *.opendb 96 | *.opensdf 97 | *.sdf 98 | *.cachefile 99 | *.VC.db 100 | *.VC.VC.opendb 101 | 102 | # Visual Studio profiler 103 | *.psess 104 | *.vsp 105 | *.vspx 106 | *.sap 107 | 108 | # Visual Studio Trace Files 109 | *.e2e 110 | 111 | # TFS 2012 Local Workspace 112 | $tf/ 113 | 114 | # Guidance Automation Toolkit 115 | *.gpState 116 | 117 | # ReSharper is a .NET coding add-in 118 | _ReSharper*/ 119 | *.[Rr]e[Ss]harper 120 | *.DotSettings.user 121 | 122 | # JustCode is a .NET coding add-in 123 | .JustCode 124 | 125 | # TeamCity is a build add-in 126 | _TeamCity* 127 | 128 | # DotCover is a Code Coverage Tool 129 | *.dotCover 130 | 131 | # AxoCover is a Code Coverage Tool 132 | .axoCover/* 133 | !.axoCover/settings.json 134 | 135 | # Visual Studio code coverage results 136 | *.coverage 137 | *.coveragexml 138 | 139 | # NCrunch 140 | _NCrunch_* 141 | .*crunch*.local.xml 142 | nCrunchTemp_* 143 | 144 | # MightyMoose 145 | *.mm.* 146 | AutoTest.Net/ 147 | 148 | # Web workbench (sass) 149 | .sass-cache/ 150 | 151 | # Installshield output folder 152 | [Ee]xpress/ 153 | 154 | # DocProject is a documentation generator add-in 155 | DocProject/buildhelp/ 156 | DocProject/Help/*.HxT 157 | DocProject/Help/*.HxC 158 | DocProject/Help/*.hhc 159 | DocProject/Help/*.hhk 160 | DocProject/Help/*.hhp 161 | DocProject/Help/Html2 162 | DocProject/Help/html 163 | 164 | # Click-Once directory 165 | publish/ 166 | 167 | # Publish Web Output 168 | *.[Pp]ublish.xml 169 | *.azurePubxml 170 | # Note: Comment the next line if you want to checkin your web deploy settings, 171 | # but database connection strings (with potential passwords) will be unencrypted 172 | *.pubxml 173 | *.publishproj 174 | 175 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 176 | # checkin your Azure Web App publish settings, but sensitive information contained 177 | # in these scripts will be unencrypted 178 | PublishScripts/ 179 | 180 | # NuGet Packages 181 | *.nupkg 182 | # The packages folder can be ignored because of Package Restore 183 | **/[Pp]ackages/* 184 | # except build/, which is used as an MSBuild target. 185 | !**/[Pp]ackages/build/ 186 | # Uncomment if necessary however generally it will be regenerated when needed 187 | #!**/[Pp]ackages/repositories.config 188 | # NuGet v3's project.json files produces more ignorable files 189 | *.nuget.props 190 | *.nuget.targets 191 | 192 | # Microsoft Azure Build Output 193 | csx/ 194 | *.build.csdef 195 | 196 | # Microsoft Azure Emulator 197 | ecf/ 198 | rcf/ 199 | 200 | # Windows Store app package directories and files 201 | AppPackages/ 202 | BundleArtifacts/ 203 | Package.StoreAssociation.xml 204 | _pkginfo.txt 205 | *.appx 206 | 207 | # Visual Studio cache files 208 | # files ending in .cache can be ignored 209 | *.[Cc]ache 210 | # but keep track of directories ending in .cache 211 | !*.[Cc]ache/ 212 | 213 | # Others 214 | ClientBin/ 215 | ~$* 216 | *~ 217 | *.dbmdl 218 | *.dbproj.schemaview 219 | *.jfm 220 | *.pfx 221 | *.publishsettings 222 | orleans.codegen.cs 223 | 224 | # Including strong name files can present a security risk 225 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 226 | #*.snk 227 | 228 | # Since there are multiple workflows, uncomment next line to ignore bower_components 229 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 230 | #bower_components/ 231 | 232 | # RIA/Silverlight projects 233 | Generated_Code/ 234 | 235 | # Backup & report files from converting an old project file 236 | # to a newer Visual Studio version. Backup files are not needed, 237 | # because we have git ;-) 238 | _UpgradeReport_Files/ 239 | Backup*/ 240 | UpgradeLog*.XML 241 | UpgradeLog*.htm 242 | ServiceFabricBackup/ 243 | *.rptproj.bak 244 | 245 | # SQL Server files 246 | *.mdf 247 | *.ldf 248 | *.ndf 249 | 250 | # Business Intelligence projects 251 | *.rdl.data 252 | *.bim.layout 253 | *.bim_*.settings 254 | *.rptproj.rsuser 255 | 256 | # Microsoft Fakes 257 | FakesAssemblies/ 258 | 259 | # GhostDoc plugin setting file 260 | *.GhostDoc.xml 261 | 262 | # Node.js Tools for Visual Studio 263 | .ntvs_analysis.dat 264 | node_modules/ 265 | 266 | # Visual Studio 6 build log 267 | *.plg 268 | 269 | # Visual Studio 6 workspace options file 270 | *.opt 271 | 272 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 273 | *.vbw 274 | 275 | # Visual Studio LightSwitch build output 276 | **/*.HTMLClient/GeneratedArtifacts 277 | **/*.DesktopClient/GeneratedArtifacts 278 | **/*.DesktopClient/ModelManifest.xml 279 | **/*.Server/GeneratedArtifacts 280 | **/*.Server/ModelManifest.xml 281 | _Pvt_Extensions 282 | 283 | # Paket dependency manager 284 | .paket/paket.exe 285 | paket-files/ 286 | 287 | # FAKE - F# Make 288 | .fake/ 289 | 290 | # JetBrains Rider 291 | .idea/ 292 | *.sln.iml 293 | 294 | # CodeRush 295 | .cr/ 296 | 297 | # Python Tools for Visual Studio (PTVS) 298 | __pycache__/ 299 | *.pyc 300 | 301 | # Cake - Uncomment if you are using it 302 | # tools/** 303 | # !tools/packages.config 304 | 305 | # Tabs Studio 306 | *.tss 307 | 308 | # Telerik's JustMock configuration file 309 | *.jmconfig 310 | 311 | # BizTalk build output 312 | *.btp.cs 313 | *.btm.cs 314 | *.odx.cs 315 | *.xsd.cs 316 | 317 | # OpenCover UI analysis results 318 | OpenCover/ 319 | 320 | # Azure Stream Analytics local run output 321 | ASALocalRun/ 322 | 323 | # MSBuild Binary and Structured Log 324 | *.binlog 325 | 326 | # NVidia Nsight GPU debugger configuration file 327 | *.nvuser 328 | 329 | # MFractors (Xamarin productivity tool) working folder 330 | .mfractor/ -------------------------------------------------------------------------------- /CodeListener.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}") = "CodeListener", "CodeListener\CodeListener.csproj", "{8C4235B6-64BC-4508-9166-BEF8AA151085}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug (Rhino 5)|Any CPU = Debug (Rhino 5)|Any CPU 11 | Debug (Rhino 5)|x64 = Debug (Rhino 5)|x64 12 | Debug (Rhino 5)|x86 = Debug (Rhino 5)|x86 13 | Debug (Rhino 6)|Any CPU = Debug (Rhino 6)|Any CPU 14 | Debug (Rhino 6)|x64 = Debug (Rhino 6)|x64 15 | Debug (Rhino 6)|x86 = Debug (Rhino 6)|x86 16 | Release (Rhino 5)|Any CPU = Release (Rhino 5)|Any CPU 17 | Release (Rhino 5)|x64 = Release (Rhino 5)|x64 18 | Release (Rhino 5)|x86 = Release (Rhino 5)|x86 19 | Release (Rhino 6)|Any CPU = Release (Rhino 6)|Any CPU 20 | Release (Rhino 6)|x64 = Release (Rhino 6)|x64 21 | Release (Rhino 6)|x86 = Release (Rhino 6)|x86 22 | EndGlobalSection 23 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 24 | {8C4235B6-64BC-4508-9166-BEF8AA151085}.Debug (Rhino 5)|Any CPU.ActiveCfg = Debug (Rhino 5)|Any CPU 25 | {8C4235B6-64BC-4508-9166-BEF8AA151085}.Debug (Rhino 5)|Any CPU.Build.0 = Debug (Rhino 5)|Any CPU 26 | {8C4235B6-64BC-4508-9166-BEF8AA151085}.Debug (Rhino 5)|x64.ActiveCfg = Debug (Rhino 5)|Any CPU 27 | {8C4235B6-64BC-4508-9166-BEF8AA151085}.Debug (Rhino 5)|x64.Build.0 = Debug (Rhino 5)|Any CPU 28 | {8C4235B6-64BC-4508-9166-BEF8AA151085}.Debug (Rhino 5)|x86.ActiveCfg = Debug (Rhino 5)|Any CPU 29 | {8C4235B6-64BC-4508-9166-BEF8AA151085}.Debug (Rhino 5)|x86.Build.0 = Debug (Rhino 5)|Any CPU 30 | {8C4235B6-64BC-4508-9166-BEF8AA151085}.Debug (Rhino 6)|Any CPU.ActiveCfg = Debug (Rhino 6)|Any CPU 31 | {8C4235B6-64BC-4508-9166-BEF8AA151085}.Debug (Rhino 6)|Any CPU.Build.0 = Debug (Rhino 6)|Any CPU 32 | {8C4235B6-64BC-4508-9166-BEF8AA151085}.Debug (Rhino 6)|x64.ActiveCfg = Debug (Rhino 6)|Any CPU 33 | {8C4235B6-64BC-4508-9166-BEF8AA151085}.Debug (Rhino 6)|x64.Build.0 = Debug (Rhino 6)|Any CPU 34 | {8C4235B6-64BC-4508-9166-BEF8AA151085}.Debug (Rhino 6)|x86.ActiveCfg = Debug (Rhino 6)|Any CPU 35 | {8C4235B6-64BC-4508-9166-BEF8AA151085}.Debug (Rhino 6)|x86.Build.0 = Debug (Rhino 6)|Any CPU 36 | {8C4235B6-64BC-4508-9166-BEF8AA151085}.Release (Rhino 5)|Any CPU.ActiveCfg = Release (Rhino 5)|Any CPU 37 | {8C4235B6-64BC-4508-9166-BEF8AA151085}.Release (Rhino 5)|Any CPU.Build.0 = Release (Rhino 5)|Any CPU 38 | {8C4235B6-64BC-4508-9166-BEF8AA151085}.Release (Rhino 5)|x64.ActiveCfg = Release (Rhino 5)|Any CPU 39 | {8C4235B6-64BC-4508-9166-BEF8AA151085}.Release (Rhino 5)|x64.Build.0 = Release (Rhino 5)|Any CPU 40 | {8C4235B6-64BC-4508-9166-BEF8AA151085}.Release (Rhino 5)|x86.ActiveCfg = Release (Rhino 5)|Any CPU 41 | {8C4235B6-64BC-4508-9166-BEF8AA151085}.Release (Rhino 5)|x86.Build.0 = Release (Rhino 5)|Any CPU 42 | {8C4235B6-64BC-4508-9166-BEF8AA151085}.Release (Rhino 6)|Any CPU.ActiveCfg = Release (Rhino 6)|Any CPU 43 | {8C4235B6-64BC-4508-9166-BEF8AA151085}.Release (Rhino 6)|Any CPU.Build.0 = Release (Rhino 6)|Any CPU 44 | {8C4235B6-64BC-4508-9166-BEF8AA151085}.Release (Rhino 6)|x64.ActiveCfg = Release (Rhino 6)|Any CPU 45 | {8C4235B6-64BC-4508-9166-BEF8AA151085}.Release (Rhino 6)|x64.Build.0 = Release (Rhino 6)|Any CPU 46 | {8C4235B6-64BC-4508-9166-BEF8AA151085}.Release (Rhino 6)|x86.ActiveCfg = Release (Rhino 6)|Any CPU 47 | {8C4235B6-64BC-4508-9166-BEF8AA151085}.Release (Rhino 6)|x86.Build.0 = Release (Rhino 6)|Any CPU 48 | EndGlobalSection 49 | GlobalSection(SolutionProperties) = preSolution 50 | HideSolutionNode = FALSE 51 | EndGlobalSection 52 | GlobalSection(ExtensibilityGlobals) = postSolution 53 | SolutionGuid = {E31AE304-E446-413F-8E04-99A913698FC6} 54 | EndGlobalSection 55 | EndGlobal 56 | -------------------------------------------------------------------------------- /CodeListener/CodeListener.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | 8.0.30703 7 | 2.0 8 | {8C4235B6-64BC-4508-9166-BEF8AA151085} 9 | Library 10 | Properties 11 | CodeListener 12 | CodeListener 13 | v4.8 14 | 512 15 | false 16 | 17 | 18 | 19 | true 20 | full 21 | false 22 | bin\Debug\Rhino5\ 23 | TRACE;DEBUG;RHINO5 24 | prompt 25 | false 26 | 27 | 28 | pdbonly 29 | true 30 | bin\Release\Rhino5\ 31 | TRACE;RHINO5 32 | prompt 33 | 4 34 | 35 | 36 | true 37 | bin\Debug\Rhino6\ 38 | TRACE;DEBUG;RHINO6 39 | full 40 | AnyCPU 41 | prompt 42 | MinimumRecommendedRules.ruleset 43 | false 44 | 45 | 46 | bin\Release\Rhino6\ 47 | TRACE;RHINO6 48 | true 49 | pdbonly 50 | AnyCPU 51 | prompt 52 | MinimumRecommendedRules.ruleset 53 | 54 | 55 | 56 | ..\..\..\..\..\..\..\Program Files\Rhino 7\System\Eto.dll 57 | False 58 | 59 | 60 | ..\..\..\..\..\..\..\Program Files\Rhino 7\Plug-ins\IronPython\IronPython.dll 61 | False 62 | 63 | 64 | 65 | 66 | ..\..\..\..\..\..\..\Program Files\Rhino 7\System\RhinoCommon.dll 67 | False 68 | 69 | 70 | ..\..\..\..\..\..\..\Program Files\Rhino 7\Plug-ins\IronPython\RhinoPythonHost.dll 71 | False 72 | 73 | 74 | 75 | 76 | 77 | 78 | ..\..\..\..\Program Files\Rhino6\System\RhinoCommon.dll 79 | False 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 | 112 | 113 | Copy "$(TargetPath)" "$(TargetDir)$(ProjectName).rhp" 114 | Erase "$(TargetPath)" 115 | 116 | 117 | en-US 118 | 119 | 120 | C:\Program Files\Rhinoceros 5 (64-bit)\System\Rhino.exe 121 | 122 | 123 | Program 124 | 125 | -------------------------------------------------------------------------------- /CodeListener/CodeListenerCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Net; 7 | using System.Net.Http; 8 | using System.Net.Http.Headers; 9 | using System.Net.Sockets; 10 | using System.Runtime.Serialization; 11 | using System.Text; 12 | using Rhino; 13 | using Rhino.Commands; 14 | using Rhino.Geometry; 15 | using System.Runtime.Serialization.Json; 16 | using System.Text.RegularExpressions; 17 | using System.Windows; 18 | using Rhino.Display; 19 | using Rhino.DocObjects; 20 | using Rhino.Runtime; 21 | using Command = Rhino.Commands.Command; 22 | using MessageBox = System.Windows.MessageBox; 23 | #if RHINO6 24 | using Eto.Forms; 25 | #endif 26 | 27 | 28 | namespace CodeListener 29 | { 30 | public class CodeListenerCommand : Command 31 | { 32 | internal static BackgroundWorker _tcpServerWorker; 33 | private RhinoDoc _idoc; 34 | internal static TcpListener _server; 35 | 36 | #if RHINO5 37 | private Application _app; 38 | #endif 39 | 40 | 41 | public CodeListenerCommand() 42 | { 43 | // Rhino only creates one instance of each command class defined in a 44 | // plug-in, so it is safe to store a refence in a static property. 45 | 46 | Instance = this; 47 | } 48 | 49 | ///The only instance of this command. 50 | public static CodeListenerCommand Instance 51 | { 52 | get; private set; 53 | } 54 | 55 | ///The command name as it appears on the Rhino command line. 56 | public override string EnglishName 57 | { 58 | get { return "CodeListener"; } 59 | } 60 | 61 | protected override Result RunCommand(RhinoDoc doc, RunMode mode) 62 | { 63 | _idoc = doc; 64 | CheckLatestVersion(); 65 | #if RHINO5 66 | // Start WPF UI Dispatcher if not running. 67 | if (_app == null) 68 | { 69 | _app = Application.Current ?? new Application { ShutdownMode = ShutdownMode.OnExplicitShutdown }; 70 | } 71 | #endif 72 | // set up the listenner 73 | if (_tcpServerWorker != null && _tcpServerWorker.IsBusy) 74 | { 75 | RhinoApp.WriteLine("VS Code Listener is running.", EnglishName); 76 | return Result.Cancel; 77 | }; 78 | 79 | // Start the worker thread 80 | _tcpServerWorker = new BackgroundWorker(); 81 | _tcpServerWorker.WorkerSupportsCancellation = true; 82 | _tcpServerWorker.DoWork += TcpServerWorkerListening; 83 | _tcpServerWorker.RunWorkerCompleted += TcpServerWorkerRunTcpServerWorkerCompleted; 84 | _tcpServerWorker.RunWorkerAsync(); 85 | 86 | 87 | return Result.Success; 88 | } 89 | 90 | // fire this function when the background worker has stopped 91 | protected void TcpServerWorkerRunTcpServerWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 92 | { 93 | _tcpServerWorker.Dispose(); 94 | _tcpServerWorker = null; 95 | RhinoApp.WriteLine("VS Code Listener stopped. Please run CodeListener again."); 96 | } 97 | 98 | // the main listener function 99 | protected void TcpServerWorkerListening(object sender, DoWorkEventArgs e) 100 | { 101 | //---listen at the specified IP and port no.--- 102 | const int portNo = 614; 103 | IPAddress serverIp = IPAddress.Parse("127.0.0.1"); 104 | if (_server == null) _server = new TcpListener(serverIp, portNo); 105 | try 106 | { 107 | _server.Start(); 108 | RhinoApp.WriteLine("VS Code Listener Started..."); 109 | } 110 | catch (Exception err) 111 | { 112 | RhinoApp.WriteLine("Error start Code Listener. Is other Rhino instance occupying?"); 113 | RhinoApp.WriteLine(err.ToString()); 114 | } 115 | 116 | while (true) 117 | { 118 | // incoming client connected 119 | TcpClient client; 120 | try 121 | { 122 | client = _server.AcceptTcpClient(); 123 | } 124 | catch (Exception serverException) 125 | { 126 | return; 127 | } 128 | 129 | // get the incoming data through a network stream 130 | NetworkStream nwStream = client.GetStream(); 131 | byte[] buffer = new byte[client.ReceiveBufferSize]; 132 | 133 | // read incoming stream 134 | int bytesRead = nwStream.Read(buffer, 0, client.ReceiveBufferSize); 135 | 136 | // convert the data received into a string 137 | StringBuilder msg = new StringBuilder(); 138 | 139 | // parse the buffer into msg 140 | foreach (var b in buffer) 141 | { 142 | if (b.Equals(00)) break; 143 | msg.Append(Convert.ToChar(b).ToString()); 144 | } 145 | 146 | // parse the received message into C# Object 147 | string msgString = msg.ToString(); 148 | msgString = Regex.Split(msgString, "}")[0] + "}"; 149 | MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(msgString)); 150 | DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(msgObject)); 151 | msgObject msgObj; 152 | try 153 | { 154 | msgObj = ser.ReadObject(ms) as msgObject; 155 | } 156 | catch (Exception ex) 157 | { 158 | RhinoApp.WriteLine("Received invalid data, please try again."); 159 | return; 160 | } 161 | 162 | ms.Close(); 163 | 164 | // invoke the main task in the main thread 165 | #if RHINO5 166 | _app.Dispatcher.Invoke(() => 167 | #else 168 | Eto.Forms.Application.Instance.Invoke(() => 169 | #endif 170 | { 171 | // create python script runner 172 | PythonScript myScript = PythonScript.Create(); 173 | // redirect output to _output field 174 | myScript.Output = PrintToVSCode; 175 | FeedbackSender feedbackSender = new FeedbackSender(nwStream); 176 | GotCodeFeekBack += feedbackSender.OnGotCodeFeedBack; 177 | 178 | // if flagged reset, then reset the script engine. 179 | if (msgObj.reset) 180 | { 181 | ResetScriptEngine.ResetEngine(); 182 | } 183 | 184 | // if it is not a temp folder, add the folder to python library path 185 | if (!msgObj.temp) 186 | { 187 | string pythonFilePath = Path.GetDirectoryName(msgObj.filename); 188 | string code = string.Format("import sys\nimport os\nif \"{0}\" not in sys.path: sys.path.append(\"{0}\")", pythonFilePath.Replace("\\", "\\\\")); 189 | try 190 | { 191 | myScript.ExecuteScript(code); 192 | } 193 | catch (Exception exception) 194 | { 195 | PrintToVSCode(exception.Message); 196 | } 197 | } 198 | 199 | // determines if run actual script 200 | if (msgObj.run) 201 | { 202 | uint sn = _idoc.BeginUndoRecord("VS Code execution"); 203 | var sn_start = RhinoObject.NextRuntimeSerialNumber; 204 | try 205 | { 206 | myScript.ExecuteFile(msgObj.filename); 207 | } 208 | catch (Exception ex) 209 | { 210 | // get the exception message 211 | var error = myScript.GetStackTraceFromException(ex); 212 | string message = ex.Message + "\n" + error; 213 | // send exception msg back to VS Code 214 | PrintToVSCode(message); 215 | } 216 | finally 217 | { 218 | CloseConnection(nwStream); 219 | _idoc.EndUndoRecord(sn); 220 | // fix the rs.Prompt bug 221 | RhinoApp.SetCommandPrompt("Command"); 222 | 223 | // select created objects 224 | var sn_end = RhinoObject.NextRuntimeSerialNumber; 225 | if (sn_end > sn_start) 226 | { 227 | for (var i = sn_start; i < sn_end; i++) 228 | { 229 | var obj = _idoc.Objects.Find(i); 230 | if (null != obj) 231 | { 232 | obj.Select(true); 233 | } 234 | } 235 | } 236 | // enable the view 237 | _idoc.Views.RedrawEnabled = true; 238 | } 239 | } 240 | else 241 | { 242 | CloseConnection(nwStream); 243 | } 244 | }); 245 | } 246 | } 247 | 248 | public class GotCodeFeedbackEventArgs : EventArgs 249 | { 250 | public string Message { get; set; } 251 | } 252 | 253 | public delegate void CodeFeedBackEventHandler(object source, GotCodeFeedbackEventArgs e); 254 | 255 | public event CodeFeedBackEventHandler GotCodeFeekBack; 256 | 257 | protected virtual void OnGotCodeFeedBack(GotCodeFeedbackEventArgs e) 258 | { 259 | GotCodeFeekBack?.Invoke(this, e); 260 | } 261 | 262 | // add the action to redirect output to VS Code 263 | protected void PrintToVSCode(string m) 264 | { 265 | RhinoApp.Write(m); 266 | GotCodeFeedbackEventArgs arg = new GotCodeFeedbackEventArgs { Message = m }; 267 | OnGotCodeFeedBack(arg); 268 | } 269 | 270 | // close connection 271 | protected void CloseConnection(NetworkStream stream) 272 | { 273 | stream.Close(); 274 | } 275 | 276 | // check plugin versions 277 | protected async void CheckLatestVersion() 278 | { 279 | // hardcoded github latest releases 280 | string sURL = "https://api.github.com/repos/ccc159/CodeListener/releases/latest"; 281 | string pageURL = "https://github.com/ccc159/CodeListener/releases/latest"; 282 | var client = new HttpClient(); 283 | ServicePointManager.SecurityProtocol = 284 | SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls; 285 | client.DefaultRequestHeaders.Add("User-Agent", "CodeListener"); 286 | try 287 | { 288 | var uri = new Uri(sURL); 289 | Stream respStream = await client.GetStreamAsync(uri); 290 | StreamReader reader = new StreamReader(respStream); 291 | string responseFromServer = reader.ReadToEnd(); 292 | var index = responseFromServer.IndexOf("tag_name", StringComparison.Ordinal); 293 | var version = (responseFromServer.Substring(index + 11, 10).Split('\"')[0]).Split('.'); 294 | // version Major.Minor.Patch -> 0.1.5 295 | int major = Int32.Parse(version[0]); 296 | int minor = Int32.Parse(version[1]); 297 | int patch = Int32.Parse(version[2]); 298 | if (CodeListenerVersion.MAJOR < major || CodeListenerVersion.MINOR < minor || 299 | CodeListenerVersion.PATCH < patch) 300 | { 301 | var msg = $"CodeListener has new a version {major}.{minor}.{patch}! Go to download page?"; 302 | var result = MessageBox.Show(msg, "New Version", MessageBoxButton.OKCancel, 303 | MessageBoxImage.Information); 304 | if (result == MessageBoxResult.OK) System.Diagnostics.Process.Start(pageURL); 305 | } 306 | } 307 | catch (Exception ex) 308 | { 309 | RhinoApp.WriteLine("Check new version failed."); 310 | } 311 | } 312 | } 313 | 314 | // define the message object structure that received from VS Code 315 | [DataContract] 316 | public class msgObject 317 | { 318 | [DataMember] 319 | internal bool run; 320 | [DataMember] 321 | internal bool temp; 322 | [DataMember] 323 | internal bool reset; 324 | [DataMember] 325 | internal string filename; 326 | } 327 | } 328 | -------------------------------------------------------------------------------- /CodeListener/CodeListenerExecute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.IO; 4 | using Rhino; 5 | using Rhino.Commands; 6 | using Rhino.Input; 7 | 8 | namespace CodeListener 9 | { 10 | [System.Runtime.InteropServices.Guid("a6dbd0d2-3ef9-44e7-8867-e0b359b57f38")] 11 | public class CodeListenerExecute : Command 12 | { 13 | static CodeListenerExecute _instance; 14 | public CodeListenerExecute() 15 | { 16 | _instance = this; 17 | } 18 | 19 | ///The only instance of the CodeListenerExecute command. 20 | public static CodeListenerExecute Instance 21 | { 22 | get { return _instance; } 23 | } 24 | 25 | public override string EnglishName 26 | { 27 | get { return "CodeListenerExecute"; } 28 | } 29 | 30 | protected override Result RunCommand(RhinoDoc doc, RunMode mode) 31 | { 32 | // get the path 33 | string path = string.Empty; 34 | RhinoGet.GetString("Absolute path of the exexutable", true, ref path); 35 | if (string.IsNullOrEmpty(path)) 36 | { 37 | RhinoApp.WriteLine("Cancelled."); 38 | return Result.Cancel; 39 | } 40 | // valdiate path 41 | if (!File.Exists(path)) 42 | { 43 | RhinoApp.WriteLine("File path is not valid."); 44 | return Result.Cancel; 45 | } 46 | // run the file 47 | try 48 | { 49 | Process.Start(path); 50 | } 51 | catch (Exception e) 52 | { 53 | RhinoApp.WriteLine(e.Message); 54 | } 55 | 56 | return Result.Success; 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /CodeListener/CodeListenerPlugIn.cs: -------------------------------------------------------------------------------- 1 | namespace CodeListener 2 | { 3 | /// 4 | /// Every RhinoCommon .rhp assembly must have one and only one PlugIn-derived 5 | /// class. DO NOT create instances of this class yourself. It is the 6 | /// responsibility of Rhino to create an instance of this class. 7 | /// To complete plug-in information, please also see all PlugInDescription 8 | /// attributes in AssemblyInfo.cs (you might need to click "Project" -> 9 | /// "Show All Files" to see it in the "Solution Explorer" window). 10 | /// 11 | public class CodeListenerPlugIn : Rhino.PlugIns.PlugIn 12 | 13 | { 14 | public CodeListenerPlugIn() 15 | { 16 | Instance = this; 17 | } 18 | 19 | ///Gets the only instance of the CodeListenerPlugIn plug-in. 20 | public static CodeListenerPlugIn Instance 21 | { 22 | get; private set; 23 | } 24 | 25 | // You can override methods here to change the plug-in behavior on 26 | // loading and shut down, add options pages to the Rhino _Option command 27 | // and maintain plug-in wide options in a document. 28 | } 29 | } -------------------------------------------------------------------------------- /CodeListener/CodeListenerVersion.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows; 3 | using System.Windows.Forms; 4 | using Rhino; 5 | using Rhino.Commands; 6 | using MessageBox = System.Windows.MessageBox; 7 | 8 | namespace CodeListener 9 | { 10 | [System.Runtime.InteropServices.Guid("56773c6e-b5d1-462f-af01-43bed492ef53")] 11 | public class CodeListenerVersion : Command 12 | { 13 | internal static int MAJOR = 0; 14 | internal static int MINOR = 1; 15 | internal static int PATCH = 8; 16 | internal static string Version = $"{MAJOR}.{MINOR}.{PATCH}"; 17 | static CodeListenerVersion _instance; 18 | public CodeListenerVersion() 19 | { 20 | _instance = this; 21 | } 22 | 23 | ///The only instance of the CodeListenerVersion command. 24 | public static CodeListenerVersion Instance 25 | { 26 | get { return _instance; } 27 | } 28 | 29 | public override string EnglishName 30 | { 31 | get { return "CodeListenerVersion"; } 32 | } 33 | 34 | protected override Result RunCommand(RhinoDoc doc, RunMode mode) 35 | { 36 | // TODO: remember to change to versions every time publish a new one!! 37 | RhinoApp.WriteLine($"CodeListener Version: {Version}"); 38 | return Result.Success; 39 | } 40 | 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /CodeListener/EmbeddedResources/plugin-utility.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jingcheng-chen/CodeListener/9186157ab6f380102efceb91712b4a6ba5c74bb0/CodeListener/EmbeddedResources/plugin-utility.ico -------------------------------------------------------------------------------- /CodeListener/FeedbackSender.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net.Sockets; 5 | using System.Text; 6 | 7 | namespace CodeListener 8 | { 9 | class FeedbackSender 10 | { 11 | private NetworkStream stream; 12 | 13 | public FeedbackSender(NetworkStream istream) 14 | { 15 | stream = istream; 16 | } 17 | 18 | public void OnGotCodeFeedBack(object source, CodeListenerCommand.GotCodeFeedbackEventArgs e) 19 | { 20 | // Process the data sent by the client. 21 | byte[] errMsgBytes = Encoding.ASCII.GetBytes(e.Message); 22 | // Send back a response. 23 | try 24 | { 25 | stream.Write(errMsgBytes, 0, errMsgBytes.Length); 26 | } 27 | catch (Exception exception) 28 | { 29 | stream.Close(); 30 | } 31 | } 32 | } 33 | 34 | 35 | } -------------------------------------------------------------------------------- /CodeListener/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | using CodeListener; 5 | using Rhino.PlugIns; 6 | 7 | // Plug-in Description Attributes - all of these are optional. 8 | // These will show in Rhino's option dialog, in the tab Plug-ins. 9 | [assembly: PlugInDescription(DescriptionType.Address, "Seestrasse 78, 8703 Erlenbach/Zürich")] 10 | [assembly: PlugInDescription(DescriptionType.Country, "Switzerland")] 11 | [assembly: PlugInDescription(DescriptionType.Email, "info@designtoproduction.com")] 12 | [assembly: PlugInDescription(DescriptionType.Phone, "+41 (0) 44 914 74 90")] 13 | [assembly: PlugInDescription(DescriptionType.Fax, "+41 (0) 44 914 74 99")] 14 | [assembly: PlugInDescription(DescriptionType.Organization, "Design-to-Production GmbH")] 15 | [assembly: PlugInDescription(DescriptionType.UpdateUrl, "https://github.com/ccc159/CodeListener")] 16 | [assembly: PlugInDescription(DescriptionType.WebSite, "http://designtoproduction.com/")] 17 | 18 | // Icons should be Windows .ico files and contain 32-bit images in the following sizes: 16, 24, 32, 48, and 256. 19 | // This is a Rhino 6-only description. 20 | 21 | // General Information about an assembly is controlled through the following 22 | // set of attributes. Change these attribute values to modify the information 23 | // associated with an assembly. 24 | [assembly: AssemblyTitle("CodeListener")] 25 | 26 | // This will be used also for the plug-in description. 27 | [assembly: AssemblyDescription("CodeListener utility plug-in")] 28 | 29 | [assembly: AssemblyConfiguration("")] 30 | [assembly: AssemblyCompany("Design-To-Production")] 31 | [assembly: AssemblyProduct("CodeListener")] 32 | [assembly: AssemblyCopyright("Copyright © 2018")] 33 | [assembly: AssemblyTrademark("")] 34 | [assembly: AssemblyCulture("")] 35 | 36 | // Setting ComVisible to false makes the types in this assembly not visible 37 | // to COM components. If you need to access a type in this assembly from 38 | // COM, set the ComVisible attribute to true on that type. 39 | [assembly: ComVisible(false)] 40 | 41 | // The following GUID is for the ID of the typelib if this project is exposed to COM 42 | [assembly: Guid("8c4235b6-64bc-4508-9166-bef8aa151085")] // This will also be the Guid of the Rhino plug-in 43 | 44 | // Version information for an assembly consists of the following four values: 45 | // 46 | // Major Version 47 | // Minor Version 48 | // Build Number 49 | // Revision 50 | // 51 | // You can specify all the values or you can default the Build and Revision Numbers 52 | // by using the '*' as shown below: 53 | // [assembly: AssemblyVersion("1.0.*")] 54 | 55 | [assembly: AssemblyVersion("0.1.7")] 56 | [assembly: AssemblyFileVersion("0.1.7")] 57 | 58 | // Make compatible with Rhino Installer Engine 59 | [assembly: AssemblyInformationalVersion("2")] 60 | -------------------------------------------------------------------------------- /CodeListener/ResetScriptEngine.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Text; 4 | using Rhino; 5 | using Rhino.Commands; 6 | 7 | namespace CodeListener 8 | { 9 | [System.Runtime.InteropServices.Guid("78d32084-0d9a-41b4-99f6-83740f67fb48"), 10 | Rhino.Commands.CommandStyle(Rhino.Commands.Style.ScriptRunner)] 11 | public class ResetScriptEngine : Command 12 | { 13 | static ResetScriptEngine _instance; 14 | public ResetScriptEngine() 15 | { 16 | _instance = this; 17 | } 18 | 19 | ///The only instance of the Reset command. 20 | public static ResetScriptEngine Instance 21 | { 22 | get { return _instance; } 23 | } 24 | 25 | public override string EnglishName 26 | { 27 | get { return "ResetScriptEngine"; } 28 | } 29 | 30 | protected override Result RunCommand(RhinoDoc doc, RunMode mode) 31 | { 32 | try 33 | { 34 | ResetEngine(); 35 | return Result.Success; 36 | } 37 | catch (Exception exception) 38 | { 39 | return Result.Failure; 40 | } 41 | } 42 | 43 | public static void ResetEngine() 44 | { 45 | // shut down host 46 | RhinoPython.Host.ShutDown(); 47 | 48 | // create a new resetscriptengine.py python file 49 | string tempPath = Path.GetTempPath(); 50 | string filepath = tempPath + "resetscriptengine.py"; 51 | try 52 | { 53 | if (!File.Exists(filepath)) 54 | { 55 | // Create the file. 56 | using (FileStream fs = File.Create(filepath)) 57 | { 58 | Byte[] info = new UTF8Encoding(true).GetBytes("print \"Python script engine has been reset.\""); 59 | fs.Write(info, 0, info.Length); 60 | } 61 | } 62 | } 63 | catch (Exception ex) 64 | { 65 | throw ex; 66 | } 67 | 68 | // run this file through RhinoApp Command to initialize a new python script engine. 69 | var script = "-_EditPythonScript Debugging=Off \n(\n" + filepath + "\n)\n"; 70 | RhinoApp.RunScript(script, true); 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /CodeListener/StopCodeListener.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Rhino; 3 | using Rhino.Commands; 4 | 5 | namespace CodeListener 6 | { 7 | [System.Runtime.InteropServices.Guid("4e5821bd-858e-4183-9107-6c3f4be1783e")] 8 | public class StopCodeListener : Command 9 | { 10 | static StopCodeListener _instance; 11 | public StopCodeListener() 12 | { 13 | _instance = this; 14 | } 15 | 16 | ///The only instance of the ResetCodeListener command. 17 | public static StopCodeListener Instance 18 | { 19 | get { return _instance; } 20 | } 21 | 22 | public override string EnglishName 23 | { 24 | get { return "StopCodeListener"; } 25 | } 26 | 27 | protected override Result RunCommand(RhinoDoc doc, RunMode mode) 28 | { 29 | if (CodeListenerCommand._server != null) 30 | { 31 | CodeListenerCommand._server.Stop(); 32 | CodeListenerCommand._server = null; 33 | return Result.Success; 34 | } 35 | 36 | return Result.Cancel; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /CodeListener/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CodeListener 2 | 3 | Codelistener is part of the plugin [RhinoPython](https://github.com/ccc159/PythonScript). It is the RhinoPython server that runs in Rhino that listens to VS Code editor. 4 | 5 | ## Installation & Usage 6 | 7 | For the instructions of installation and usage please refer to [RhinoPython readme](https://github.com/ccc159/PythonScript/blob/master/README.md). 8 | 9 | ## Commands 10 | 11 | - **CodeListener**: Start CodeListener in the background. 12 | - **StopCodeListener**: Stop CodeListener, to allow other rhino instances to run CodeListener. Automatically stopped if current rhino instance exits. 13 | - **CodeListenerVersion**: Check current CodeListener version. 14 | - **CodeListenerExecute**: Allow Rhino to execute a file with given path. Can be used in combination with keyboard shortcut. For instance: `F2` binds to `_-CodeListenerExecute "C:\Program Files\Rhinoceros 5 (64-bit)\Plug-ins\IronPython\RhinoIronPython.chm" _Enter` 15 | - **ResetScriptEngine**: Reset Rhino python script engine. --------------------------------------------------------------------------------