Skip to content

Commit cffdb8a

Browse files
romangolevjmcouffin
authored andcommitted
feat: change unique id to mimic pythonic one, add logic to CommandTypeGenerator
1 parent 0546001 commit cffdb8a

File tree

12 files changed

+183
-139
lines changed

12 files changed

+183
-139
lines changed

dev/pyRevitLoader/pyRevitAssemblyBuilder/AssemblyMaker/AssemblyBuilderService.cs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
using System.IO;
33
using System.Reflection;
44
using System.Reflection.Emit;
5-
using pyRevitAssemblyBuilder.Shared;
5+
using pyRevitAssemblyBuilder.SessionManager;
66

77
namespace pyRevitAssemblyBuilder.AssemblyMaker
88
{
@@ -17,7 +17,7 @@ public AssemblyBuilderService(CommandTypeGenerator typeGenerator, string revitVe
1717
_revitVersion = revitVersion ?? throw new ArgumentNullException(nameof(revitVersion));
1818
}
1919

20-
public ExtensionAssemblyInfo BuildExtensionAssembly(IExtension extension)
20+
public ExtensionAssemblyInfo BuildExtensionAssembly(WrappedExtension extension)
2121
{
2222
string extensionHash = GetStableHash(extension.GetHash() + _revitVersion).Substring(0, 16);
2323
string fileName = $"pyRevit_{_revitVersion}_{extensionHash}_{extension.Name}.dll";
@@ -31,6 +31,7 @@ public ExtensionAssemblyInfo BuildExtensionAssembly(IExtension extension)
3131

3232
string outputPath = Path.Combine(outputDir, fileName);
3333

34+
// TODO: Put the right version here
3435
var asmName = new AssemblyName(extension.Name)
3536
{
3637
Version = new Version(1, 0, 0, 0)
@@ -46,12 +47,10 @@ public ExtensionAssemblyInfo BuildExtensionAssembly(IExtension extension)
4647
var asmBuilder = AssemblyBuilder.DefineDynamicAssembly(asmName, AssemblyBuilderAccess.Run);
4748
var moduleBuilder = asmBuilder.DefineDynamicModule(fileNameWithoutExt);
4849
#endif
49-
5050
foreach (var cmd in extension.GetAllCommands())
5151
{
5252
_typeGenerator.DefineCommandType(extension, cmd, moduleBuilder);
5353
}
54-
5554
#if NETFRAMEWORK
5655
asmBuilder.Save(fileName);
5756
#else

dev/pyRevitLoader/pyRevitAssemblyBuilder/AssemblyMaker/CommandTypeGenerator.cs

Lines changed: 83 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,97 @@
1-
using System.Reflection.Emit;
1+
using System;
2+
using System.ComponentModel;
3+
using System.IO;
4+
using System.Linq;
25
using System.Reflection;
3-
using pyRevitAssemblyBuilder.Shared;
6+
using System.Reflection.Emit;
7+
using pyRevitAssemblyBuilder.SessionManager;
48

59
namespace pyRevitAssemblyBuilder.AssemblyMaker
610
{
711
public class CommandTypeGenerator
812
{
9-
public void DefineCommandType(IExtension extension, ICommandComponent command, ModuleBuilder moduleBuilder)
13+
private static Type _scriptCommandBaseType;
14+
15+
public CommandTypeGenerator()
16+
{
17+
var runtimeAssembly = AppDomain.CurrentDomain.GetAssemblies()
18+
.FirstOrDefault(a => a.FullName.StartsWith("pyRevitLabs.PyRevit.Runtime"));
19+
20+
if (runtimeAssembly == null)
21+
throw new InvalidOperationException("ScriptCommand base types could not be resolved. Ensure PyRevitLabs.PyRevit.Runtime is loaded.");
22+
23+
_scriptCommandBaseType = runtimeAssembly.GetType("PyRevitLabs.PyRevit.Runtime.ScriptCommand")
24+
?? throw new InvalidOperationException("ScriptCommand type not found in runtime assembly.");
25+
}
26+
27+
public void DefineCommandType(WrappedExtension extension, FileCommandComponent command, ModuleBuilder moduleBuilder)
1028
{
29+
// TODO: try to build assemblies in the isolated context with referencies
30+
//var typeBuilder = moduleBuilder.DefineType(
31+
// fullTypeName,
32+
// TypeAttributes.Public | TypeAttributes.Class,
33+
// _scriptCommandBaseType
34+
//);
35+
1136
var typeBuilder = moduleBuilder.DefineType(
12-
name: command.UniqueId,
13-
attr: TypeAttributes.Public | TypeAttributes.Class
37+
command.UniqueId,
38+
TypeAttributes.Public | TypeAttributes.Class
1439
);
40+
// Add Description attribute
41+
var descAttrCtor = typeof(DescriptionAttribute).GetConstructor(new[] { typeof(string) });
42+
var descAttr = new CustomAttributeBuilder(descAttrCtor, new object[] { command.Tooltip ?? "" });
43+
typeBuilder.SetCustomAttribute(descAttr);
44+
45+
var ctorParams = Enumerable.Repeat(typeof(string), 13).ToArray();
1546

16-
var attrBuilder = new CustomAttributeBuilder(
17-
typeof(System.ComponentModel.DescriptionAttribute).GetConstructor(new[] { typeof(string) }),
18-
new object[] { command.Tooltip }
47+
var baseCtor = _scriptCommandBaseType.GetConstructor(
48+
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
49+
null, ctorParams, null
1950
);
20-
typeBuilder.SetCustomAttribute(attrBuilder);
51+
52+
if (baseCtor == null)
53+
throw new InvalidOperationException("Could not find ScriptCommand base constructor.");
54+
55+
var ctorBuilder = typeBuilder.DefineConstructor(
56+
MethodAttributes.Public,
57+
CallingConventions.Standard,
58+
Type.EmptyTypes
59+
);
60+
61+
var il = ctorBuilder.GetILGenerator();
62+
63+
// Setup constructor arguments
64+
string scriptPath = command.ScriptPath;
65+
string configScriptPath = null;
66+
string searchPaths = string.Join(";", new[]
67+
{
68+
Path.GetDirectoryName(command.ScriptPath),
69+
Path.Combine(extension.Directory, "lib"),
70+
Path.Combine(extension.Directory, "..", "..", "pyrevitlib"),
71+
Path.Combine(extension.Directory, "..", "..", "site-packages")
72+
});
73+
string args = "";
74+
string help = "";
75+
string tooltip = command.Tooltip ?? "";
76+
string name = command.Name;
77+
string bundle = Path.GetFileName(Path.GetDirectoryName(command.ScriptPath));
78+
string extName = extension.Name;
79+
string uniqueName = command.UniqueId;
80+
string ctrlId = $"CustomCtrl_%{extName}%{bundle}%{name}";
81+
string context = "(zero-doc)";
82+
string engineCfgs = "{\"clean\": false, \"persistent\": false, \"full_frame\": false}";
83+
84+
foreach (var arg in new[]
85+
{
86+
scriptPath, configScriptPath, searchPaths, args, help,
87+
tooltip, name, bundle, extName, uniqueName, ctrlId, context, engineCfgs
88+
})
89+
{
90+
il.Emit(OpCodes.Ldstr, arg ?? string.Empty);
91+
}
92+
93+
il.Emit(OpCodes.Call, baseCtor);
94+
il.Emit(OpCodes.Ret);
2195

2296
typeBuilder.CreateType();
2397
}

dev/pyRevitLoader/pyRevitAssemblyBuilder/AssemblyMaker/TypeMakerService.cs

Lines changed: 0 additions & 33 deletions
This file was deleted.
Lines changed: 27 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,44 @@
1-
using System.Collections.Generic;
1+
using System.Collections.Generic;
22
using System.Linq;
33
using pyRevitExtensionParser;
4-
using pyRevitAssemblyBuilder.Shared;
54

65
namespace pyRevitAssemblyBuilder.SessionManager
76
{
87
public class ExtensionManagerService
98
{
10-
public IEnumerable<IExtension> GetInstalledExtensions()
9+
public IEnumerable<WrappedExtension> GetInstalledExtensions()
1110
{
12-
foreach (var parsedExtension in ExtensionParser.ParseInstalledExtensions())
11+
var installedExtensions = ExtensionParser.ParseInstalledExtensions();
12+
foreach (var parsedExtension in installedExtensions)
1313
{
14-
yield return new FileSystemExtension(
14+
var pushbuttonCommands = CollectCommandComponents(parsedExtension.Children)
15+
.Select(ConvertComponent)
16+
.ToList();
17+
18+
yield return new WrappedExtension(
1519
name: parsedExtension.Name,
1620
path: parsedExtension.Directory,
17-
commands: parsedExtension.Children.Select(ConvertComponent).ToList(),
21+
commands: pushbuttonCommands,
22+
children: parsedExtension.Children.Select(ConvertComponent).ToList(),
1823
metadata: parsedExtension.Metadata
1924
);
2025
}
2126
}
22-
23-
private ICommandComponent ConvertComponent(ParsedComponent parsed)
27+
private IEnumerable<ParsedComponent> CollectCommandComponents(IEnumerable<ParsedComponent> components)
28+
{
29+
foreach (var component in components)
30+
{
31+
if (!string.IsNullOrEmpty(component.ScriptPath))
32+
yield return component;
33+
34+
if (component.Children != null)
35+
{
36+
foreach (var child in CollectCommandComponents(component.Children))
37+
yield return child;
38+
}
39+
}
40+
}
41+
private FileCommandComponent ConvertComponent(ParsedComponent parsed)
2442
{
2543
return new FileCommandComponent
2644
{
@@ -33,42 +51,5 @@ private ICommandComponent ConvertComponent(ParsedComponent parsed)
3351
Children = parsed.Children?.Select(ConvertComponent).Cast<object>().ToList() ?? new List<object>()
3452
};
3553
}
36-
37-
private class FileSystemExtension : IExtension
38-
{
39-
private readonly IEnumerable<ICommandComponent> _commands;
40-
41-
public FileSystemExtension(string name, string path, IEnumerable<ICommandComponent> commands, ParsedExtensionMetadata metadata)
42-
{
43-
Name = name;
44-
Directory = path;
45-
_commands = commands;
46-
Metadata = metadata;
47-
}
48-
49-
public string Name { get; }
50-
public string Directory { get; }
51-
public ParsedExtensionMetadata Metadata { get; }
52-
53-
public string GetHash() => Directory.GetHashCode().ToString("X");
54-
55-
public IEnumerable<ICommandComponent> GetAllCommands() => _commands;
56-
57-
public IEnumerable<object> Children => _commands;
58-
public string Type => ".extension";
59-
60-
object IExtension.Children => Children;
61-
}
62-
63-
private class FileCommandComponent : ICommandComponent
64-
{
65-
public string Name { get; set; }
66-
public string ScriptPath { get; set; }
67-
public string Tooltip { get; set; }
68-
public string UniqueId { get; set; }
69-
public string ExtensionName { get; set; }
70-
public string Type { get; set; }
71-
public IEnumerable<object> Children { get; set; } = Enumerable.Empty<object>();
72-
}
7354
}
74-
}
55+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
4+
namespace pyRevitAssemblyBuilder.SessionManager
5+
{
6+
public class FileCommandComponent
7+
{
8+
public string Name { get; set; }
9+
public string ScriptPath { get; set; }
10+
public string Tooltip { get; set; }
11+
public string UniqueId { get; set; }
12+
public string ExtensionName { get; set; }
13+
public string Type { get; set; }
14+
public IEnumerable<object> Children { get; set; } = Enumerable.Empty<object>();
15+
}
16+
}

dev/pyRevitLoader/pyRevitAssemblyBuilder/SessionManager/HookManager.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ namespace pyRevitAssemblyBuilder.SessionManager
88
{
99
public class HookManager
1010
{
11-
public void RegisterHooks(IExtension extension)
11+
public void RegisterHooks(WrappedExtension extension)
1212
{
1313
if (extension == null)
1414
return;
@@ -29,15 +29,15 @@ public void RegisterHooks(IExtension extension)
2929
// Future: implement actual execution logic for scripts if needed
3030
}
3131

32-
private IEnumerable<string> GetHookScripts(IExtension extension)
32+
private IEnumerable<string> GetHookScripts(WrappedExtension extension)
3333
{
3434
var hooksPath = Path.Combine(extension.Directory, "hooks");
3535
return Directory.Exists(hooksPath)
3636
? Directory.GetFiles(hooksPath)
3737
: Enumerable.Empty<string>();
3838
}
3939

40-
private IEnumerable<string> GetCheckScripts(IExtension extension)
40+
private IEnumerable<string> GetCheckScripts(WrappedExtension extension)
4141
{
4242
var checksPath = Path.Combine(extension.Directory, "checks");
4343
return Directory.Exists(checksPath)

dev/pyRevitLoader/pyRevitAssemblyBuilder/SessionManager/SessionManagerService.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ public SessionManagerService(
2727
public void LoadSession()
2828
{
2929
var extensions = _extensionManager.GetInstalledExtensions();
30+
3031
foreach (var ext in extensions)
3132
{
3233
var assmInfo = _assemblyBuilder.BuildExtensionAssembly(ext);

dev/pyRevitLoader/pyRevitAssemblyBuilder/SessionManager/UIManagerService.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ public UIManagerService(UIApplication uiApp)
1616
_uiApp = uiApp;
1717
}
1818

19-
public void BuildUI(IExtension extension, ExtensionAssemblyInfo assemblyInfo)
19+
public void BuildUI(WrappedExtension extension, ExtensionAssemblyInfo assemblyInfo)
2020
{
2121
if (extension?.Children == null)
2222
return;
@@ -27,7 +27,7 @@ public void BuildUI(IExtension extension, ExtensionAssemblyInfo assemblyInfo)
2727

2828
private void RecursivelyBuildUI(object obj, object parentComponent, RibbonPanel parentPanel, string tabName, ExtensionAssemblyInfo assemblyInfo)
2929
{
30-
var component = obj as ICommandComponent;
30+
var component = obj as FileCommandComponent;
3131
if (component == null)
3232
return;
3333

@@ -50,11 +50,11 @@ private void RecursivelyBuildUI(object obj, object parentComponent, RibbonPanel
5050

5151
case CommandComponentType.Stack:
5252
var itemDataList = new List<RibbonItemData>();
53-
var originalItems = new List<ICommandComponent>();
53+
var originalItems = new List<FileCommandComponent>();
5454

5555
foreach (var child in component.Children as IEnumerable<object> ?? Enumerable.Empty<object>())
5656
{
57-
var subCmd = child as ICommandComponent;
57+
var subCmd = child as FileCommandComponent;
5858
if (subCmd == null)
5959
continue;
6060

@@ -93,7 +93,7 @@ private void RecursivelyBuildUI(object obj, object parentComponent, RibbonPanel
9393
{
9494
foreach (var child in origComponent.Children ?? Enumerable.Empty<object>())
9595
{
96-
if (child is ICommandComponent subCmd &&
96+
if (child is FileCommandComponent subCmd &&
9797
CommandComponentTypeExtensions.FromExtension(subCmd.Type) == CommandComponentType.PushButton)
9898
{
9999
var subData = new PushButtonData(subCmd.UniqueId, subCmd.Name, assemblyInfo.Location, subCmd.UniqueId);
@@ -125,7 +125,7 @@ private void RecursivelyBuildUI(object obj, object parentComponent, RibbonPanel
125125

126126
foreach (var child in component.Children ?? Enumerable.Empty<object>())
127127
{
128-
if (child is ICommandComponent subCmd &&
128+
if (child is FileCommandComponent subCmd &&
129129
CommandComponentTypeExtensions.FromExtension(subCmd.Type) == CommandComponentType.PushButton)
130130
{
131131
var subData = new PushButtonData(subCmd.UniqueId, subCmd.Name, assemblyInfo.Location, subCmd.UniqueId);
@@ -137,7 +137,7 @@ private void RecursivelyBuildUI(object obj, object parentComponent, RibbonPanel
137137
}
138138

139139
private PulldownButtonData CreatePulldown(
140-
ICommandComponent component,
140+
FileCommandComponent component,
141141
RibbonPanel parentPanel,
142142
string tabName,
143143
ExtensionAssemblyInfo assemblyInfo,
@@ -155,7 +155,7 @@ private PulldownButtonData CreatePulldown(
155155

156156
foreach (var child in component.Children ?? Enumerable.Empty<object>())
157157
{
158-
if (child is ICommandComponent subCmd &&
158+
if (child is FileCommandComponent subCmd &&
159159
CommandComponentTypeExtensions.FromExtension(subCmd.Type) == CommandComponentType.PushButton)
160160
{
161161
var subData = new PushButtonData(subCmd.UniqueId, subCmd.Name, assemblyInfo.Location, subCmd.UniqueId);

0 commit comments

Comments
 (0)