Skip to content

Commit

Permalink
fix(tools/spxls): correct spx source file formatter
Browse files Browse the repository at this point in the history
Signed-off-by: Aofei Sheng <[email protected]>
  • Loading branch information
aofei committed Dec 26, 2024
1 parent b64a5ab commit f997379
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 53 deletions.
41 changes: 20 additions & 21 deletions tools/spxls/internal/server/format.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ func (s *Server) textDocumentFormatting(params *DocumentFormattingParams) ([]Tex

// Simply replace the entire document.
lines := bytes.Count(content, []byte("\n"))
lastLineLen := len(bytes.TrimRight(bytes.Split(content, []byte("\n"))[lines], "\r"))
lastNewLine := bytes.LastIndex(content, []byte("\n"))
lastLineLen := len(content) - (lastNewLine + 1)
return []TextEdit{
{
Range: Range{
Expand All @@ -68,20 +69,15 @@ func formatSpx(src []byte) ([]byte, error) {

// Find all var blocks and function declarations.
var (
firstVarBlock *gopast.GenDecl
varsToMerge []*gopast.GenDecl
funcDecls []gopast.Decl
otherDecls []gopast.Decl
varBlocks []*gopast.GenDecl
funcDecls []gopast.Decl
otherDecls []gopast.Decl
)
for _, decl := range f.Decls {
switch d := decl.(type) {
case *gopast.GenDecl:
if d.Tok == goptoken.VAR {
if firstVarBlock == nil {
firstVarBlock = d
} else {
varsToMerge = append(varsToMerge, d)
}
varBlocks = append(varBlocks, d)
} else {
otherDecls = append(otherDecls, d)
}
Expand All @@ -96,20 +92,23 @@ func formatSpx(src []byte) ([]byte, error) {
//
// See https://github.com/goplus/builder/issues/591 and https://github.com/goplus/builder/issues/752.
newDecls := make([]gopast.Decl, 0, len(f.Decls))
if firstVarBlock != nil {
if len(varBlocks) > 0 {
// Merge multiple var blocks into a single one.
firstVarBlock.Lparen = goptoken.Pos(1) // Force parenthesized form.
for _, varBlock := range varsToMerge {
for _, spec := range varBlock.Specs {
valueSpec, ok := spec.(*gopast.ValueSpec)
if !ok {
return nil, fmt.Errorf("unexpected non-value spec in var block: %T", spec)
}
for _, name := range valueSpec.Names {
name.NamePos = goptoken.NoPos
firstVarBlock := varBlocks[0]
firstVarBlock.Lparen = firstVarBlock.Pos()
if len(varBlocks) > 1 {
firstVarBlock.Rparen = varBlocks[len(varBlocks)-1].End()
for _, varBlock := range varBlocks[1:] {
for _, spec := range varBlock.Specs {
valueSpec, ok := spec.(*gopast.ValueSpec)
if !ok {
return nil, fmt.Errorf("unexpected non-value spec in var block: %T", spec)
}
firstVarBlock.Specs = append(firstVarBlock.Specs, valueSpec)
}
firstVarBlock.Specs = append(firstVarBlock.Specs, valueSpec)
}
} else {
firstVarBlock.Rparen = firstVarBlock.End()
}
newDecls = append(newDecls, firstVarBlock)
}
Expand Down
90 changes: 58 additions & 32 deletions tools/spxls/internal/server/format_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (

"github.com/goplus/builder/tools/spxls/internal/vfs"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestServerTextDocumentFormatting(t *testing.T) {
Expand All @@ -27,9 +28,9 @@ run "assets", { Title: "Bullet (by Go+)" }
}

edits, err := s.textDocumentFormatting(params)
assert.NoError(t, err)
assert.Len(t, edits, 1)
assert.Equal(t, TextEdit{
require.NoError(t, err)
require.Len(t, edits, 1)
assert.Contains(t, edits, TextEdit{
Range: Range{
Start: Position{Line: 0, Character: 0},
End: Position{Line: 8, Character: 0},
Expand All @@ -43,7 +44,7 @@ var (
run "assets", {Title: "Bullet (by Go+)"}
`,
}, edits[0])
})
})

t.Run("NonSpxFile", func(t *testing.T) {
Expand All @@ -57,8 +58,8 @@ run "assets", {Title: "Bullet (by Go+)"}
}

edits, err := s.textDocumentFormatting(params)
assert.NoError(t, err)
assert.Nil(t, edits)
require.NoError(t, err)
require.Nil(t, edits)
})

t.Run("FileNotFound", func(t *testing.T) {
Expand All @@ -70,8 +71,8 @@ run "assets", {Title: "Bullet (by Go+)"}
}

edits, err := s.textDocumentFormatting(params)
assert.Error(t, err)
assert.Nil(t, edits)
require.Error(t, err)
require.Nil(t, edits)
})

t.Run("NoChangesNeeded", func(t *testing.T) {
Expand All @@ -85,8 +86,8 @@ run "assets", {Title: "Bullet (by Go+)"}
}

edits, err := s.textDocumentFormatting(params)
assert.NoError(t, err)
assert.Nil(t, edits)
require.NoError(t, err)
require.Nil(t, edits)
})

t.Run("FormatError", func(t *testing.T) {
Expand All @@ -100,29 +101,43 @@ run "assets", {Title: "Bullet (by Go+)"}
}

edits, err := s.textDocumentFormatting(params)
assert.Error(t, err)
assert.Contains(t, err.Error(), "failed to format document")
assert.Nil(t, edits)
require.Error(t, err)
require.Contains(t, err.Error(), "failed to format document")
require.Nil(t, edits)
})

t.Run("WithFormatSpx", func(t *testing.T) {
s := New(vfs.NewMapFS(func() map[string][]byte {
return map[string][]byte{
"main.spx": []byte(`// A spx game.
func Func1() {}
var (
MyAircraft MyAircraft
// The aircraft.
MyAircraft MyAircraft // The only aircraft.
)
func Func2() {}
var Bullet Bullet // The first bullet.
var Bullet Bullet
// The second bullet.
var Bullet2 Bullet
func Func3() {}
var (
// The third bullet.
Bullet3 Bullet
)
run "assets", {Title: "Bullet (by Go+)"}
// The fifth var block.
var (
Bullet4 Bullet // The fourth bullet.
)
// The last var block.
var (
// The fifth bullet.
Bullet5 Bullet
Bullet6 Bullet // The sixth bullet.
)
`),
}
}), nil)
Expand All @@ -131,28 +146,39 @@ run "assets", {Title: "Bullet (by Go+)"}
}

edits, err := s.textDocumentFormatting(params)
assert.NoError(t, err)
assert.Len(t, edits, 1)
assert.Equal(t, TextEdit{
require.NoError(t, err)
require.Len(t, edits, 1)
assert.Contains(t, edits, TextEdit{
Range: Range{
Start: Position{Line: 0, Character: 0},
End: Position{Line: 15, Character: 0},
End: Position{Line: 29, Character: 0},
},
NewText: `// A spx game.
var (
MyAircraft MyAircraft
Bullet Bullet
)
// The aircraft.
MyAircraft MyAircraft // The only aircraft.
func Func1() {}
Bullet Bullet // The first bullet.
func Func2() {}
// The second bullet.
Bullet2 Bullet
func Func3() {}
// The third bullet.
Bullet3 Bullet
run "assets", {Title: "Bullet (by Go+)"}
// The fifth var block.
Bullet4 Bullet // The fourth bullet.
// The last var block.
// The fifth bullet.
Bullet5 Bullet
Bullet6 Bullet // The sixth bullet.
)
`,
}, edits[0])
})
})
}

0 comments on commit f997379

Please sign in to comment.