@@ -199,11 +199,11 @@ repository.prototype.scan = function(directory) {
199199 // ignore file (due to pattern)
200200 if ( ! matched ) return done ( ) ;
201201 // handle in-memory cache
202- if ( self . files . hasOwnProperty ( filename ) ) {
202+ if ( filename in self . files ) {
203203 // check if need to parse again
204204 if (
205205 self . options . cacheByFileDate &&
206- self . files [ filename ] . mtime === stat . mtime . getTime ( )
206+ self . files [ filename ] . mtime > stat . mtime . getTime ( )
207207 ) {
208208 self . emit ( 'cache' , {
209209 name : filename
@@ -230,8 +230,10 @@ repository.prototype.scan = function(directory) {
230230 ) . then ( function ( file ) {
231231 self . counter . loading -- ;
232232 self . counter . loaded ++ ;
233- self . counter . size += file . size ;
234- self . counter . symbols += file . nodes . length ;
233+ if ( file ) {
234+ self . counter . size += file . size ;
235+ self . counter . symbols += file . nodes . length ;
236+ }
235237 self . emit ( 'progress' , self . counter ) ;
236238 done ( ) ;
237239 } , function ( e ) {
@@ -364,17 +366,15 @@ repository.prototype.parse = function(filename, encoding, stat) {
364366 self . files [ filename ] = new file ( self , filename , ast ) ;
365367 self . files [ filename ] . refresh ( ) ;
366368 self . files [ filename ] . size = data . length ;
367- if ( self . options . cacheByFileDate ) {
368- self . files [ filename ] . mtime = data . length ;
369- }
370369 return done ( self . files [ filename ] ) ;
371370 } catch ( e ) {
372371 delete self . files [ filename ] ;
373372 self . emit ( 'error' , e ) ;
374373 return reject ( e ) ;
375374 }
376375 }
377- } ) ;
376+ }
377+ ) ;
378378 }
379379 } ) ;
380380 return this . files [ filename ] ;
@@ -485,6 +485,120 @@ repository.prototype.getNamespace = function(name) {
485485 }
486486} ;
487487
488+ /**
489+ * Synchronize with specified offset
490+ * @return {boolean } True is node is in sync
491+ */
492+ repository . prototype . sync = function ( filename , contents , offset ) {
493+ if ( ! ( filename in this . files ) ) {
494+ // not already in memory
495+ // @todo use the lazy cache
496+ var ast ;
497+ try {
498+ var reader = new parser ( {
499+ ast : {
500+ withPositions : true
501+ } ,
502+ parser : {
503+ extractDoc : this . options . scanDocs ,
504+ suppressErrors : true
505+ }
506+ } ) ;
507+ ast = reader . parseCode (
508+ contents , filename
509+ ) ;
510+ } catch ( e ) {
511+ this . emit ( 'error' , e ) ;
512+ return false ;
513+ }
514+ this . files [ filename ] = new file ( this , filename , ast ) ;
515+ this . files [ filename ] . refresh ( ) ;
516+ this . files [ filename ] . size = contents . length ;
517+ // nothing to sync
518+ return true ;
519+ }
520+ // @todo first implementation (not optimized for speed)
521+ // collect nodes
522+ var file = this . files [ filename ] ;
523+ var syncNode = null ;
524+ var start = offset [ 0 ] ;
525+ var end = offset [ 1 ] ;
526+ for ( var i = 0 , size = file . nodes . length ; i < size ; i ++ ) {
527+ var node = file . nodes [ i ] ;
528+ var position = node . position . offset ;
529+ // x... [ ]
530+ // var isNodeAfter = position.start > start && position.end > end;
531+ // [ ] x...
532+ // var isNodeBefore = position.start > end;
533+ // [ x... ] or [ x.].. or x.[.. ] or x.[.].
534+ // var isNodeIntersect = !isNodeAfter && !isNodeBefore;
535+ // only [ x... ]
536+ var isNodeContainer = position . start <= start && position . end >= end ;
537+ if ( isNodeContainer ) {
538+ // locate nodes to be synced :
539+ // [--[x..]--]
540+ // nodes that is not father of other nodes
541+ if ( syncNode ) {
542+ // lets naively think that the start offset should be greated (included from text position)
543+ if ( syncNode . position . offset . start < position . start ) {
544+ syncNode = node ;
545+ }
546+ } else {
547+ syncNode = node ;
548+ }
549+ }
550+ }
551+ if ( ! syncNode ) {
552+ // refresh full file
553+ syncNode = file ;
554+ }
555+
556+ var ast ;
557+ try {
558+ var reader = new parser ( {
559+ ast : {
560+ withPositions : true
561+ } ,
562+ parser : {
563+ extractDoc : this . options . scanDocs ,
564+ suppressErrors : true
565+ }
566+ } ) ;
567+
568+ if ( syncNode . position . offset . start === 0 ) {
569+ // full refresh
570+ ast = reader . parseCode ( contents , filename ) ;
571+ // delete references
572+ file . remove ( ) ;
573+ // create a new one
574+ this . files [ filename ] = new file ( this , filename , ast ) ;
575+ this . files [ filename ] . refresh ( ) ;
576+ this . files [ filename ] . size = contents . length ;
577+ } else {
578+ // inject parsing state
579+ reader . lexer . mode_eval = false ;
580+ reader . lexer . all_tokens = false ;
581+ reader . lexer . comment_tokens = this . options . scanDocs ;
582+ reader . lexer . setInput ( contents ) ;
583+ reader . lexer . setState ( syncNode . state ) ;
584+ reader . lexer . begin ( 'ST_IN_SCRIPTING' ) ;
585+ reader . parser . _errors = [ ] ;
586+ reader . parser . filename = filename ;
587+ reader . parser . currentNamespace = [ '' ] ;
588+ // @notice innerList state (node not extracted to flag ignored)
589+ reader . parser . token = syncNode . state . token ;
590+ // generic entry point :
591+ ast = reader . parser . read_top_statement ( ) ;
592+ file . removeNode ( syncNode ) ;
593+ syncNode . parent . consumeChild ( ast ) ;
594+ }
595+ } catch ( e ) {
596+ this . emit ( 'error' , e ) ;
597+ return false ;
598+ }
599+ return true ;
600+ } ;
601+
488602
489603/**
490604 * Clean all the cache
0 commit comments