From a3ba946a9bd9cfc93783945a6076307cbf4b3f0f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 8 Nov 2025 16:28:24 +0000 Subject: [PATCH 1/3] Initial plan From b7c0f58f4dba3135bc7bbf71b3e7f204e58c2915 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 8 Nov 2025 16:40:03 +0000 Subject: [PATCH 2/3] Add failing test for nil pointer panic in FormatOnEnter Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com> --- internal/format/format_test.go | 55 ++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/internal/format/format_test.go b/internal/format/format_test.go index 1bf7945076..5268a8cc1e 100644 --- a/internal/format/format_test.go +++ b/internal/format/format_test.go @@ -1,6 +1,7 @@ package format_test import ( + "context" "strings" "testing" @@ -58,3 +59,57 @@ func TestFormatNoTrailingNewline(t *testing.T) { }) } } + +// Test for panic in childStartsOnTheSameLineWithElseInIfStatement +// when FindPrecedingToken returns nil (Issue: panic handling request textDocument/onTypeFormatting) +func TestFormatOnEnter_NilPrecedingToken(t *testing.T) { + t.Parallel() + + // Test case where else statement is at the beginning of the file + // which can cause FindPrecedingToken to return nil + testCases := []struct { + name string + text string + position int // position where enter is pressed + }{ + { + name: "else at file start - edge case", + text: "if(a){}\nelse{}", + position: 9, // After the newline, before 'else' + }, + { + name: "simple if-else with enter after if block", + text: "if (true) {\n}\nelse {\n}", + position: 13, // After "}\n", before "else" + }, + { + name: "if-else with enter in else block", + text: "if (true) {\n} else {\n}", + position: 21, // Inside else block + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + sourceFile := parser.ParseSourceFile(ast.SourceFileParseOptions{ + FileName: "/test.ts", + Path: "/test.ts", + }, tc.text, core.ScriptKindTS) + + ctx := format.WithFormatCodeSettings(context.Background(), &format.FormatCodeSettings{ + EditorSettings: format.EditorSettings{ + TabSize: 4, + IndentSize: 4, + NewLineCharacter: "\n", + ConvertTabsToSpaces: true, + IndentStyle: format.IndentStyleSmart, + }, + }, "\n") + + // This should not panic + edits := format.FormatOnEnter(ctx, sourceFile, tc.position) + _ = edits // Just ensuring no panic + }) + } +} From 57e17692ce42c10c055b7fb41514f1defae1c2db Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 8 Nov 2025 16:45:07 +0000 Subject: [PATCH 3/3] Fix nil pointer panic in childStartsOnTheSameLineWithElseInIfStatement Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com> --- internal/format/indent.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/internal/format/indent.go b/internal/format/indent.go index 170873c2a4..1e581ffa7a 100644 --- a/internal/format/indent.go +++ b/internal/format/indent.go @@ -242,6 +242,12 @@ func findFirstNonWhitespaceCharacterAndColumn(startPos int, endPos int, sourceFi func childStartsOnTheSameLineWithElseInIfStatement(parent *ast.Node, child *ast.Node, childStartLine int, sourceFile *ast.SourceFile) bool { if parent.Kind == ast.KindIfStatement && parent.AsIfStatement().ElseStatement == child { elseKeyword := astnav.FindPrecedingToken(sourceFile, child.Pos()) + if elseKeyword == nil { + // In edge cases, FindPrecedingToken can return nil (e.g., else at file start). + // When this happens, we can't determine if the else keyword is on the same line, + // so we return false to avoid a nil pointer dereference. + return false + } debug.AssertIsDefined(elseKeyword) elseKeywordStartLine, _ := getStartLineAndCharacterForNode(elseKeyword, sourceFile) return elseKeywordStartLine == childStartLine