@@ -14,6 +14,7 @@ import BidirectionalMultiMap from './bidirectional-multi-map.js';
1414import { isFunction } from 'utils' ;
1515import lively from "src/client/lively.js" ;
1616import _ from 'src/external/lodash/lodash.js' ;
17+ import diff from 'src/external/diff-match-patch.js' ;
1718
1819/*MD # Dependency Analysis MD*/
1920
@@ -213,36 +214,192 @@ class Dependency {
213214 }
214215
215216}
217+ /*MD # Debugging Cache MD*/
218+
219+ export class AEDebuggingCache {
220+
221+ constructor ( ) {
222+ this . registeredDebuggingViews = [ ] ;
223+ this . debouncedUpdateDebuggingViews = _ . debounce ( this . updateDebggingViews , 100 ) ;
224+ }
225+ /*MD ## Registration MD*/
226+ async registerFileForAEDebugging ( url , context , triplesCallback ) {
227+ const callback = async ( ) => {
228+ if ( context && ( ! context . valid || context . valid ( ) ) ) {
229+ triplesCallback ( ( await this . getDependencyTriplesForFile ( url ) ) ) ;
230+ return true ;
231+ }
232+ return false ;
233+ } ;
234+ this . registeredDebuggingViews . push ( callback ) ;
235+
236+ triplesCallback ( ( await this . getDependencyTriplesForFile ( url ) ) ) ;
237+ }
238+
239+ /*MD ## Caching MD*/
240+
241+ /*MD ## Code Change API MD*/
242+ async updateFile ( url , oldCode , newCode ) {
243+ try {
244+ const lineMapping = this . calculateMapping ( oldCode , newCode ) ;
245+ for ( const ae of DependenciesToAExprs . getAEsInFile ( url ) ) {
246+ const location = ae . meta ( ) . get ( "location" ) ;
247+ this . remapLocation ( lineMapping , location ) ;
248+ }
249+ for ( const hook of await HooksToDependencies . getHooksInFile ( url ) ) {
250+ hook . getLocations ( ) . then ( locations => {
251+ for ( const location of locations ) {
252+ this . remapLocation ( lineMapping , location ) ;
253+ }
254+ } ) ;
255+ }
256+ } catch ( e ) {
257+ console . error ( e ) ;
258+ }
259+ }
260+
261+ remapLocation ( lineMapping , location ) {
262+ let [ diff , lineChanged ] = lineMapping [ location . start . line ] ;
263+
264+ if ( diff === - 1 ) {
265+ location . start . line = 0 ;
266+ } else {
267+ location . start . line = diff ;
268+ }
269+
270+ let [ diff2 , lineChanged2 ] = lineMapping [ location . end . line ] ;
271+
272+ if ( diff2 === - 1 ) {
273+ location . end . line = 0 ;
274+ } else {
275+ location . end . line = diff2 ;
276+ }
277+ if ( lineChanged || lineChanged2 ) {
278+ lively . notify ( "Changed AE code for existing AE. There are outdated expressions in the system" ) ;
279+ //ToDo: Check if the content is similar enough, mark AEs as outdated
280+ }
281+ }
282+
283+ calculateMapping ( oldCode , newCode ) {
284+ const mapping = [ ] ; //For each line of oldCode: new Line in newCode if existing, changedContent if the line no longer exists but can be mapped to a newCode line
285+ var dmp = new diff . diff_match_patch ( ) ;
286+ var a = dmp . diff_linesToChars_ ( oldCode , newCode ) ;
287+ var lineText1 = a . chars1 ;
288+ var lineText2 = a . chars2 ;
289+ var lineArray = a . lineArray ;
290+ var diffs = dmp . diff_main ( lineText1 , lineText2 , false ) ;
291+
292+ let originalLine = 0 ;
293+ let newLine = 0 ;
294+ let recentDeletions = 0 ;
295+ let recentAdditions = 0 ;
296+ for ( let [ type , data ] of diffs ) {
297+ if ( type === 0 ) {
298+ recentDeletions = 0 ;
299+ recentAdditions = 0 ;
300+ for ( let i = 0 ; i < data . length ; i ++ ) {
301+ mapping [ originalLine ] = [ newLine , false ] ;
302+ originalLine ++ ;
303+ newLine ++ ;
304+ }
305+ } else if ( type === 1 ) {
306+ if ( recentDeletions > 0 ) {
307+ const matchingLines = Math . max ( recentDeletions , data . length ) ;
308+ for ( let i = 0 ; i < matchingLines ; i ++ ) {
309+ mapping [ originalLine - matchingLines + i ] = [ newLine , true ] ;
310+ newLine ++ ;
311+ }
312+ data = data . substring ( matchingLines )
313+ }
314+ if ( data . length > 0 ) {
315+ newLine += data . length ;
316+ recentAdditions += data . length ;
317+ }
318+ } else {
319+ if ( recentAdditions > 0 ) {
320+ const matchingLines = Math . max ( recentAdditions , data . length ) ;
321+ for ( let i = 0 ; i < matchingLines ; i ++ ) {
322+ mapping [ originalLine ] = [ newLine - matchingLines + i , true ] ;
323+ originalLine ++ ;
324+ }
325+ data = data . substring ( matchingLines )
326+ }
327+ recentDeletions += data . length ;
328+ for ( let i = 0 ; i < data . length ; i ++ ) {
329+ mapping [ originalLine ] = [ - 1 , false ] ;
330+ originalLine ++ ;
331+ }
332+ }
333+ }
334+
335+ dmp . diff_charsToLines_ ( diffs , lineArray ) ;
336+
337+ return mapping ;
338+ }
339+ /*MD ## Rewriting API MD*/
340+ async updateDebggingViews ( ) {
341+ for ( let i = 0 ; i < this . registeredDebuggingViews . length ; i ++ ) {
342+ if ( ! ( await this . registeredDebuggingViews [ i ] ( ) ) ) {
343+ this . registeredDebuggingViews . splice ( i , 1 ) ;
344+ i -- ;
345+ }
346+ }
347+ }
348+
349+ async getDependencyTriplesForFile ( url ) {
350+ const result = [ ] ;
351+ for ( const ae of DependenciesToAExprs . getAEsInFile ( url ) ) {
352+ for ( const dependency of DependenciesToAExprs . getDepsForAExpr ( ae ) ) {
353+ for ( const hook of HooksToDependencies . getHooksForDep ( dependency ) ) {
354+ result . push ( { hook, dependency, ae } ) ;
355+ }
356+ }
357+ }
358+ for ( const hook of await HooksToDependencies . getHooksInFile ( url ) ) {
359+ for ( const dependency of HooksToDependencies . getDepsForHook ( hook ) ) {
360+ for ( const ae of DependenciesToAExprs . getAExprsForDep ( dependency ) ) {
361+ const location = ae . meta ( ) . get ( "location" ) . file ;
362+ // if the AE is also in this file, we already covered it with the previous loop
363+ if ( ! location . includes ( url ) ) {
364+ result . push ( { hook, dependency, ae } ) ;
365+ }
366+ }
367+ }
368+ }
369+ return result ;
370+ }
371+ }
372+ export const DebuggingCache = new AEDebuggingCache ( ) ;
216373
217374const DependenciesToAExprs = {
218375 _depsToAExprs : new BidirectionalMultiMap ( ) ,
219376 _AEsPerFile : new Map ( ) ,
220377
221378 associate ( dep , aexpr ) {
222- const location = aexpr . meta ( ) . get ( "location" ) . file ;
223- if ( ! this . _AEsPerFile . has ( location ) ) {
379+ const location = aexpr . meta ( ) . get ( "location" ) ;
380+ if ( location && location . file && ! this . _AEsPerFile . has ( location . file ) ) {
224381 this . _AEsPerFile . set ( location , new Set ( ) ) ;
225382 }
226383 this . _AEsPerFile . get ( location ) . add ( aexpr ) ;
227384 this . _depsToAExprs . associate ( dep , aexpr ) ;
228385 dep . updateTracking ( ) ;
229- debouncedUpdateDebuggingViews ( ) ;
386+ DebuggingCache . debouncedUpdateDebuggingViews ( ) ;
230387 } ,
231388
232389 disconnectAllForAExpr ( aexpr ) {
233- const location = aexpr . meta ( ) . get ( "location" ) . file ;
234- if ( this . _AEsPerFile . has ( location ) ) {
390+ const location = aexpr . meta ( ) . get ( "location" ) ;
391+ if ( location && location . file && this . _AEsPerFile . has ( location . file ) ) {
235392 this . _AEsPerFile . get ( location ) . delete ( aexpr ) ;
236393 }
237394 const deps = this . getDepsForAExpr ( aexpr ) ;
238395 this . _depsToAExprs . removeAllLeftFor ( aexpr ) ;
239396 deps . forEach ( dep => dep . updateTracking ( ) ) ;
240- debouncedUpdateDebuggingViews ( ) ;
397+ DebuggingCache . debouncedUpdateDebuggingViews ( ) ;
241398 } ,
242399
243400 getAEsInFile ( url ) {
244- for ( const [ location , aes ] of this . _AEsPerFile . entries ( ) ) {
245- if ( location . includes ( url ) ) return aes ;
401+ for ( const [ location , aes ] of this . _AEsPerFile . entries ( ) ) {
402+ if ( location . includes ( url ) ) return aes ;
246403 }
247404 return [ ] ;
248405 } ,
@@ -275,27 +432,29 @@ const HooksToDependencies = {
275432
276433 associate ( hook , dep ) {
277434 this . _hooksToDeps . associate ( hook , dep ) ;
278- debouncedUpdateDebuggingViews ( ) ;
435+ DebuggingCache . debouncedUpdateDebuggingViews ( ) ;
279436 } ,
280-
437+
281438 remove ( hook , dep ) {
282439 this . _hooksToDeps . remove ( hook , dep ) ;
283- debouncedUpdateDebuggingViews ( ) ;
440+ DebuggingCache . debouncedUpdateDebuggingViews ( ) ;
284441 } ,
285442
286443 async getHooksInFile ( url ) {
287444 const hooksWithLocations = await Promise . all ( this . _hooksToDeps . getAllLeft ( ) . map ( hook => {
288- return hook . getLocations ( ) . then ( locations => { return { hook, locations} } ) ;
289- } ) )
290- return hooksWithLocations . filter ( ( { hook, locations} ) => {
291- const location = locations . find ( loc => loc && loc . source ) ;
292- return location && locations . source . includes ( url ) ;
293- } ) . map ( ( { hook, locations} ) => hook ) ;
445+ return hook . getLocations ( ) . then ( locations => {
446+ return { hook, locations } ;
447+ } ) ;
448+ } ) ) ;
449+ return hooksWithLocations . filter ( ( { hook, locations } ) => {
450+ const location = locations . find ( loc => loc && loc . file ) ;
451+ return location && location . file . includes ( url ) ;
452+ } ) . map ( ( { hook, locations } ) => hook ) ;
294453 } ,
295454
296455 disconnectAllForDependency ( dep ) {
297456 this . _hooksToDeps . removeAllLeftFor ( dep ) ;
298- debouncedUpdateDebuggingViews ( ) ;
457+ DebuggingCache . debouncedUpdateDebuggingViews ( ) ;
299458 } ,
300459
301460 getDepsForHook ( hook ) {
@@ -358,14 +517,15 @@ class Hook {
358517 }
359518
360519 addLocation ( location ) {
361- if ( ! this . locations . some ( loc => _ . isEqual ( loc , location ) ) ) {
520+ if ( ! this . locations . some ( loc => _ . isEqual ( loc , location ) ) ) {
362521 this . locations . push ( location ) ;
363522 }
364523 //Todo: Promises get added multiple times... Also, use a Set?
365524 }
366525
367526 async getLocations ( ) {
368- return await Promise . all ( this . locations ) ;
527+ this . locations = await Promise . all ( this . locations ) ;
528+ return this . locations ;
369529 }
370530
371531 notifyDependencies ( ) {
@@ -378,12 +538,12 @@ class SourceCodeHook extends Hook {
378538 const compKey = ContextAndIdentifierCompositeKey . for ( context , identifier ) ;
379539 return CompositeKeyToSourceCodeHook . getOrCreateRightFor ( compKey , key => new SourceCodeHook ( ) ) ;
380540 }
381-
541+
382542 static get ( context , identifier ) {
383543 const compKey = ContextAndIdentifierCompositeKey . for ( context , identifier ) ;
384- return CompositeKeyToSourceCodeHook . getRightFor ( compKey ) ;
544+ return CompositeKeyToSourceCodeHook . getRightFor ( compKey ) ;
385545 }
386-
546+
387547 constructor ( context , identifier ) {
388548 super ( ) ;
389549
@@ -713,28 +873,28 @@ class TracingHandler {
713873 * ********************** update ********************************
714874 * **************************************************************
715875 */
716- static memberUpdated ( obj , prop , location ) {
876+ static memberUpdated ( obj , prop , location ) {
717877 const hook = SourceCodeHook . get ( obj , prop ) ;
718- if ( ! hook ) return ;
878+ if ( ! hook ) return ;
719879 hook . addLocation ( location || TracingHandler . findRegistrationLocation ( ) ) ;
720880 hook . notifyDependencies ( ) ;
721- debouncedUpdateDebuggingViews ( ) ;
881+ DebuggingCache . debouncedUpdateDebuggingViews ( ) ;
722882 }
723883
724884 static globalUpdated ( globalName , location ) {
725885 const hook = SourceCodeHook . get ( globalRef , globalName ) ;
726- if ( ! hook ) return ;
886+ if ( ! hook ) return ;
727887 hook . addLocation ( location || TracingHandler . findRegistrationLocation ( ) ) ;
728888 hook . notifyDependencies ( ) ;
729- debouncedUpdateDebuggingViews ( ) ;
889+ DebuggingCache . debouncedUpdateDebuggingViews ( ) ;
730890 }
731891
732892 static localUpdated ( scope , varName , location ) {
733893 const hook = SourceCodeHook . get ( scope , varName ) ;
734- if ( ! hook ) return ;
894+ if ( ! hook ) return ;
735895 hook . addLocation ( location || TracingHandler . findRegistrationLocation ( ) ) ;
736896 hook . notifyDependencies ( ) ;
737- debouncedUpdateDebuggingViews ( ) ;
897+ DebuggingCache . debouncedUpdateDebuggingViews ( ) ;
738898 }
739899
740900 static async findRegistrationLocation ( ) {
@@ -746,11 +906,11 @@ class TracingHandler {
746906 if ( ! frame . file . includes ( "active-expression" ) ) {
747907 const loc = await frame . getSourceLoc ( ) ;
748908 return {
749- start : { line : loc . line , column : loc . column } ,
750- end : { line : loc . line , column : loc . column } ,
751- file : loc . source ,
752- }
753- }
909+ start : { line : loc . line , column : loc . column } ,
910+ end : { line : loc . line , column : loc . column } ,
911+ file : loc . source
912+ } ;
913+ }
754914 }
755915 return undefined ;
756916 }
@@ -902,63 +1062,6 @@ export function getGlobal(globalName) {
9021062 }
9031063}
9041064
905- const registeredDebuggingViews = [ ] ;
906-
907- const debouncedUpdateDebuggingViews = _ . debounce ( updateDebggingViews , 100 ) ;
908-
909- async function updateDebggingViews ( ) {
910- for ( let i = 0 ; i < registeredDebuggingViews . length ; i ++ ) {
911- if ( ! await registeredDebuggingViews [ i ] ( ) ) {
912- registeredDebuggingViews . splice ( i , 1 ) ;
913- i -- ;
914- }
915- }
916- }
917-
918- export async function registerFileForAEDebugging ( url , context , triplesCallback ) {
919- const callback = async ( ) => {
920- if ( context && ( ! context . valid || context . valid ( ) ) ) {
921- triplesCallback ( await getDependencyTriplesForFile ( url ) ) ;
922- return true ;
923- }
924- return false ;
925- }
926- registeredDebuggingViews . push ( callback ) ;
927-
928- triplesCallback ( await getDependencyTriplesForFile ( url ) ) ;
929- }
930-
931- export async function getDependencyTriplesForFile ( url ) {
932- const result = [ ] ;
933- for ( const ae of DependenciesToAExprs . getAEsInFile ( url ) ) {
934- for ( const dependency of DependenciesToAExprs . getDepsForAExpr ( ae ) ) {
935- for ( const hook of HooksToDependencies . getHooksForDep ( dependency ) ) {
936- result . push ( { hook, dependency, ae } ) ;
937- }
938- }
939- }
940- for ( const hook of await HooksToDependencies . getHooksInFile ( url ) ) {
941- for ( const dependency of HooksToDependencies . getDepsForHook ( hook ) ) {
942- for ( const ae of DependenciesToAExprs . getAExprsForDep ( dependency ) ) {
943- const location = ae . meta ( ) . get ( "location" ) . file ;
944- // if the AE is also in this file, we already covered it with the previous loop
945- if ( ! location . includes ( url ) ) {
946- result . push ( { hook, dependency, ae } ) ;
947- }
948- }
949- }
950- }
951- return result ;
952- }
953-
954- export async function getHookTriplesForFile ( url ) {
955- return HooksToDependencies . getHookTriplesForFile ( url ) ;
956- }
957-
958- export async function getAETriplesForFile ( url ) {
959- return DependenciesToAExprs . getAETriplesForFile ( url ) ;
960- }
961-
9621065export function setGlobal ( globalName , location ) {
9631066 TracingHandler . globalUpdated ( globalName , location ) ;
9641067}
0 commit comments