Skip to content

Commit be1a022

Browse files
committed
Invalidate rewrite sessions on checkout of a dirty rewriter
A rewriter can only be dirty on checkout if the code of the corresponding module has been modified since the last parse. Applying such a rewriter will erase all modifications since the last parse and thus probably destroy work by the user. To avoid this, the rewrite sessions will invalidate themselves when they encounter a stale module. To allow to communicate the reason of the invalidation, IsInvalidated and Invalidate have been replaced by a Status property taking values in the new RewriteSessionState enum. It can only be changed from Valid to some other state, Afterwards, all attempts to set it are ignored. Consequently, the Status shows the original reason for invalidation.
1 parent 99c2d75 commit be1a022

File tree

7 files changed

+245
-68
lines changed

7 files changed

+245
-68
lines changed

Rubberduck.Parsing/Rewriter/IRewriteSession.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,15 @@ public interface IRewriteSession
77
{
88
IModuleRewriter CheckOutModuleRewriter(QualifiedModuleName module);
99
bool TryRewrite();
10-
bool IsInvalidated { get; }
11-
void Invalidate();
10+
RewriteSessionState Status { get; set; }
1211
CodeKind TargetCodeKind { get; }
1312
}
13+
14+
public enum RewriteSessionState
15+
{
16+
Valid,
17+
RewriteApplied,
18+
OtherSessionsRewriteApplied,
19+
StaleParseTree
20+
}
1421
}

Rubberduck.Parsing/Rewriter/RewriteSessionBase.cs

Lines changed: 31 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public abstract class RewriteSessionBase : IRewriteSession
1515
private readonly Func<IRewriteSession, bool> _rewritingAllowed;
1616

1717
protected readonly Logger Logger = LogManager.GetCurrentClassLogger();
18-
private readonly object _invalidationLockObject = new object();
18+
private readonly object _statusLockObject = new object();
1919

2020
public abstract CodeKind TargetCodeKind { get; }
2121

@@ -25,6 +25,27 @@ protected RewriteSessionBase(IRewriterProvider rewriterProvider, Func<IRewriteSe
2525
_rewritingAllowed = rewritingAllowed;
2626
}
2727

28+
private RewriteSessionState _status = RewriteSessionState.Valid;
29+
public RewriteSessionState Status
30+
{
31+
get
32+
{
33+
lock (_statusLockObject)
34+
{
35+
return _status;
36+
}
37+
}
38+
set
39+
{
40+
lock (_statusLockObject)
41+
{
42+
if (_status == RewriteSessionState.Valid)
43+
{
44+
_status = value;
45+
}
46+
}
47+
}
48+
}
2849

2950
public IModuleRewriter CheckOutModuleRewriter(QualifiedModuleName module)
3051
{
@@ -35,6 +56,13 @@ public IModuleRewriter CheckOutModuleRewriter(QualifiedModuleName module)
3556

3657
rewriter = ModuleRewriter(module);
3758
CheckedOutModuleRewriters.Add(module, rewriter);
59+
60+
if (rewriter.IsDirty)
61+
{
62+
//The parse tree is stale.
63+
Status = RewriteSessionState.StaleParseTree;
64+
}
65+
3866
return rewriter;
3967
}
4068

@@ -48,9 +76,9 @@ public bool TryRewrite()
4876
}
4977

5078
//This is thread-safe because, once invalidated, there is no way back.
51-
if (IsInvalidated)
79+
if (Status != RewriteSessionState.Valid)
5280
{
53-
Logger.Warn("Tried to execute Rewrite on a RewriteSession that was already invalidated.");
81+
Logger.Warn($"Tried to execute Rewrite on a RewriteSession that was in the invalid status {Status}.");
5482
return false;
5583
}
5684

@@ -64,25 +92,5 @@ public bool TryRewrite()
6492
}
6593

