-
-
Notifications
You must be signed in to change notification settings - Fork 467
Expand file tree
/
Copy pathProgram.cs
More file actions
163 lines (141 loc) · 6.55 KB
/
Copy pathProgram.cs
File metadata and controls
163 lines (141 loc) · 6.55 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
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
110
111
112
113
114
115
116
117
118
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
151
152
153
154
155
156
157
158
159
160
161
162
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Threading;
using Microsoft.Scripting.Hosting;
using PyRevitLabs.PyRevit.Shell;
using PythonConsoleControl;
namespace PyRevitLabs.PyRevit.Shell.DevHost;
/// <summary>
/// Development-only launcher that hosts the interactive Python Shell window outside of Revit, so
/// the REPL/editor can be iterated on without starting Revit. It is not built by the product
/// pipeline and is never shipped to end users.
///
/// It mirrors <c>ShellLauncher</c>'s modal path but drops the Revit-specific pieces
/// (UIApplication, ExternalEvent dispatch, runtime-builtin injection): statements run on the UI
/// dispatcher and the engine gets a plain IronPython environment with pyRevit's library paths on
/// sys.path.
/// </summary>
internal static class Program
{
[STAThread]
private static int Main(string[] args)
{
var root = FindRepositoryRoot();
var engineDir = Path.Combine(root, "bin", "netcore", "engines", "IPY2712PR");
if (!File.Exists(Path.Combine(engineDir, "pyRevitLabs.PyRevit.Shell.dll")))
{
Console.Error.WriteLine(
"The shell is not built yet. Build it first, e.g.:" + Environment.NewLine +
" cd build && dotnet run -c Release -- ci" + Environment.NewLine +
"or just the shell:" + Environment.NewLine +
" dotnet build dev/pyRevitLabs.PyRevit.Shell/pyRevitLabs.PyRevit.Shell.csproj -c \"Release IPY2712PR\"");
return 1;
}
var searchPaths = BuildSearchPaths(root, engineDir);
var useDark = args.Contains("--dark", StringComparer.OrdinalIgnoreCase);
var consoleOnly = args.Contains("--console", StringComparer.OrdinalIgnoreCase);
Window window;
IronPythonConsoleControl consoleControl;
if (consoleOnly)
{
var shell = new InteractiveShellWindow();
shell.ApplyTheme(useDark);
window = shell;
consoleControl = shell.ConsoleControl;
}
else
{
var editor = new InteractiveEditorWindow();
editor.ApplyTheme(useDark);
window = editor;
consoleControl = editor.ConsoleControl;
}
var mainDispatcher = Dispatcher.CurrentDispatcher;
consoleControl.WithConsoleHost(host =>
{
// Statements run on the UI dispatcher, matching the in-Revit modal dispatch so this
// host behaves like the real shell rather than the console's native background thread.
host.Console.SetCommandDispatcher(command => RunOnDispatcher(mainDispatcher, command));
host.Editor.SetCompletionDispatcher(command => RunOnDispatcher(mainDispatcher, command));
ConfigureStandaloneEngine(host.Engine, host.Console.ScriptScope, searchPaths);
host.Console.ScriptScope.SetVariable("__window__", window);
});
window.ShowDialog();
return 0;
}
private static IList<string> BuildSearchPaths(string root, string engineDir)
{
var paths = new List<string> { engineDir };
var stdlibZip = Path.Combine(root, "dev", "libs", "IronPython", "python_2712pr_lib.zip");
if (File.Exists(stdlibZip))
paths.Add(stdlibZip);
AddIfExists(paths, Path.Combine(root, "pyrevitlib"));
AddIfExists(paths, Path.Combine(root, "site-packages"));
AddIfExists(paths, Path.Combine(root, "extensions"));
return paths;
}
private static void AddIfExists(List<string> paths, string dir)
{
if (Directory.Exists(dir))
paths.Add(dir);
}
private static void ConfigureStandaloneEngine(ScriptEngine engine, ScriptScope scope, IList<string> searchPaths)
{
engine.SetSearchPaths(searchPaths);
// Safe defaults for the reserved pyrevit builtins the interactive shell expects, so user
// snippets that read them don't NameError in the no-Revit host.
try
{
engine.Execute(StubBuiltinsSnippet, scope);
}
catch
{
// Best effort: the REPL still works without these defaults.
}
}
// Poll the dispatcher operation so a Ctrl+C keyboard interrupt can break a long run.
private static void RunOnDispatcher(Dispatcher dispatcher, Action command)
{
if (command == null)
return;
var operation = dispatcher.BeginInvoke(DispatcherPriority.Normal, command);
while (operation.Status != DispatcherOperationStatus.Completed)
operation.Wait(TimeSpan.FromSeconds(1));
}
private static string FindRepositoryRoot()
{
var current = new DirectoryInfo(AppContext.BaseDirectory);
while (current is not null)
{
if (File.Exists(Path.Combine(current.FullName, "pyRevitfile")))
return current.FullName;
current = current.Parent;
}
return Directory.GetCurrentDirectory();
}
private const string StubBuiltinsSnippet =
"try: __execid__\nexcept NameError: __execid__ = ''\n" +
"try: __timestamp__\nexcept NameError: __timestamp__ = ''\n" +
"try: __cachedengine__\nexcept NameError: __cachedengine__ = False\n" +
"try: __cachedengineid__\nexcept NameError: __cachedengineid__ = None\n" +
"try: __scriptruntime__\nexcept NameError: __scriptruntime__ = None\n" +
"try: __revit__\nexcept NameError: __revit__ = None\n" +
"try: __commanddata__\nexcept NameError: __commanddata__ = None\n" +
"try: __elements__\nexcept NameError: __elements__ = None\n" +
"try: __uibutton__\nexcept NameError: __uibutton__ = None\n" +
"try: __commandpath__\nexcept NameError: __commandpath__ = ''\n" +
"try: __configcommandpath__\nexcept NameError: __configcommandpath__ = ''\n" +
"try: __commandname__\nexcept NameError: __commandname__ = 'Interactive Shell (dev)'\n" +
"try: __commandbundle__\nexcept NameError: __commandbundle__ = 'pyRevit Shell'\n" +
"try: __commandextension__\nexcept NameError: __commandextension__ = 'pyRevitCore'\n" +
"try: __commanduniqueid__\nexcept NameError: __commanduniqueid__ = 'pyrevit-interactive-shell'\n" +
"try: __commandcontrolid__\nexcept NameError: __commandcontrolid__ = 'pyrevit-interactive-shell'\n" +
"try: __forceddebugmode__\nexcept NameError: __forceddebugmode__ = False\n" +
"try: __shiftclick__\nexcept NameError: __shiftclick__ = False\n" +
"try: __result__\nexcept NameError: __result__ = {}\n" +
"try: __eventsender__\nexcept NameError: __eventsender__ = None\n" +
"try: __eventargs__\nexcept NameError: __eventargs__ = None\n";
}