Skip to content

Commit 39bd248

Browse files
committed
Add recovery on next parse option for code pane open state
This also reworks the the recovery on next parse for selections and the active code pane forcing the execution order to be: open state -> selection -> active pane
1 parent 1a34d93 commit 39bd248

File tree

3 files changed

+165
-16
lines changed

3 files changed

+165
-16
lines changed

Rubberduck.Parsing/Rewriter/ISelectionRecoverer.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,6 @@ public interface ISelectionRecoverer
1717

1818
void SaveOpenState(IEnumerable<QualifiedModuleName> modules);
1919
void RecoverOpenState();
20+
void RecoverOpenStateOnNextParse();
2021
}
2122
}

Rubberduck.Parsing/Rewriter/SelectionRecoverer.cs

Lines changed: 64 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -62,16 +62,14 @@ public void RecoverSavedSelections()
6262

6363
public void RecoverSavedSelectionsOnNextParse()
6464
{
65-
_parseManager.StateChanged += ExecuteSelectionRecovery;
65+
PrimeRecoveryOnNextParse();
66+
_selectionRecoveryPrimed = true;
6667
}
6768

68-
private void ExecuteSelectionRecovery(object sender, ParserStateEventArgs e)
69+
private void ExecuteSelectionRecovery()
6970
{
70-
if (e.State == ParserStateOnWhichToTriggerRecovery)
71-
{
72-
_parseManager.StateChanged -= ExecuteSelectionRecovery;
73-
RecoverSavedSelections();
74-
}
71+
_selectionRecoveryPrimed = false;
72+
RecoverSavedSelections();
7573
}
7674

7775

@@ -91,16 +89,14 @@ public void RecoverActiveCodePane()
9189

9290
public void RecoverActiveCodePaneOnNextParse()
9391
{
94-
_parseManager.StateChanged += ExecuteActiveCodePaneRecovery;
92+
PrimeRecoveryOnNextParse();
93+
_activeCodePaneRecoveryPrimed = true;
9594
}
9695

97-
private void ExecuteActiveCodePaneRecovery(object sender, ParserStateEventArgs e)
96+
private void ExecuteActiveCodePaneRecovery()
9897
{
99-
if (e.State == ParserStateOnWhichToTriggerRecovery)
100-
{
101-
_parseManager.StateChanged -= ExecuteActiveCodePaneRecovery;
102-
RecoverActiveCodePane();
103-
}
98+
_activeCodePaneRecoveryPrimed = false;
99+
RecoverActiveCodePane();
104100
}
105101

106102
public void SaveOpenState(IEnumerable<QualifiedModuleName> modules)
@@ -119,10 +115,62 @@ public void RecoverOpenState()
119115
_savedOpenModules.Clear();
120116
}
121117

118+
public void RecoverOpenStateOnNextParse()
119+
{
120+
PrimeRecoveryOnNextParse();
121+
_openStateRecoveryPrimed = true;
122+
}
123+
124+
private void ExecuteOpenStateRecovery()
125+
{
126+
_openStateRecoveryPrimed = false;
127+
RecoverOpenState();
128+
}
129+
130+
private bool _selectionRecoveryPrimed;
131+
private bool _openStateRecoveryPrimed;
132+
private bool _activeCodePaneRecoveryPrimed;
133+
134+
private bool RecoveryPrimed => _selectionRecoveryPrimed || _openStateRecoveryPrimed || _activeCodePaneRecoveryPrimed;
135+
136+
private void ExecuteRecovery(object sender, ParserStateEventArgs e)
137+
{
138+
if (e.State != ParserStateOnWhichToTriggerRecovery)
139+
{
140+
return;
141+
}
142+
143+
_parseManager.StateChanged -= ExecuteRecovery;
144+
145+
if (_openStateRecoveryPrimed)
146+
{
147+
ExecuteOpenStateRecovery();
148+
}
149+
150+
if (_selectionRecoveryPrimed)
151+
{
152+
ExecuteSelectionRecovery();
153+
}
154+
155+
if (_activeCodePaneRecoveryPrimed)
156+
{
157+
ExecuteActiveCodePaneRecovery();
158+
}
159+
}
160+
161+
private void PrimeRecoveryOnNextParse()
162+
{
163+
if (RecoveryPrimed)
164+
{
165+
return;
166+
}
167+
168+
_parseManager.StateChanged += ExecuteRecovery;
169+
}
170+
122171
public void Dispose()
123172
{
124-
_parseManager.StateChanged -= ExecuteSelectionRecovery;
125-
_parseManager.StateChanged -= ExecuteActiveCodePaneRecovery;
173+
_parseManager.StateChanged -= ExecuteRecovery;
126174
}
127175
}
128176
}