6694
protected abstract bool TryRewriteInternal();
67-
68-
private bool _isInvalidated = false;
69-
public bool IsInvalidated
70-
{
71-
get
72-
{
73-
lock (_invalidationLockObject)
74-
{
75-
return _isInvalidated;
76-
}
77-
}
78-
}
79-
80-
public void Invalidate()
81-
{
82-
lock(_invalidationLockObject)
83-
{
84-
_isInvalidated = true;
85-
}
86-
}
8795
}
8896
}

Rubberduck.Parsing/Rewriter/RewritingManager.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ private bool TryAllowExclusiveRewrite(IRewriteSession rewriteSession)
5050
return false;
5151
}
5252

53+
rewriteSession.Status = RewriteSessionState.RewriteApplied;
54+
5355
InvalidateAllSessionsInternal();
5456
return true;
5557
}
@@ -80,13 +82,13 @@ private void InvalidateAllSessionsInternal()
8082
{
8183
foreach (var rewriteSession in _activeCodePaneSessions)
8284
{
83-
rewriteSession.Invalidate();
85+
rewriteSession.Status = RewriteSessionState.OtherSessionsRewriteApplied;
8486
}
8587
_activeCodePaneSessions.Clear();
8688

8789
foreach (var rewriteSession in _activeAttributesSessions)
8890
{
89-
rewriteSession.Invalidate();
91+
rewriteSession.Status = RewriteSessionState.OtherSessionsRewriteApplied;
9092
}
9193
_activeAttributesSessions.Clear();
9294
}

RubberduckTests/Rewriter/AttributesRewriteSessionTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,9 +89,9 @@ public void TargetsAttributesCode()
8989
Assert.AreEqual(CodeKind.AttributesCode, rewriteSession.TargetCodeKind);
9090
}
9191

92-
protected override IRewriteSession RewriteSession(IParseManager parseManager, Func<IRewriteSession, bool> rewritingAllowed, out MockRewriterProvider mockProvider)
92+
protected override IRewriteSession RewriteSession(IParseManager parseManager, Func<IRewriteSession, bool> rewritingAllowed, out MockRewriterProvider mockProvider, bool rewritersAreDirty = false)
9393
{
94-
mockProvider = new MockRewriterProvider();
94+
mockProvider = new MockRewriterProvider(rewritersAreDirty);
9595
return new AttributesRewriteSession(parseManager, mockProvider, rewritingAllowed);
9696
}
9797
}

RubberduckTests/Rewriter/CodePaneRewriteSessionTests.cs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using System;
2-
using System.Linq;
32
using Moq;
43
using NUnit.Framework;
54
using Rubberduck.Parsing.Rewriter;
@@ -41,7 +40,7 @@ public void DoesNotCallRequestsParseAfterRewriteIfInvalidated()
4140
var module = new QualifiedModuleName("TestProject", string.Empty, "TestModule");
4241
var otherModule = new QualifiedModuleName("TestProject", string.Empty, "OtherTestModule");
4342
rewriteSession.CheckOutModuleRewriter(module);
44-
rewriteSession.Invalidate();
43+
rewriteSession.Status = RewriteSessionState.OtherSessionsRewriteApplied;
4544
rewriteSession.CheckOutModuleRewriter(otherModule);
4645

4746
rewriteSession.TryRewrite();
@@ -99,9 +98,9 @@ public void TargetsCodePaneCode()
9998
Assert.AreEqual(CodeKind.CodePaneCode, rewriteSession.TargetCodeKind);
10099
}
101100

102-
protected override IRewriteSession RewriteSession(IParseManager parseManager, Func<IRewriteSession, bool> rewritingAllowed, out MockRewriterProvider mockProvider)
101+
protected override IRewriteSession RewriteSession(IParseManager parseManager, Func<IRewriteSession, bool> rewritingAllowed, out MockRewriterProvider mockProvider, bool rewritersAreDirty = false)
103102
{
104-
mockProvider = new MockRewriterProvider();
103+
mockProvider = new MockRewriterProvider(rewritersAreDirty);
105104
return new CodePaneRewriteSession(parseManager, mockProvider, rewritingAllowed);
106105
}
107106
}

0 commit comments

Comments
 (0)