FileWatcher.hx 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. package hide.tools;
  2. typedef FileWatchEvent = {path:String,fun:Void->Void,checkDel:Bool,element:js.html.Element,?ignoreCheck:String};
  3. private typedef Watch = {
  4. path : String,
  5. events : Array<FileWatchEvent>,
  6. w : js.node.fs.FSWatcher,
  7. wasChanged : Bool,
  8. changed : Bool,
  9. isDir : Bool,
  10. version : Int
  11. };
  12. class FileWatcher {
  13. var ide : hide.Ide;
  14. var watches : Map<String, Watch> = new Map();
  15. var timer : haxe.Timer;
  16. public function new() {
  17. ide = hide.Ide.inst;
  18. }
  19. public function pause() {
  20. for( w in watches )
  21. if( w.w != null ) {
  22. var sign = getSignature(w.path);
  23. if( sign != null ) {
  24. for( f in w.events )
  25. f.ignoreCheck = sign;
  26. }
  27. w.w.close();
  28. w.w = null;
  29. }
  30. }
  31. public function resume() {
  32. for( w in watches )
  33. if( w.w == null && w.events.length > 0 ) {
  34. initWatch(w);
  35. var sign = getSignature(w.path);
  36. for( f in w.events )
  37. if( f.ignoreCheck != sign || w.isDir ) {
  38. w.changed = true;
  39. w.version++;
  40. w.wasChanged = sign != null;
  41. break;
  42. }
  43. if( w.changed )
  44. haxe.Timer.delay(onEventChanged.bind(w),0);
  45. }
  46. }
  47. public function ignorePrevChange( f : FileWatchEvent ) {
  48. f.ignoreCheck = getSignature(f.path);
  49. }
  50. function getSignature( path : String ) : String {
  51. var sign = js.node.Crypto.createHash(js.node.Crypto.CryptoAlgorithm.MD5);
  52. try {
  53. sign.update(js.node.Fs.readFileSync(ide.getPath(path)));
  54. return sign.digest("base64");
  55. } catch( e : Dynamic ) {
  56. return null;
  57. }
  58. }
  59. public function dispose() {
  60. if( timer != null ) {
  61. timer.stop();
  62. timer = null;
  63. }
  64. for( w in watches )
  65. if( w.w != null )
  66. w.w.close();
  67. watches = new Map();
  68. }
  69. public function register( path : String, updateFun, ?checkDelete : Bool, ?element : Element ) : FileWatchEvent {
  70. path = ide.getPath(path);
  71. var w = getWatches(path);
  72. var f : FileWatchEvent = { path : path, fun : updateFun, checkDel : checkDelete, element : element == null ? null : element[0] };
  73. w.events.push(f);
  74. if( element != null && timer == null ) {
  75. timer = new haxe.Timer(1000);
  76. timer.run = cleanEvents;
  77. }
  78. return f;
  79. }
  80. public function registerRaw( path : String, updateFun, ?checkDelete : Bool, ?element : js.html.Element) : FileWatchEvent {
  81. path = ide.getPath(path);
  82. var w = getWatches(path);
  83. var f : FileWatchEvent = { path : path, fun : updateFun, checkDel : checkDelete, element: element};
  84. w.events.push(f);
  85. if( element != null && timer == null ) {
  86. timer = new haxe.Timer(1000);
  87. timer.run = cleanEvents;
  88. }
  89. return f;
  90. }
  91. public function unregister( path : String, updateFun : Void -> Void ) {
  92. path = ide.getPath(path);
  93. var w = getWatches(path);
  94. for( e in w.events )
  95. if( Reflect.compareMethods(e.fun, updateFun) ) {
  96. w.events.remove(e);
  97. break;
  98. }
  99. if( w.events.length == 0 ) {
  100. watches.remove(path);
  101. if( w.w != null ) w.w.close();
  102. }
  103. }
  104. public function unregisterElement( element : Element ) {
  105. for( path => w in watches ) {
  106. for( e in w.events.copy() )
  107. if( e.element == element[0] )
  108. w.events.remove(e);
  109. if( w.events.length == 0 ) {
  110. watches.remove(path);
  111. if( w.w != null ) w.w.close();
  112. }
  113. }
  114. }
  115. public function getVersion( path : String ) : Int {
  116. var w = watches.get(ide.getPath(path));
  117. if( w == null )
  118. return 0;
  119. return w.version;
  120. }
  121. function cleanEvents() {
  122. for( w in watches )
  123. for( e in w.events.copy() )
  124. isLive(w.events, e);
  125. }
  126. function isLive( events : Array<FileWatchEvent>, e : FileWatchEvent ) {
  127. if( e.element == null ) return true;
  128. var elt = e.element;
  129. while( elt != null ) {
  130. if( elt.nodeName == "BODY" ) return true;
  131. elt = elt.parentElement;
  132. }
  133. events.remove(e);
  134. return false;
  135. }
  136. function onEventChanged( w : Watch ) {
  137. if( !w.changed ) return;
  138. w.changed = false;
  139. var sign = null;
  140. for( e in w.events.copy() )
  141. if( isLive(w.events,e) && (w.wasChanged || e.checkDel) ) {
  142. if( e.ignoreCheck != null ) {
  143. if( sign == null ) sign = getSignature(w.path);
  144. if( sign == e.ignoreCheck ) continue;
  145. e.ignoreCheck = null;
  146. }
  147. e.fun();
  148. }
  149. w.wasChanged = false;
  150. }
  151. function initWatch( w : Watch ) {
  152. w.w = js.node.Fs.watch(w.path, function(k:String, file:String) {
  153. if( w.isDir && k == "change" ) return;
  154. if( k == "change" ) w.wasChanged = true;
  155. if( w.changed ) return;
  156. w.changed = true;
  157. w.version++;
  158. haxe.Timer.delay(onEventChanged.bind(w),100);
  159. });
  160. }
  161. function getWatches( path : String ) {
  162. var w = watches.get(path);
  163. if( w == null ) {
  164. var fullPath = ide.getPath(path);
  165. w = {
  166. path : fullPath,
  167. events : [],
  168. w : null,
  169. changed : false,
  170. isDir : try sys.FileSystem.isDirectory(fullPath) catch( e : Dynamic ) false,
  171. wasChanged : false,
  172. version : 0,
  173. };
  174. try initWatch(w) catch( e : Dynamic ) {
  175. // file does not exists, trigger a delayed event
  176. haxe.Timer.delay(function() {
  177. for( e in w.events.copy() )
  178. if( isLive(w.events,e) && e.checkDel )
  179. e.fun();
  180. }, 0);
  181. return w;
  182. }
  183. watches.set(path, w);
  184. }
  185. return w;
  186. }
  187. }