RubberduckTests/Rewriter/SelectionRecovererTests.cs

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -512,13 +512,113 @@ public void ActivatesOpenModulesWithSavedOpenStateOnRecoverOpenState()
512512
selectionServiceMock.Verify(m => m.TryActivate(_testModuleSelections[3].QualifiedName), Times.Never);
513513
}
514514

515+
[Test]
516+
[Category("Rewriting")]
517+
public void ActivatesOpenModulesWithSavedOpenStateOnNextParseAfterRecoverOpenStateOnNextParse()
518+
{
519+
var selectionServiceMock = TestSelectionServiceMock();
520+
var openModules = _testModuleSelections.Take(3)
521+
.Select(qualifiedSelection => qualifiedSelection.QualifiedName)
522+
.ToHashSet();
523+
selectionServiceMock.Setup(m => m.OpenModules()).Returns(openModules);
524+
525+
var parseManagerMock = new Mock<IParseManager>();
526+
var selectionRecoverer = new SelectionRecoverer(selectionServiceMock.Object, parseManagerMock.Object);
527+
528+
var modulesForWhichToSaveOpenState = _testModuleSelections.Skip(1)
529+
.Select(qualifiedSelection => qualifiedSelection.QualifiedName)
530+
.ToHashSet();
531+
532+
selectionRecoverer.SaveOpenState(modulesForWhichToSaveOpenState);
533+
selectionRecoverer.RecoverOpenStateOnNextParse();
534+
535+
var stateEventArgs = new ParserStateEventArgs(_stateExpectedToTriggerTheRecovery, ParserState.Pending,
536+
CancellationToken.None);
537+
parseManagerMock.Raise(m => m.StateChanged += null, stateEventArgs);
538+
539+
selectionServiceMock.Verify(m => m.TryActivate(_testModuleSelections[1].QualifiedName), Times.Once);
540+
selectionServiceMock.Verify(m => m.TryActivate(_testModuleSelections[2].QualifiedName), Times.Once);
541+
selectionServiceMock.Verify(m => m.TryActivate(_testModuleSelections[0].QualifiedName), Times.Never);
542+
selectionServiceMock.Verify(m => m.TryActivate(_testModuleSelections[3].QualifiedName), Times.Never);
543+
}
544+
545+
[Test]
546+
[Category("Rewriting")]
547+
public void OnNextParseAfterSetupOpenModulesWithSavedOpenStateBeforeSettingSelection()
548+
{
549+
var lastCalledMethod = string.Empty;
550+
var selectionServiceMock = TestSelectionServiceMock();
551+
selectionServiceMock.Setup(m => m.TryActivate(It.IsAny<QualifiedModuleName>()))
552+
.Callback((QualifiedModuleName module) => lastCalledMethod = "TryActivate");
553+
selectionServiceMock.Setup(m => m.TrySetSelection(It.IsAny<QualifiedModuleName>(), It.IsAny<Selection>()))
554+
.Callback((QualifiedModuleName module, Selection selection) => lastCalledMethod = "TrySetSelection");
555+
556+
var openModules = _testModuleSelections.Take(3)
557+
.Select(qualifiedSelection => qualifiedSelection.QualifiedName)
558+
.ToHashSet();
559+
560+
selectionServiceMock.Setup(m => m.OpenModules()).Returns(openModules);
561+
562+
var parseManagerMock = new Mock<IParseManager>();
563+
var selectionRecoverer = new SelectionRecoverer(selectionServiceMock.Object, parseManagerMock.Object);
564+
565+
var modulesForWhichToSaveOpenState = _testModuleSelections.Skip(1)
566+
.Select(qualifiedSelection => qualifiedSelection.QualifiedName)
567+
.ToHashSet();
568+
var selectionRecoveryModules = _testModuleSelections
569+
.Select(qualifiedSelection => qualifiedSelection.QualifiedName).Take(2);
570+
571+
selectionRecoverer.SaveSelections(selectionRecoveryModules);
572+
selectionRecoverer.RecoverSavedSelectionsOnNextParse();
573+
selectionRecoverer.SaveOpenState(modulesForWhichToSaveOpenState);
574+
selectionRecoverer.RecoverOpenStateOnNextParse();
575+
576+
var stateEventArgs = new ParserStateEventArgs(_stateExpectedToTriggerTheRecovery, ParserState.Pending,
577+
CancellationToken.None);
578+
parseManagerMock.Raise(m => m.StateChanged += null, stateEventArgs);
579+
580+
Assert.AreEqual("TrySetSelection", lastCalledMethod);
581+
}
582+
583+
[Test]
584+
[Category("Rewriting")]
585+
public void OnNextParseAfterSetupSetsSelectionsBeforeReactivatingTheActiveCodePane()
586+
{
587+
var lastCalledMethod = string.Empty;
588+
var selectionServiceMock = TestSelectionServiceMock();
589+
selectionServiceMock.Setup(m => m.TryActivate(It.IsAny<QualifiedModuleName>()))
590+
.Callback((QualifiedModuleName module) => lastCalledMethod = "TryActivate");
591+
selectionServiceMock.Setup(m => m.TrySetSelection(It.IsAny<QualifiedModuleName>(), It.IsAny<Selection>()))
592+
.Callback((QualifiedModuleName module, Selection selection) => lastCalledMethod = "TrySetSelection");
593+
594+
selectionServiceMock.Setup(m => m.ActiveSelection()).Returns(() => _testModuleSelections[1]);
595+
596+
var parseManagerMock = new Mock<IParseManager>();
597+
var selectionRecoverer = new SelectionRecoverer(selectionServiceMock.Object, parseManagerMock.Object);
598+
599+
var selectionRecoveryModules = _testModuleSelections
600+
.Select(qualifiedSelection => qualifiedSelection.QualifiedName).Take(2);
601+
602+
selectionRecoverer.SaveSelections(selectionRecoveryModules);
603+
selectionRecoverer.RecoverSavedSelectionsOnNextParse();
604+
selectionRecoverer.SaveActiveCodePane();
605+
selectionRecoverer.RecoverActiveCodePaneOnNextParse();
606+
607+
var stateEventArgs = new ParserStateEventArgs(_stateExpectedToTriggerTheRecovery, ParserState.Pending,
608+
CancellationToken.None);
609+
parseManagerMock.Raise(m => m.StateChanged += null, stateEventArgs);
610+
611+
Assert.AreEqual("TryActivate", lastCalledMethod);
612+
}
613+
515614
private Mock<ISelectionService> TestSelectionServiceMock()
516615
{
517616
var mock = new Mock<ISelectionService>();
518617
foreach (var qualifiedSelection in _testModuleSelections)
519618
{
520619
mock.Setup(m => m.Selection(qualifiedSelection.QualifiedName)).Returns(qualifiedSelection.Selection);
521620
}
621+
mock.Setup(m => m.OpenModules()).Returns(() => _testModuleSelections.Select(qualifiedSelection => qualifiedSelection.QualifiedName).ToList());
522622

523623
mock.Setup(m => m.TrySetSelection(It.IsAny<QualifiedModuleName>(), It.IsAny<Selection>()));
524624
mock.Setup(m => m.TryActivate(It.IsAny<QualifiedModuleName>()));

0 commit comments

Comments
 (0)