diff --git a/internal/format/format_test.go b/internal/format/format_test.go index 1bf7945076..7afc2cbfe4 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,62 @@ func TestFormatNoTrailingNewline(t *testing.T) { }) } } + +// Test for panic handling request textDocument/onTypeFormatting (issue #2042) +// The panic occurs when nodes in a list can be nil, causing deriveActualIndentationFromList +// to panic when accessing node properties +func TestFormatOnEnter_NilNodesInList(t *testing.T) { + t.Parallel() + + // Test cases that can produce nil nodes in AST lists + testCases := []struct { + name string + text string + position int // position where enter is pressed + }{ + { + name: "empty file", + text: "", + position: 0, + }, + { + name: "simple statement", + text: "const x = 1;", + position: 12, + }, + { + name: "incomplete code", + text: "if (", + position: 4, + }, + { + name: "malformed syntax", + text: "function f() { return }", + position: 21, + }, + } + + 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 even with nil nodes in lists + edits := format.FormatOnEnter(ctx, sourceFile, tc.position) + _ = edits // Just ensuring no panic + }) + } +} diff --git a/internal/format/indent.go b/internal/format/indent.go index 170873c2a4..cabff17e36 100644 --- a/internal/format/indent.go +++ b/internal/format/indent.go @@ -177,6 +177,9 @@ func deriveActualIndentationFromList(list *ast.NodeList, index int, sourceFile * debug.Assert(list != nil && index >= 0 && index < len(list.Nodes)) node := list.Nodes[index] + if node == nil { + return -1 + } // walk toward the start of the list starting from current node and check if the line is the same for all items. // if end line for item [i - 1] differs from the start line for item [i] - find column of the first non-whitespace character on the line of item [i] @@ -184,6 +187,9 @@ func deriveActualIndentationFromList(list *ast.NodeList, index int, sourceFile * line, char := getStartLineAndCharacterForNode(node, sourceFile) for i := index; i >= 0; i-- { + if list.Nodes[i] == nil { + continue + } if list.Nodes[i].Kind == ast.KindCommaToken { continue }