├── sample ├── build.hxml ├── PartialBar.hx ├── PartialFoo.hx ├── Sample.hx └── output │ └── partials.js ├── partials ├── Partial.hx └── Partials.hx ├── haxelib.json ├── .gitignore ├── LICENSE ├── README.md └── .gitattributes /sample/build.hxml: -------------------------------------------------------------------------------- 1 | -lib partials 2 | -main Sample 3 | -js output/partials.js -------------------------------------------------------------------------------- /sample/PartialBar.hx: -------------------------------------------------------------------------------- 1 | class PartialBar implements partials.Partial { 2 | public static function bar() { 3 | trace("BAR!"); 4 | } 5 | } -------------------------------------------------------------------------------- /sample/PartialFoo.hx: -------------------------------------------------------------------------------- 1 | class PartialFoo implements partials.Partial { 2 | public static function foo() { 3 | trace("FOO!"); 4 | } 5 | } -------------------------------------------------------------------------------- /sample/Sample.hx: -------------------------------------------------------------------------------- 1 | @:partials(PartialFoo, PartialBar) 2 | class Sample implements partials.Partial { 3 | static public function main() { 4 | trace("My partials are here!"); 5 | foo(); 6 | bar(); 7 | } 8 | } -------------------------------------------------------------------------------- /partials/Partial.hx: -------------------------------------------------------------------------------- 1 | package partials; 2 | 3 | /** 4 | * Implement this interface to indicate that part of the class is defined somewhere else. 5 | */ 6 | @:autoBuild(partials.Partials.process()) 7 | interface Partial { 8 | 9 | } -------------------------------------------------------------------------------- /haxelib.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "partials", 3 | "url" : "https://github.com/FuzzyWuzzie/haxe-partials", 4 | "license": "MIT", 5 | "tags": ["cross", "utility"], 6 | "description": "A simple macro library for writing classes as partials (splitting a single class into multiple source files).", 7 | "version": "1.0.0", 8 | "releasenote": "Initial release, basic testing shows all systems are go!", 9 | "contributors": ["FuzzyWuzzie"], 10 | "dependencies": {} 11 | } -------------------------------------------------------------------------------- /sample/output/partials.js: -------------------------------------------------------------------------------- 1 | (function (console) { "use strict"; 2 | var partials_Partial = function() { }; 3 | var Sample = function() { }; 4 | Sample.__interfaces__ = [partials_Partial]; 5 | Sample.main = function() { 6 | console.log("My partials are here!"); 7 | Sample.foo(); 8 | Sample.bar(); 9 | }; 10 | Sample.foo = function() { 11 | console.log("FOO!"); 12 | }; 13 | Sample.bar = function() { 14 | console.log("BAR!"); 15 | }; 16 | Sample.main(); 17 | })(typeof console != "undefined" ? console : {log:function(){}}); 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### Windows ### 2 | # Windows image file caches 3 | Thumbs.db 4 | ehthumbs.db 5 | 6 | # Folder config file 7 | Desktop.ini 8 | 9 | # Recycle Bin used on file shares 10 | $RECYCLE.BIN/ 11 | 12 | # Windows Installer files 13 | *.cab 14 | *.msi 15 | *.msm 16 | *.msp 17 | 18 | # Windows shortcuts 19 | *.lnk 20 | 21 | 22 | ### OSX ### 23 | .DS_Store 24 | .AppleDouble 25 | .LSOverride 26 | 27 | # Icon must end with two \r 28 | Icon 29 | 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear in the root of a volume 35 | .DocumentRevisions-V100 36 | .fseventsd 37 | .Spotlight-V100 38 | .TemporaryItems 39 | .Trashes 40 | .VolumeIcon.icns 41 | 42 | # Directories potentially created on remote AFP share 43 | .AppleDB 44 | .AppleDesktop 45 | Network Trash Folder 46 | Temporary Items 47 | .apdisk 48 | 49 | 50 | ### Linux ### 51 | *~ 52 | 53 | # KDE directory preferences 54 | .directory 55 | 56 | # Linux trash folder which might appear on any partition or disk 57 | .Trash-* -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Kenton Hamaluik 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # haxe-partials 2 | [![License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)](https://github.com/FuzzyWuzzie/haxe-partials/blob/master/LICENSE) 3 | 4 | A simple macro library for writing classes as partials (splitting a single class into multiple source files). In the context of this library, partials are the same as [C# partials](https://msdn.microsoft.com/en-CA/library/wa80x488.aspx), and really just allow for a bit more organization. 5 | 6 | You probably don't even really want to use this in production, as if you are, it likely means your classes have gotten out of hand and you should refactor to keep things [DRY](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself). That said, partials still serve a great deal of utility, such as helping you break apart those monolithic classes to examine code patterns and help your refactor. A more in-depth discussion of their utility is available on [StackExchange](http://programmers.stackexchange.com/questions/71494/why-use-partial-classes). 7 | 8 | This library was inspired by [mpartial](https://github.com/massiveinteractive/mpartial). This library is **far** simpler and does much less than mpartial. However, mpartial is now quite out of date and is no longer usable thanks to changes in [tink_macro](https://github.com/haxetink/tink_macro). 9 | 10 | That said, you can still do a lot of the same things, such as separate platform-specific code into different partials which get included based on `#if` defines. 11 | 12 | ## Usage 13 | 14 | To define a class as a partial, simply implement the `partials.Partial` interface. To indicate the "host" class for a series of partials, use the `@:partials()` metadata on the class. 15 | 16 | ## Examples 17 | 18 | ### Basic Usage 19 | 20 | ```haxe 21 | package my.package; 22 | 23 | @:partials(my.package.PartialDefinitionA, my.package.partials.PartialDefinitionB) 24 | class MyClassThatWouldBeReallyLongWithoutPartials implements partials.Partial { 25 | public function new() { 26 | trace("My partials are here!"); 27 | foo(); 28 | bar(); 29 | } 30 | } 31 | ``` 32 | 33 | ```haxe 34 | package my.package; 35 | 36 | class PartialDefinitionA implements partials.Partial { 37 | public function foo() { 38 | trace("FOO!"); 39 | } 40 | } 41 | ``` 42 | 43 | ```haxe 44 | package my.package.partials; 45 | 46 | class PartialDefinitionB implements partials.Partial { 47 | public function bar() { 48 | trace("BAR!"); 49 | } 50 | } 51 | ``` 52 | 53 | This would output: 54 | 55 | ``` 56 | My partials are here! 57 | FOO! 58 | BAR! 59 | ``` 60 | 61 | ### Platform-Specific Partials 62 | 63 | ```haxe 64 | #if js 65 | @:partials(Log_js) 66 | #elseif cpp 67 | @:partials(Log_cpp) 68 | #end 69 | class Log implements partials.Partial { 70 | public static function info(msg:String) { 71 | log("info", msg); 72 | } 73 | 74 | public static function warn(msg:String) { 75 | log("warn", msg); 76 | } 77 | 78 | public static function error(msg:String) { 79 | throw msg; 80 | } 81 | } 82 | ``` 83 | 84 | ```haxe 85 | class Log_js implements partials.Partial { 86 | public static function log(level:String, msg:String) { 87 | var style:String = switch(level) { 88 | case "info": 89 | "color: green;" 90 | case "warn": 91 | "color: orange; font-weight: bold;" 92 | }; 93 | untyped console.log("%c" + level + ": " + msg, style); 94 | } 95 | } 96 | ``` 97 | 98 | ```haxe 99 | class Log_cpp implements partials.Partial { 100 | public static function log(level:String, msg:String) { 101 | trace(level + ": " + msg); 102 | } 103 | } 104 | ``` 105 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Apply native OS line-endings on checkout of these files... 2 | *.boo text 3 | *.c text 4 | *.cginc text 5 | *.config text 6 | *.contentproj text 7 | *.cpp text 8 | *.cs text 9 | *.css text 10 | *.dae text 11 | *.DAE text 12 | *.dtd text 13 | *.fx text 14 | *.glsl text 15 | *.h text 16 | *.htm text 17 | *.html text 18 | *.inc text 19 | *.ini text 20 | *.js text 21 | *.JSFL text 22 | *.jsfl text 23 | *.json text 24 | *.log text 25 | *.md text 26 | *.mel text 27 | *.php text 28 | *.shader text 29 | *.txt text 30 | *.TXT text 31 | *.xaml text 32 | *.xml text 33 | *.xsd text 34 | .gitattributes text 35 | .gitignore text 36 | COPYING text 37 | INSTALL* text 38 | KEYS* text 39 | LICENSE* text 40 | NEWS* text 41 | NOTICE* text 42 | README* text 43 | TODO* text 44 | WHATSNEW* text 45 | 46 | # Apply Unix-style LF line-endings on checkout of these files... 47 | *.meta text eol=lf 48 | *.sh text eol=lf 49 | *.vspscc text eol=lf 50 | .htaccess text eol=lf 51 | 52 | # Apply Windows/DOS-style CR-LF line-endings on checkout of these files... 53 | *.bat text eol=crlf 54 | *.cmd text eol=crlf 55 | *.csproj text eol=crlf 56 | *.sln text eol=crlf 57 | *.user text eol=crlf 58 | *.vcproj text eol=crlf 59 | 60 | # No end-of-line conversions are applied (i.e., "-text -diff") to these files... 61 | *.7z binary 62 | *.ai binary 63 | *.anim binary 64 | *.apk binary 65 | *.asset binary 66 | *.bin binary 67 | *.bmp binary 68 | *.BMP binary 69 | *.com binary 70 | *.COM binary 71 | *.controller binary 72 | *.cubemap binary 73 | *.dex binary 74 | *.dll binary 75 | *.DLL binary 76 | *.dylib binary 77 | *.eps binary 78 | *.exe binary 79 | *.EXE binary 80 | *.exr binary 81 | *.fbx binary 82 | *.FBX binary 83 | *.fla binary 84 | *.flare binary 85 | *.flv binary 86 | *.gif binary 87 | *.guiskin binary 88 | *.gz binary 89 | *.ht binary 90 | *.ico binary 91 | *.jpeg binary 92 | *.jpg binary 93 | *.keystore binary 94 | *.mask binary 95 | *.mat binary 96 | *.mb binary 97 | *.mp3 binary 98 | *.mp4 binary 99 | *.mpg binary 100 | *.ogg binary 101 | *.PCX binary 102 | *.pcx binary 103 | *.pdb binary 104 | *.pdf binary 105 | *.physicMaterial binary 106 | *.physicmaterial binary 107 | *.png binary 108 | *.prefab binary 109 | *.ps binary 110 | *.psd binary 111 | *.qt binary 112 | *.so binary 113 | *.swf binary 114 | *.tga binary 115 | *.tif binary 116 | *.tiff binary 117 | *.ttf binary 118 | *.TTF binary 119 | *.unity binary 120 | *.unitypackage binary 121 | *.unityPackage binary 122 | *.wav binary 123 | *.wmv binary 124 | *.zip binary 125 | *.ZIP binary -------------------------------------------------------------------------------- /partials/Partials.hx: -------------------------------------------------------------------------------- 1 | package partials; 2 | 3 | import haxe.macro.Context; 4 | import haxe.macro.Compiler; 5 | import haxe.macro.Expr; 6 | 7 | import haxe.rtti.Meta; 8 | 9 | import haxe.ds.StringMap; 10 | 11 | /** 12 | * Utility macros for defining multiple parts of a class in different files. To define a class 13 | * as a partial, simply implement the partials.Partial interface. To indicate the "host" class 14 | * for a series of partials, use the `@:partials()` metadata on the class, like so: 15 | * 16 | * ```haxe 17 | * package my.package; 18 | * 19 | * @:partials(my.package.PartialDefinitionA, my.package.partials.PartialDefinitionB) 20 | * class MyClassThatWouldBeReallyLongWithoutPartials implements partials.Partial { 21 | * public function new() { 22 | * trace("My partials are here!"); 23 | * foo(); 24 | * bar(); 25 | * } 26 | * } 27 | * ``` 28 | * 29 | * ```haxe 30 | * package my.package; 31 | * 32 | * class PartialDefinitionA implements partials.Partial { 33 | * public function foo() { 34 | * trace("FOO!"); 35 | * } 36 | * } 37 | * ``` 38 | * 39 | * ```haxe 40 | * package my.package.partials; 41 | * 42 | * class PartialDefinitionB implements partials.Partial { 43 | * public function bar() { 44 | * trace("BAR!"); 45 | * } 46 | * } 47 | * ``` 48 | * 49 | * This would output: 50 | * 51 | * ``` 52 | * My partials are here! 53 | * FOO! 54 | * BAR! 55 | * ``` 56 | */ 57 | class Partials { 58 | private static var partials:StringMap> = new StringMap>(); 59 | 60 | private static function getModuleName(e:Expr):String { 61 | return switch(e.expr) { 62 | case EConst(c): 63 | switch(c) { 64 | case CIdent(s): s; 65 | default: null; 66 | } 67 | case EField(e, field): 68 | getModuleName(e) + "." + field; 69 | default: null; 70 | }; 71 | } 72 | 73 | // TODO: necessary? 74 | /*private static function translatePositions(expr:Expr):Expr { 75 | //expr.pos = Context.currentPos(); 76 | return Context.makeExpr(expr.expr, Context.currentPos()); 77 | } 78 | 79 | private static function repositionMeta(meta:Null):Null { 80 | if(meta == null) return null; 81 | for(entry in meta) { 82 | entry.pos = Context.currentPos(); 83 | if(entry.params != null) { 84 | for(param in entry.params) { 85 | param = translatePositions(param); 86 | } 87 | } 88 | } 89 | return meta; 90 | }*/ 91 | 92 | macro public static function process():Array { 93 | var localFields:Array = Context.getBuildFields(); 94 | 95 | // see if it is a partial host 96 | if(Context.getLocalClass().get().meta.has(":partials")) { 97 | // yup, it is! 98 | var params:Array = Context.getLocalClass().get().meta.extract(":partials")[0].params; 99 | for(param in params) { 100 | // force-import the referenced module 101 | var moduleName:String = getModuleName(param); 102 | Context.getModule(moduleName); 103 | 104 | // ok, now that it's imported, bring in all of its fields 105 | var moduleFields:Array = partials.get(moduleName); 106 | for(field in moduleFields) { 107 | field.pos = Context.currentPos(); 108 | localFields.push(field); 109 | } 110 | } 111 | } 112 | else { 113 | // nope, just a regular partial 114 | // save its fields 115 | partials.set(Context.getLocalModule(), localFields); 116 | 117 | // and trash it 118 | Compiler.exclude(Context.getLocalModule()); 119 | return new Array(); 120 | } 121 | 122 | return localFields; 123 | } 124 | } --------------------------------------------------------------------------------