From 481a555db27a274744217954c142ceb4663c262a Mon Sep 17 00:00:00 2001 From: Aofei Sheng Date: Thu, 26 Dec 2024 14:57:28 +0800 Subject: [PATCH] fix(tools/spxls): correct spx source file formatter (#1175) Signed-off-by: Aofei Sheng --- tools/spxls/internal/server/format.go | 41 +++++----- tools/spxls/internal/server/format_test.go | 90 ++++++++++++++-------- 2 files changed, 78 insertions(+), 53 deletions(-) diff --git a/tools/spxls/internal/server/format.go b/tools/spxls/internal/server/format.go index 97e4760d1..3901f8c69 100644 --- a/tools/spxls/internal/server/format.go +++ b/tools/spxls/internal/server/format.go @@ -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{ @@ -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) } @@ -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) } diff --git a/tools/spxls/internal/server/format_test.go b/tools/spxls/internal/server/format_test.go index 94f30087d..8117cabdb 100644 --- a/tools/spxls/internal/server/format_test.go +++ b/tools/spxls/internal/server/format_test.go @@ -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) { @@ -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}, @@ -43,7 +44,7 @@ var ( run "assets", {Title: "Bullet (by Go+)"} `, - }, edits[0]) + }) }) t.Run("NonSpxFile", func(t *testing.T) { @@ -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) { @@ -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) { @@ -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) { @@ -100,9 +101,9 @@ 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) { @@ -110,19 +111,33 @@ run "assets", {Title: "Bullet (by Go+)"} 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) @@ -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]) + }) }) }