Skip to content

Commit 4e965cf

Browse files
authored
MayBe: Fix flaky formatting on type (#2665)
1 parent e822994 commit 4e965cf

10 files changed

Lines changed: 76 additions & 1136 deletions

internal/fourslash/fourslash.go

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -735,12 +735,21 @@ func (f *FourslashTest) writeMsg(t *testing.T, msg *lsproto.Message) {
735735
}
736736

737737
func sendRequest[Params, Resp any](t *testing.T, f *FourslashTest, info lsproto.RequestInfo[Params, Resp], params Params) Resp {
738+
t.Helper()
739+
return sendRequestAndBaselineWorker(t, f, info, params, true)
740+
}
741+
742+
func sendRequestAndBaselineWorker[Params, Resp any](t *testing.T, f *FourslashTest, info lsproto.RequestInfo[Params, Resp], params Params, baselineProjects bool) Resp {
738743
t.Helper()
739744
prefix := f.getCurrentPositionPrefix()
740-
f.baselineState(t)
745+
if baselineProjects {
746+
f.baselineState(t)
747+
}
741748
f.baselineRequestOrNotification(t, info.Method, params)
742749
resMsg, result, resultOk := sendRequestWorker(t, f, info, params)
743-
f.baselineState(t)
750+
if baselineProjects {
751+
f.baselineState(t)
752+
}
744753
switch info.Method {
745754
case lsproto.MethodTextDocumentOnTypeFormatting:
746755
if !f.reportFormatOnTypeCrash {
@@ -764,8 +773,14 @@ func sendRequest[Params, Resp any](t *testing.T, f *FourslashTest, info lsproto.
764773

765774
func sendNotification[Params any](t *testing.T, f *FourslashTest, info lsproto.NotificationInfo[Params], params Params) {
766775
t.Helper()
767-
f.baselineState(t)
768-
f.updateState(info.Method, params)
776+
if info.Method != lsproto.MethodTextDocumentDidChange {
777+
// This is called eg when doing typeText = which is series of edits and formatting - which becomes non deterministic "after state"
778+
// The notification can only guarantee before state and thats what it baselines, but in case of type it creates
779+
// multiple edits which results in getting different state -based on if the snapshot was updated or not at the time of formatting requests
780+
// So this is used for all the incremental edits - to baseline only request data but not project state between those edits
781+
f.baselineState(t)
782+
f.updateState(info.Method, params)
783+
}
769784
f.baselineRequestOrNotification(t, info.Method, params)
770785
sendNotificationWorker(t, f, info, params)
771786
}
@@ -1665,7 +1680,6 @@ func (f *FourslashTest) VerifyImportFixAtPosition(t *testing.T, expectedTexts []
16651680
// Save the original content before any edits
16661681
script := f.getScriptInfo(f.activeFilename)
16671682
originalContent := script.content
1668-
16691683
// For each import action, apply it and check the result
16701684
actualTextArray := make([]string, 0, len(importActions))
16711685
for _, action := range importActions {
@@ -2809,19 +2823,22 @@ func roundtripThroughJson[T any](value any) (T, error) {
28092823
// Insert text at the current caret position.
28102824
func (f *FourslashTest) Insert(t *testing.T, text string) {
28112825
t.Helper()
2826+
f.baselineState(t)
28122827
f.typeText(t, text)
28132828
}
28142829

28152830
// Insert text and a new line at the current caret position.
28162831
func (f *FourslashTest) InsertLine(t *testing.T, text string) {
28172832
t.Helper()
2833+
f.baselineState(t)
28182834
f.typeText(t, text+"\n")
28192835
}
28202836

28212837
// Removes the text at the current caret position as if the user pressed backspace `count` times.
28222838
func (f *FourslashTest) Backspace(t *testing.T, count int) {
28232839
script := f.getScriptInfo(f.activeFilename)
28242840
offset := int(f.converters.LineAndCharacterToPosition(script, f.currentCaretPosition))
2841+
f.baselineState(t)
28252842

28262843
for range count {
28272844
offset--
@@ -2837,6 +2854,7 @@ func (f *FourslashTest) Backspace(t *testing.T, count int) {
28372854
func (f *FourslashTest) DeleteAtCaret(t *testing.T, count int) {
28382855
script := f.getScriptInfo(f.activeFilename)
28392856
offset := int(f.converters.LineAndCharacterToPosition(script, f.currentCaretPosition))
2857+
f.baselineState(t)
28402858

28412859
for range count {
28422860
f.editScriptAndUpdateMarkers(t, f.activeFilename, offset, offset+1, "")
@@ -2848,11 +2866,12 @@ func (f *FourslashTest) DeleteAtCaret(t *testing.T, count int) {
28482866
func (f *FourslashTest) Paste(t *testing.T, text string) {
28492867
script := f.getScriptInfo(f.activeFilename)
28502868
start := int(f.converters.LineAndCharacterToPosition(script, f.currentCaretPosition))
2869+
f.baselineState(t)
28512870
f.editScriptAndUpdateMarkers(t, f.activeFilename, start, start, text)
28522871

28532872
// post-paste fomatting
28542873
if f.stateEnableFormatting {
2855-
result := sendRequest(t, f, lsproto.TextDocumentRangeFormattingInfo, &lsproto.DocumentRangeFormattingParams{
2874+
result := sendRequestAndBaselineWorker(t, f, lsproto.TextDocumentRangeFormattingInfo, &lsproto.DocumentRangeFormattingParams{
28562875
TextDocument: lsproto.TextDocumentIdentifier{
28572876
Uri: lsconv.FileNameToDocumentURI(f.activeFilename),
28582877
},
@@ -2861,7 +2880,7 @@ func (f *FourslashTest) Paste(t *testing.T, text string) {
28612880
End: f.converters.PositionToLineAndCharacter(script, core.TextPos(start+len(text))),
28622881
},
28632882
Options: f.userPreferences.FormatCodeSettings.ToLSFormatOptions(),
2864-
})
2883+
}, false)
28652884
if result.TextEdits != nil {
28662885
f.applyTextEdits(t, *result.TextEdits)
28672886
}
@@ -2871,6 +2890,7 @@ func (f *FourslashTest) Paste(t *testing.T, text string) {
28712890

28722891
// Selects a line and replaces it with a new text.
28732892
func (f *FourslashTest) ReplaceLine(t *testing.T, lineIndex int, text string) {
2893+
f.baselineState(t)
28742894
f.selectLine(t, lineIndex)
28752895
f.typeText(t, text)
28762896
}
@@ -2944,6 +2964,12 @@ func (f *FourslashTest) applyTextEdits(t *testing.T, edits []*lsproto.TextEdit)
29442964
}
29452965

29462966
func (f *FourslashTest) Replace(t *testing.T, start int, length int, text string) {
2967+
f.baselineState(t)
2968+
f.replaceWorker(t, start, length, text)
2969+
}
2970+
2971+
func (f *FourslashTest) replaceWorker(t *testing.T, start int, length int, text string) {
2972+
t.Helper()
29472973
f.editScriptAndUpdateMarkers(t, f.activeFilename, start, start+length, text)
29482974
// f.checkPostEditInvariants() // !!! do we need this?
29492975
}
@@ -2958,7 +2984,7 @@ func (f *FourslashTest) typeText(t *testing.T, text string) {
29582984

29592985
script := f.getScriptInfo(f.activeFilename)
29602986
selection := f.getSelection()
2961-
f.Replace(t, selection.Pos(), selection.End()-selection.Pos(), "")
2987+
f.replaceWorker(t, selection.Pos(), selection.End()-selection.Pos(), "")
29622988

29632989
totalSize := 0
29642990

@@ -2973,14 +2999,14 @@ func (f *FourslashTest) typeText(t *testing.T, text string) {
29732999

29743000
// Handle post-keystroke formatting
29753001
if f.stateEnableFormatting {
2976-
result := sendRequest(t, f, lsproto.TextDocumentOnTypeFormattingInfo, &lsproto.DocumentOnTypeFormattingParams{
3002+
result := sendRequestAndBaselineWorker(t, f, lsproto.TextDocumentOnTypeFormattingInfo, &lsproto.DocumentOnTypeFormattingParams{
29773003
TextDocument: lsproto.TextDocumentIdentifier{
29783004
Uri: lsconv.FileNameToDocumentURI(f.activeFilename),
29793005
},
29803006
Position: f.currentCaretPosition,
29813007
Ch: string(r),
29823008
Options: f.userPreferences.FormatCodeSettings.ToLSFormatOptions(),
2983-
})
3009+
}, false)
29843010
if result.TextEdits != nil {
29853011
offset += f.applyTextEdits(t, *result.TextEdits)
29863012
}

0 commit comments

Comments
 (0)