@@ -21,13 +21,20 @@ internal class BufferLineCoverage :
2121        private  readonly  IAppOptionsProvider  appOptionsProvider ; 
2222        private  readonly  ILogger  logger ; 
2323        private  readonly  ITextBuffer2  textBuffer ; 
24-         private  ITrackedLines  trackedLines ; 
2524        private  bool ?  editorCoverageModeOff ; 
2625        private  IFileLineCoverage  fileLineCoverage ; 
2726        private  Nullable < DateTime >  lastChanged ; 
28-         private  DateTime  lastTestExecutionStarting ;  
27+         private  DateTime  lastTestExecutionStarting ; 
28+ 
29+         public  ITrackedLines  TrackedLines  {  get ;  set ;  } 
30+ 
31+         internal  enum  SerializedCoverageState 
32+         { 
33+             NotSerialized ,  OutOfDate ,  Ok 
34+         } 
35+ 
2936        public  BufferLineCoverage ( 
30-             IFileLineCoverage   fileLineCoverage , 
37+             ILastCoverage   lastCoverage , 
3138            ITextInfo  textInfo , 
3239            IEventAggregator  eventAggregator , 
3340            ITrackedLinesFactory  trackedLinesFactory , 
@@ -36,7 +43,12 @@ public BufferLineCoverage(
3643            ILogger  logger 
3744        ) 
3845        { 
39-             this . fileLineCoverage  =  fileLineCoverage ; 
46+             if  ( lastCoverage  !=  null ) 
47+             { 
48+                 this . fileLineCoverage  =  lastCoverage . FileLineCoverage ; 
49+                 this . lastTestExecutionStarting  =  lastCoverage . TestExecutionStartingDate ; 
50+             } 
51+             
4052            this . textBuffer  =  textInfo . TextBuffer ; 
4153            this . textInfo  =  textInfo ; 
4254            this . eventAggregator  =  eventAggregator ; 
@@ -47,17 +59,16 @@ ILogger logger
4759            void  AppOptionsChanged ( IAppOptions  appOptions ) 
4860            { 
4961                bool  newEditorCoverageModeOff  =  appOptions . EditorCoverageColouringMode  ==  EditorCoverageColouringMode . Off ; 
50-                 if  ( this . trackedLines  !=  null  &&  newEditorCoverageModeOff  &&  this . editorCoverageModeOff  !=  newEditorCoverageModeOff ) 
62+                 this . editorCoverageModeOff  =  newEditorCoverageModeOff ; 
63+                 if  ( this . TrackedLines  !=  null  &&  newEditorCoverageModeOff ) 
5164                { 
52-                     this . trackedLines  =  null ; 
65+                     this . TrackedLines  =  null ; 
5366                    this . SendCoverageChangedMessage ( ) ; 
5467                } 
55- 
56-                 this . editorCoverageModeOff  =  newEditorCoverageModeOff ; 
5768            } 
5869
5970            appOptionsProvider . OptionsChanged  +=  AppOptionsChanged ; 
60-             if  ( fileLineCoverage  !=  null ) 
71+             if  ( this . fileLineCoverage  !=  null ) 
6172            { 
6273                this . CreateTrackedLinesIfRequired ( true ) ; 
6374            } 
@@ -66,7 +77,7 @@ void AppOptionsChanged(IAppOptions appOptions)
6677            this . textBuffer . ChangedOnBackground  +=  this . TextBuffer_ChangedOnBackground ; 
6778            void  textViewClosedHandler ( object  s ,  EventArgs  e ) 
6879            { 
69-                 this . UpdateDynamicCoverageStore ( ( s  as  ITextView ) . TextSnapshot . GetText ( ) ) ; 
80+                 this . UpdateDynamicCoverageStore ( ( s  as  ITextView ) . TextSnapshot ) ; 
7081                this . textBuffer . Changed  -=  this . TextBuffer_ChangedOnBackground ; 
7182                textInfo . TextView . Closed  -=  textViewClosedHandler ; 
7283                appOptionsProvider . OptionsChanged  -=  AppOptionsChanged ; 
@@ -76,26 +87,40 @@ void textViewClosedHandler(object s, EventArgs e)
7687            textInfo . TextView . Closed  +=  textViewClosedHandler ; 
7788        } 
7889
79-         private  void  UpdateDynamicCoverageStore ( string   text ) 
90+         private  void  UpdateDynamicCoverageStore ( ITextSnapshot   textSnapshot ) 
8091        { 
81-             if  ( this . trackedLines  !=  null ) 
92+             if  ( this . TrackedLines  !=  null ) 
8293            { 
83-                 this . dynamicCoverageStore . SaveSerializedCoverage ( 
84-                     this . textInfo . FilePath ,  
85-                     this . trackedLinesFactory . Serialize ( this . trackedLines ,  text ) 
86-                 ) ; 
94+                 string  snapshotText  =  textSnapshot . GetText ( ) ; 
95+                 if  ( this . FileSystemReflectsTrackedLines ( snapshotText ) ) 
96+                 { 
97+                     // this only applies to the last coverage run. 
98+                     // the DynamicCoverageStore ensures this is removed when next coverage is run 
99+                     this . dynamicCoverageStore . SaveSerializedCoverage ( 
100+                         this . textInfo . FilePath , 
101+                         this . trackedLinesFactory . Serialize ( this . TrackedLines ,  snapshotText ) 
102+                     ) ; 
103+                 } 
104+                 else 
105+                 { 
106+                     this . dynamicCoverageStore . RemoveSerializedCoverage ( this . textInfo . FilePath ) ; 
107+                 } 
87108            } 
88109            else 
89110            { 
90111                this . dynamicCoverageStore . RemoveSerializedCoverage ( this . textInfo . FilePath ) ; 
91112            } 
92113        } 
93114
115+         //todo - behaviour if exception reading text 
116+         private  bool  FileSystemReflectsTrackedLines ( string  snapshotText ) 
117+             =>  this . textInfo . GetFileText ( )  ==  snapshotText ; 
118+         
94119        private  void  CreateTrackedLinesIfRequired ( bool  initial ) 
95120        { 
96121            if  ( this . EditorCoverageColouringModeOff ( ) ) 
97122            { 
98-                 this . trackedLines  =  null ; 
123+                 this . TrackedLines  =  null ; 
99124            } 
100125            else 
101126            { 
@@ -117,52 +142,90 @@ private void TryCreateTrackedLines(bool initial)
117142
118143        private  void  CreateTrackedLinesIfRequiredWithMessage ( ) 
119144        { 
120-             bool  hadTrackedLines  =  this . trackedLines  !=  null ; 
145+             bool  hadTrackedLines  =  this . TrackedLines  !=  null ; 
121146            if  ( ! this . lastChanged . HasValue  ||  this . lastChanged  <  this . lastTestExecutionStarting ) 
122147            { 
123148                this . CreateTrackedLinesIfRequired ( false ) ; 
124149            } 
125150            else 
126151            { 
127152                this . logger . Log ( $ "Not creating editor marks for { this . textInfo . FilePath }  as it was changed after test execution started") ; 
128-                 this . trackedLines  =  null ; 
153+                 this . TrackedLines  =  null ; 
129154            } 
130155
131-             bool  hasTrackedLines  =  this . trackedLines  !=  null ; 
156+             bool  hasTrackedLines  =  this . TrackedLines  !=  null ; 
132157            if  ( hadTrackedLines  ||  hasTrackedLines ) 
133158            { 
134159                this . SendCoverageChangedMessage ( ) ; 
135160            } 
136161        } 
137162
163+         private  ( SerializedCoverageState , string )  GetSerializedCoverageInfo ( SerializedCoverageWhen  serializedCoverageWhen ) 
164+         { 
165+             DateTime  lastWriteTime  =  this . textInfo . GetLastWriteTime ( ) ; 
166+ 
167+             
168+             if  ( serializedCoverageWhen  ==  null ) 
169+             { 
170+                 SerializedCoverageState  state  =  lastWriteTime  >  this . lastTestExecutionStarting  ? 
171+                     SerializedCoverageState . OutOfDate  : 
172+                     SerializedCoverageState . NotSerialized ; 
173+                 return  ( state ,  null ) ; 
174+             } 
175+ 
176+             /* 
177+                 If there is a When then it applies to the current coverage run ( as DynamicCoverageStore removes ) 
178+                 as When is written when the text view is closed it is always - LastWriteTime < When 
179+             */ 
180+             return  serializedCoverageWhen . When  <  lastWriteTime 
181+                 ?  ( ( SerializedCoverageState ,  string ) ) ( SerializedCoverageState . OutOfDate ,  null ) 
182+                 :  ( SerializedCoverageState . Ok ,  serializedCoverageWhen . Serialized ) ; 
183+         } 
184+ 
138185        private  void  CreateTrackedLines ( bool  initial ) 
139186        { 
140187            string  filePath  =  this . textInfo . FilePath ; 
141188            ITextSnapshot  currentSnapshot  =  this . textBuffer . CurrentSnapshot ; 
142189            if  ( initial ) 
143190            { 
144-                 string  serializedCoverage  =  this . dynamicCoverageStore . GetSerializedCoverage ( filePath ) ; 
145-                 if  ( serializedCoverage  !=  null ) 
191+                 SerializedCoverageWhen  serializedCoverageWhen  =  this . dynamicCoverageStore . GetSerializedCoverage ( 
192+                     filePath 
193+                 ) ; 
194+                 ( SerializedCoverageState  state ,  string  serializedCoverage )  =  this . GetSerializedCoverageInfo ( serializedCoverageWhen ) ; 
195+                 switch  ( state ) 
146196                { 
147-                     this . trackedLines  =  this . trackedLinesFactory . Create ( serializedCoverage ,  currentSnapshot ,  filePath ) ; 
148-                     return ; 
197+                     case  SerializedCoverageState . NotSerialized : 
198+                         break ; 
199+                     case  SerializedCoverageState . Ok : 
200+                         this . TrackedLines  =  this . trackedLinesFactory . Create ( 
201+                             serializedCoverage ,  currentSnapshot ,  filePath ) ; 
202+                         return ; 
203+                     default :  // Out of date 
204+                         this . logger . Log ( $ "Not creating editor marks for { this . textInfo . FilePath }  as coverage is out of date") ; 
205+                         return ; 
149206                } 
150207            } 
151208
152209            var  lines  =  this . fileLineCoverage . GetLines ( this . textInfo . FilePath ) . ToList ( ) ; 
153-             this . trackedLines  =  this . trackedLinesFactory . Create ( lines ,  currentSnapshot ,  filePath ) ; 
210+             this . TrackedLines  =  this . trackedLinesFactory . Create ( lines ,  currentSnapshot ,  filePath ) ; 
154211        } 
155212
156213        private  bool  EditorCoverageColouringModeOff ( ) 
157214        { 
215+             // as handling the event do not need to check the value again 
216+             if  ( this . editorCoverageModeOff . HasValue ) 
217+             { 
218+                 return  this . editorCoverageModeOff . Value ; 
219+             } 
220+ 
158221            this . editorCoverageModeOff  =  this . appOptionsProvider . Get ( ) . EditorCoverageColouringMode  ==  EditorCoverageColouringMode . Off ; 
159222            return  this . editorCoverageModeOff . Value ; 
160223        } 
161224
162225        private  void  TextBuffer_ChangedOnBackground ( object  sender ,  TextContentChangedEventArgs  textContentChangedEventArgs ) 
163226        { 
164227            this . lastChanged  =  DateTime . Now ; 
165-             if  ( this . trackedLines  !=  null ) 
228+             if  ( this . TrackedLines  !=  null ) 
166229            { 
167230                this . TryUpdateTrackedLines ( textContentChangedEventArgs ) ; 
168231            } 
@@ -182,7 +245,7 @@ private void TryUpdateTrackedLines(TextContentChangedEventArgs textContentChange
182245
183246        private  void  UpdateTrackedLines ( TextContentChangedEventArgs  textContentChangedEventArgs ) 
184247        { 
185-             IEnumerable < int >  changedLineNumbers  =  this . trackedLines . GetChangedLineNumbers ( textContentChangedEventArgs . After ,  textContentChangedEventArgs . Changes . Select ( change =>  change . NewSpan ) . ToList ( ) ) 
248+             IEnumerable < int >  changedLineNumbers  =  this . TrackedLines . GetChangedLineNumbers ( textContentChangedEventArgs . After ,  textContentChangedEventArgs . Changes . Select ( change =>  change . NewSpan ) . ToList ( ) ) 
186249                . Where ( changedLine =>  changedLine  >=  0  &&  changedLine  <  textContentChangedEventArgs . After . LineCount ) ; 
187250            this . SendCoverageChangedMessageIfChanged ( changedLineNumbers ) ; 
188251        } 
@@ -199,16 +262,16 @@ private void SendCoverageChangedMessage(IEnumerable<int> changedLineNumbers = nu
199262            =>  this . eventAggregator . SendMessage ( new  CoverageChangedMessage ( this ,  this . textInfo . FilePath ,  changedLineNumbers ) ) ; 
200263
201264        public  IEnumerable < IDynamicLine >  GetLines ( int  startLineNumber ,  int  endLineNumber ) 
202-             =>  this . trackedLines  ==  null  ?  Enumerable . Empty < IDynamicLine > ( )  :  this . trackedLines . GetLines ( startLineNumber ,  endLineNumber ) ; 
265+             =>  this . TrackedLines  ==  null  ?  Enumerable . Empty < IDynamicLine > ( )  :  this . TrackedLines . GetLines ( startLineNumber ,  endLineNumber ) ; 
203266
204267        public  void  Handle ( NewCoverageLinesMessage  message ) 
205268        { 
206269            this . fileLineCoverage  =  message . CoverageLines ; 
207270
208-             bool  hadTrackedLines  =  this . trackedLines  !=  null ; 
271+             bool  hadTrackedLines  =  this . TrackedLines  !=  null ; 
209272            if  ( this . fileLineCoverage  ==  null ) 
210273            { 
211-                 this . trackedLines  =  null ; 
274+                 this . TrackedLines  =  null ; 
212275                if  ( hadTrackedLines ) 
213276                { 
214277                    this . SendCoverageChangedMessage ( ) ; 
0 commit comments