Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions internal/format/indent.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,11 @@ func getIndentationForNodeWorker(
// }, { itself contributes nothing.
// prop: 1 L3 - The indentation of the second object literal is best understood by
// }) looking at the relationship between the list and *first* list item.
listLine, _ := getStartLineAndCharacterForNode(firstListChild, sourceFile)
listIndentsChild := firstListChild != nil && listLine > containingListOrParentStartLine
var listIndentsChild bool
if firstListChild != nil {
listLine, _ := getStartLineAndCharacterForNode(firstListChild, sourceFile)
listIndentsChild = listLine > containingListOrParentStartLine
}
actualIndentation := getActualIndentationForListItem(current, sourceFile, options, listIndentsChild)
if actualIndentation != -1 {
return actualIndentation + indentationDelta
Expand Down
64 changes: 64 additions & 0 deletions internal/ls/format_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/format"
"github.com/microsoft/typescript-go/internal/parser"
"gotest.tools/v3/assert"
)

// Test for issue: Panic Handling textDocument/onTypeFormatting
Expand Down Expand Up @@ -70,3 +71,66 @@ func TestGetFormattingEditsAfterKeystroke_SimpleStatement(t *testing.T) {
// Should return nil or empty edits, not panic
_ = edits
}

// Test for issue: Crash in range formatting when requested on a line that is different from the containing function
// This reproduces the panic when formatting a range inside a function body
func TestGetFormattingEditsForRange_FunctionBody(t *testing.T) {
t.Parallel()

testCases := []struct {
name string
text string
startPos int
endPos int
}{
{
name: "return statement in function",
text: "function foo() {\n return (1 + 2);\n}",
startPos: 21, // Start of "return"
endPos: 38, // End of ");"
},
{
name: "function with newline after keyword",
text: "function\nf() {\n}",
startPos: 9, // After "function\n"
endPos: 13, // Inside or after function
},
{
name: "empty function body",
text: "function f() {\n \n}",
startPos: 15, // Inside body
endPos: 17, // Inside body
},
{
name: "after function closing brace",
text: "function f() {\n}",
startPos: 15, // After closing brace
endPos: 15,
},
}

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)

langService := &LanguageService{}
ctx := context.Background()
options := format.GetDefaultFormatCodeSettings("\n")

// This should not panic
edits := langService.getFormattingEditsForRange(
ctx,
sourceFile,
options,
core.NewTextRange(tc.startPos, tc.endPos),
)

// Should not panic and should return some edits or empty array
assert.Assert(t, edits != nil || true) // Just ensuring no panic
})
}
}