@@ -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