1
1
using System ;
2
- using System . ComponentModel ;
3
2
using System . IO ;
3
+ using System . Text ;
4
4
using System . Linq ;
5
- using System . Reflection ;
6
- using System . Reflection . Emit ;
7
5
using pyRevitAssemblyBuilder . SessionManager ;
6
+ using Autodesk . Revit . Attributes ;
8
7
9
8
namespace pyRevitAssemblyBuilder . AssemblyMaker
10
9
{
11
10
public class CommandTypeGenerator
12
11
{
13
- private static Type _scriptCommandBaseType ;
14
-
15
- public CommandTypeGenerator ( )
12
+ public string GenerateExtensionCode ( WrappedExtension extension )
16
13
{
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." ) ;
14
+ var sb = new StringBuilder ( ) ;
15
+ sb . AppendLine ( "#nullable disable" ) ;
16
+ //sb.AppendLine("using System;");
17
+ //sb.AppendLine("using System.ComponentModel;");
18
+ sb . AppendLine ( "using Autodesk.Revit.Attributes;" ) ;
19
+ //sb.AppendLine("using Autodesk.Revit.UI;");
20
+ sb . AppendLine ( "using PyRevitLabs.PyRevit.Runtime;" ) ;
21
+ foreach ( var cmd in extension . GetAllCommands ( ) )
22
+ {
23
+ // Replace invalid characters with underscores for valid C# identifiers
24
+ string safeClassName = SanitizeClassName ( cmd . UniqueId ) ;
25
+ string originalUniqueName = cmd . UniqueId ;
26
+ string scriptPath = cmd . ScriptPath ;
27
+ string searchPaths = string . Join ( ";" , new [ ] {
28
+ Path . GetDirectoryName ( cmd . ScriptPath ) ,
29
+ Path . Combine ( extension . Directory , "lib" ) ,
30
+ Path . Combine ( extension . Directory , ".." , ".." , "pyrevitlib" ) ,
31
+ Path . Combine ( extension . Directory , ".." , ".." , "site-packages" )
32
+ } ) ;
33
+ string tooltip = cmd . Tooltip ?? "" ;
34
+ string name = cmd . Name ;
35
+ string bundle = Path . GetFileName ( Path . GetDirectoryName ( cmd . ScriptPath ) ) ;
36
+ string extName = extension . Name ;
37
+ string ctrlId = $ "CustomCtrl_%{ extName } %{ bundle } %{ name } ";
38
+ string engineCfgs = @"{""clean"": false, ""persistent"": false, ""full_frame"": false}" ;
39
+ sb . AppendLine ( ) ;
40
+ sb . AppendLine ( "[Regeneration(RegenerationOption.Manual)]" ) ;
41
+ sb . AppendLine ( "[Transaction(TransactionMode.Manual)]" ) ;
42
+ sb . AppendLine ( $ "public class { safeClassName } : ScriptCommand") ;
43
+ sb . AppendLine ( "{" ) ;
44
+ sb . AppendLine ( $ " public { safeClassName } ()") ;
45
+ sb . AppendLine ( " : base(" ) ;
46
+ sb . AppendLine ( $ " @\" { Escape ( scriptPath ) } \" ,") ;
47
+ sb . AppendLine ( $ " @\" { Escape ( scriptPath ) } \" ,") ;
48
+ sb . AppendLine ( $ " @\" { Escape ( searchPaths ) } \" ,") ;
49
+ sb . AppendLine ( " \" \" ," ) ;
50
+ sb . AppendLine ( " \" \" ," ) ;
51
+ sb . AppendLine ( $ " @\" { Escape ( tooltip ) } \" ,") ;
52
+ sb . AppendLine ( $ " \" { Escape ( name ) } \" ,") ;
53
+ sb . AppendLine ( $ " \" { Escape ( bundle ) } \" ,") ;
54
+ sb . AppendLine ( $ " \" { Escape ( extName ) } \" ,") ;
55
+ sb . AppendLine ( $ " \" { originalUniqueName } \" ,") ;
56
+ sb . AppendLine ( $ " \" { Escape ( ctrlId ) } \" ,") ;
57
+ sb . AppendLine ( " \" (zero-doc)\" ," ) ;
58
+ sb . AppendLine ( $ " @\" { EscapeJsonForVerbatimString ( engineCfgs ) } \" ") ;
59
+ sb . AppendLine ( " )" ) ;
60
+ sb . AppendLine ( " {" ) ;
61
+ sb . AppendLine ( " }" ) ;
62
+ sb . AppendLine ( "}" ) ;
63
+ }
64
+ return sb . ToString ( ) ;
25
65
}
26
66
27
- public void DefineCommandType ( WrappedExtension extension , FileCommandComponent command , ModuleBuilder moduleBuilder )
67
+ private static string SanitizeClassName ( string name )
28
68
{
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
-
36
- var typeBuilder = moduleBuilder . DefineType (
37
- command . UniqueId ,
38
- TypeAttributes . Public | TypeAttributes . Class
39
- ) ;
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 ( ) ;
46
-
47
- var baseCtor = _scriptCommandBaseType . GetConstructor (
48
- BindingFlags . Instance | BindingFlags . Public | BindingFlags . NonPublic ,
49
- null , ctorParams , null
50
- ) ;
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
- } )
69
+ var sb = new StringBuilder ( ) ;
70
+ foreach ( char c in name )
89
71
{
90
- il . Emit ( OpCodes . Ldstr , arg ?? string . Empty ) ;
72
+ sb . Append ( char . IsLetterOrDigit ( c ) ? c : '_' ) ;
91
73
}
74
+ return sb . ToString ( ) ;
75
+ }
92
76
93
- il . Emit ( OpCodes . Call , baseCtor ) ;
94
- il . Emit ( OpCodes . Ret ) ;
77
+ private static string Escape ( string str )
78
+ {
79
+ return str ?
80
+ . Replace ( "\" " , "\" \" " ) // for verbatim strings
81
+ . Replace ( "\r " , "" )
82
+ . Replace ( "\n " , "\\ n" )
83
+ ?? "" ;
84
+ }
85
+
86
+ private static string EscapeJsonForVerbatimString ( string jsonStr )
87
+ {
88
+ // This method specifically handles JSON strings in verbatim string literals
89
+ // It properly escapes quotes by doubling them
90
+ if ( string . IsNullOrEmpty ( jsonStr ) )
91
+ return "" ;
95
92
96
- typeBuilder . CreateType ( ) ;
93
+ return jsonStr . Replace ( " \" " , " \" \" " ) ;
97
94
}
98
95
}
99
96
}
0 commit comments