From 568b77179c693bec2b7b77b2deff0ceca2b752b4 Mon Sep 17 00:00:00 2001 From: karitham Date: Fri, 3 Apr 2026 15:02:48 +0200 Subject: [PATCH] refactor(format)!: remove globals and implement proper DI BREAKING CHANGE: All format functions now require Options parameter. Removes the 4 global variables from format/options.go: - var Indent - var Align - var FieldLineComma - var TrailingNewline All format functions now accept Options as first parameter: - FormatDocument, FormatDocumentWithValidation, etc. - All MustFormat* functions Implements proper dependency injection chain: main() -> NewStreamServer(opts) -> Server.formatOpts -> formatting() Cleans up redundant indent parameters - top-level formatters (Struct, Union, Exception, Enum, Service) now use opts.GetIndent() internally instead of accepting indent parameter. Benefits: - Thread-safe concurrent formatting - Testable with different configs - Easy to add new options without signature changes - No hidden dependencies --- format/annotation.go | 18 +++++------ format/annotation_test.go | 6 ++-- format/comment.go | 4 +-- format/comment_test.go | 5 +-- format/const.go | 18 +++++------ format/constvalue.go | 28 ++++++++--------- format/constvalue_test.go | 3 +- format/document.go | 54 ++++++++++++++++----------------- format/document_include_test.go | 22 +++++++++----- format/document_test.go | 23 +++++++------- format/enum.go | 16 +++++----- format/enumvalue.go | 24 +++++++-------- format/exception.go | 16 +++++----- format/field.go | 46 ++++++++++++++-------------- format/field_test.go | 3 +- format/function.go | 41 +++++++++++++------------ format/identifier.go | 4 +-- format/include.go | 20 ++++++------ format/keyword.go | 4 +-- format/literal.go | 4 +-- format/namespace.go | 12 ++++---- format/options.go | 32 +++++++++++-------- format/service.go | 20 ++++++------ format/struct.go | 16 +++++----- format/struct_test.go | 3 +- format/typedef.go | 12 ++++---- format/union.go | 16 +++++----- format/utils.go | 10 +++--- format/utils_test.go | 3 +- lsp/codejump/hover.go | 24 ++++++++++----- lsp/format.go | 5 ++- lsp/impl_test.go | 29 +++++++++--------- lsp/server.go | 13 +++++--- lsp/stream.go | 12 +++++--- lsp/symbols/field.go | 4 ++- lsp/symbols/typedef.go | 5 ++- main.go | 10 ++++-- 37 files changed, 316 insertions(+), 269 deletions(-) diff --git a/format/annotation.go b/format/annotation.go index d9fc0a1..f21f2c4 100644 --- a/format/annotation.go +++ b/format/annotation.go @@ -7,10 +7,10 @@ import ( "github.com/joyme123/thrift-ls/parser" ) -func MustFormatAnnotations(annotations *parser.Annotations) string { +func MustFormatAnnotations(annotations *parser.Annotations, opts Options) string { buf := bytes.NewBuffer(nil) - buf.WriteString(MustFormatKeyword(annotations.LParKeyword.Keyword)) + buf.WriteString(MustFormatKeyword(opts, annotations.LParKeyword.Keyword)) var preNode parser.Node preNode = annotations.LParKeyword @@ -22,9 +22,9 @@ func MustFormatAnnotations(annotations *parser.Annotations) string { if lineDistance(preNode, annotations.Annotations[i]) >= 1 { buf.WriteString("\n") isNewLine = true - indent = Indent + Indent + indent = opts.GetIndent() + opts.GetIndent() } - buf.WriteString(MustFormatAnnotation(anno, i == len(annotations.Annotations)-1, i == 0, indent, isNewLine)) + buf.WriteString(MustFormatAnnotation(anno, opts, i == len(annotations.Annotations)-1, i == 0, indent, isNewLine)) preNode = annotations.Annotations[i] isNewLine = false indent = "" @@ -32,17 +32,17 @@ func MustFormatAnnotations(annotations *parser.Annotations) string { if lineDistance(preNode, annotations.RParKeyword) >= 1 { buf.WriteString("\n") - buf.WriteString(Indent) + buf.WriteString(opts.GetIndent()) } - buf.WriteString(MustFormatKeyword(annotations.RParKeyword.Keyword)) + buf.WriteString(MustFormatKeyword(opts, annotations.RParKeyword.Keyword)) return buf.String() } -func MustFormatAnnotation(anno *parser.Annotation, isLast bool, isFirst bool, indent string, isNewLine bool) string { +func MustFormatAnnotation(anno *parser.Annotation, opts Options, isLast bool, isFirst bool, indent string, isNewLine bool) string { sep := "" if (!isLast) && anno.ListSeparatorKeyword != nil { - sep = MustFormatKeyword(anno.ListSeparatorKeyword.Keyword) + sep = MustFormatKeyword(opts, anno.ListSeparatorKeyword.Keyword) } space := "" @@ -51,5 +51,5 @@ func MustFormatAnnotation(anno *parser.Annotation, isLast bool, isFirst bool, in } // a = "xxxx", - return fmt.Sprintf("%s%s %s %s%s", space, MustFormatIdentifier(anno.Identifier, indent), MustFormatKeyword(anno.EqualKeyword.Keyword), MustFormatLiteral(anno.Value, ""), sep) + return fmt.Sprintf("%s%s %s %s%s", space, MustFormatIdentifier(opts, anno.Identifier, indent), MustFormatKeyword(opts, anno.EqualKeyword.Keyword), MustFormatLiteral(opts, anno.Value, ""), sep) } diff --git a/format/annotation_test.go b/format/annotation_test.go index e5c3891..08394d8 100644 --- a/format/annotation_test.go +++ b/format/annotation_test.go @@ -32,7 +32,8 @@ func TestMustFormatAnnotations(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - assert.Equal(t, tt.want, MustFormatAnnotations(tt.args.annotations)) + opts := Options{} + assert.Equal(t, tt.want, MustFormatAnnotations(tt.args.annotations, opts)) }) } } @@ -73,7 +74,8 @@ struct Foo { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - assert.Equal(t, tt.want, MustFormatAnnotations(tt.args.annotations)) + opts := Options{} + assert.Equal(t, tt.want, MustFormatAnnotations(tt.args.annotations, opts)) }) } } diff --git a/format/comment.go b/format/comment.go index 383e6d8..fab669e 100644 --- a/format/comment.go +++ b/format/comment.go @@ -10,7 +10,7 @@ import ( // TODO(jpf): 多行注释,换行上还需要优化 // MustFormatComments formats comments // return string doesn't include '\n' end of line -func MustFormatComments(comments []*parser.Comment, indent string) string { +func MustFormatComments(opts Options, comments []*parser.Comment, indent string, innerIndent string) string { fmtCtx := &fmtContext{} buf := bytes.NewBuffer(nil) for _, c := range comments { @@ -29,7 +29,7 @@ func MustFormatComments(comments []*parser.Comment, indent string) string { return buf.String() } -func MustFormatEndLineComments(comments []*parser.Comment, indent string) string { +func MustFormatEndLineComments(opts Options, comments []*parser.Comment, indent string, innerIndent string) string { if len(comments) == 0 { return "" } diff --git a/format/comment_test.go b/format/comment_test.go index 17dde02..8adc5af 100644 --- a/format/comment_test.go +++ b/format/comment_test.go @@ -47,7 +47,7 @@ include "a.thrift" // aaaaa name: "comments", args: args{ comments: ast.(*parser.Document).Includes[0].Comments, - indent: Indent, + indent: " ", }, want: strings.TrimSpace(` /* @@ -71,7 +71,8 @@ include "a.thrift" // aaaaa } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - assert.Equal(t, tt.want, MustFormatComments(tt.args.comments, "")) + // For comments test, pass empty indent to match expected output format + assert.Equal(t, tt.want, MustFormatComments(Options{}, tt.args.comments, "", "")) }) } } diff --git a/format/const.go b/format/const.go index 54a2fa7..a5834b6 100644 --- a/format/const.go +++ b/format/const.go @@ -19,27 +19,27 @@ type ConstFormatter struct { EndLineComments string } -func MustFormatConst(cst *parser.Const) string { - comments, annos := formatCommentsAndAnnos(cst.Comments, cst.Annotations, "") +func MustFormatConst(cst *parser.Const, opts Options) string { + comments, annos := formatCommentsAndAnnos(opts, cst.Comments, cst.Annotations, "") if len(cst.Comments) > 0 && lineDistance(cst.Comments[len(cst.Comments)-1], cst.ConstKeyword) > 1 { comments = comments + "\n" } sep := "" if cst.ListSeparatorKeyword != nil { - sep = MustFormatKeyword(cst.ListSeparatorKeyword.Keyword) + sep = MustFormatKeyword(opts, cst.ListSeparatorKeyword.Keyword) } f := &ConstFormatter{ Comments: comments, - Const: MustFormatKeyword(cst.ConstKeyword.Keyword), - Type: MustFormatFieldType(cst.ConstType), - Name: MustFormatIdentifier(cst.Name, ""), + Const: MustFormatKeyword(opts, cst.ConstKeyword.Keyword), + Type: MustFormatFieldType(cst.ConstType, opts), + Name: MustFormatIdentifier(opts, cst.Name, ""), Annotations: annos, - Equal: MustFormatKeyword(cst.EqualKeyword.Keyword), - Value: MustFormatConstValue(cst.Value, "", false), + Equal: MustFormatKeyword(opts, cst.EqualKeyword.Keyword), + Value: MustFormatConstValue(cst.Value, opts, "", false), ListSeparator: sep, - EndLineComments: MustFormatEndLineComments(cst.EndLineComments, ""), + EndLineComments: MustFormatEndLineComments(opts, cst.EndLineComments, "", ""), } return MustFormat(constOneLineTpl, f) diff --git a/format/constvalue.go b/format/constvalue.go index 5f1cb67..3b30f68 100644 --- a/format/constvalue.go +++ b/format/constvalue.go @@ -7,14 +7,14 @@ import ( "github.com/joyme123/thrift-ls/parser" ) -func MustFormatConstValue(cv *parser.ConstValue, indent string, newLine bool) string { +func MustFormatConstValue(cv *parser.ConstValue, opts Options, indent string, newLine bool) string { buf := bytes.NewBuffer(nil) if len(cv.Comments) > 0 { - buf.WriteString(MustFormatComments(cv.Comments, indent)) + buf.WriteString(MustFormatComments(opts, cv.Comments, indent, "")) } sep := "" if cv.ListSeparatorKeyword != nil { - sep = MustFormatKeyword(cv.ListSeparatorKeyword.Keyword) + " " + sep = MustFormatKeyword(opts, cv.ListSeparatorKeyword.Keyword) + " " } switch cv.TypeName { @@ -27,13 +27,13 @@ func MustFormatConstValue(cv *parser.ConstValue, indent string, newLine bool) st } } - buf.WriteString(MustFormatKeyword(cv.LBrkKeyword.Keyword)) + buf.WriteString(MustFormatKeyword(opts, cv.LBrkKeyword.Keyword)) for i := range values { // TODO(jpf): 优化显示 newLine = false - buf.WriteString(MustFormatConstValue(values[i], indent, newLine)) + buf.WriteString(MustFormatConstValue(values[i], opts, indent, newLine)) } - buf.WriteString(MustFormatKeyword(cv.RBrkKeyword.Keyword)) + buf.WriteString(MustFormatKeyword(opts, cv.RBrkKeyword.Keyword)) case "map": values := cv.Value.([]*parser.ConstValue) @@ -44,7 +44,7 @@ func MustFormatConstValue(cv *parser.ConstValue, indent string, newLine bool) st } var preNode parser.Node - buf.WriteString(MustFormatKeyword(cv.LCurKeyword.Keyword)) + buf.WriteString(MustFormatKeyword(opts, cv.LCurKeyword.Keyword)) preNode = cv.LCurKeyword for i := range values { distance := lineDistance(preNode, values[i]) @@ -57,14 +57,14 @@ func MustFormatConstValue(cv *parser.ConstValue, indent string, newLine bool) st } newLine = false } - buf.WriteString(MustFormatConstValue(values[i], indent, newLine)) + buf.WriteString(MustFormatConstValue(values[i], opts, indent, newLine)) preNode = values[i] } if lineDistance(preNode, cv.RCurKeyword) >= 1 { buf.WriteString("\n") buf.WriteString(indent) } - buf.WriteString(MustFormatKeyword(cv.RCurKeyword.Keyword)) + buf.WriteString(MustFormatKeyword(opts, cv.RCurKeyword.Keyword)) case "pair": key := cv.Key.(*parser.ConstValue) value := cv.Value.(*parser.ConstValue) @@ -76,12 +76,12 @@ func MustFormatConstValue(cv *parser.ConstValue, indent string, newLine bool) st } if cv.ListSeparatorKeyword != nil { - sep = MustFormatKeyword(cv.ListSeparatorKeyword.Keyword) + sep = MustFormatKeyword(opts, cv.ListSeparatorKeyword.Keyword) } buf.WriteString(fmt.Sprintf("%s%s %s%s", - MustFormatConstValue(key, indent+Indent, newLine), - MustFormatKeyword(cv.ColonKeyword.Keyword), - MustFormatConstValue(value, indent, false), + MustFormatConstValue(key, opts, indent+opts.GetIndent(), newLine), + MustFormatKeyword(opts, cv.ColonKeyword.Keyword), + MustFormatConstValue(value, opts, indent, false), sep)) case "identifier": if len(cv.Comments) > 0 { @@ -125,7 +125,7 @@ func MustFormatConstValue(cv *parser.ConstValue, indent string, newLine bool) st indent = "" } - val = MustFormatLiteral(literal, indent) + val = MustFormatLiteral(opts, literal, indent) buf.WriteString(fmt.Sprintf("%s%s", val, sep)) } case "i64": diff --git a/format/constvalue_test.go b/format/constvalue_test.go index 67b9d3e..48e45a9 100644 --- a/format/constvalue_test.go +++ b/format/constvalue_test.go @@ -191,7 +191,8 @@ func TestMustFormatConstValue(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - assert.Equal(t, tt.want, MustFormatConstValue(tt.args.cv, "", false)) + opts := Options{} + assert.Equal(t, tt.want, MustFormatConstValue(tt.args.cv, opts, "", false)) }) } } diff --git a/format/document.go b/format/document.go index b656b72..d9d787c 100644 --- a/format/document.go +++ b/format/document.go @@ -16,11 +16,11 @@ type fmtContext struct { preNode parser.Node } -func FormatDocument(doc *parser.Document) (string, error) { - return FormatDocumentWithValidation(doc, false) +func FormatDocument(doc *parser.Document, opts Options) (string, error) { + return FormatDocumentWithValidation(doc, opts, false) } -func FormatDocumentWithValidation(doc *parser.Document, selfValidation bool) (string, error) { +func FormatDocumentWithValidation(doc *parser.Document, opts Options, selfValidation bool) (string, error) { if doc.ChildrenBadNode() { return "", BadNodeError } @@ -40,25 +40,25 @@ func FormatDocumentWithValidation(doc *parser.Document, selfValidation bool) (st switch node.Type() { case "Include": - buf.WriteString(MustFormatInclude(node.(*parser.Include))) + buf.WriteString(MustFormatInclude(node.(*parser.Include), opts)) case "CPPInclude": - buf.WriteString(MustFormatCPPInclude(node.(*parser.CPPInclude))) + buf.WriteString(MustFormatCPPInclude(node.(*parser.CPPInclude), opts)) case "Namespace": - buf.WriteString(MustFormatNamespace(node.(*parser.Namespace))) + buf.WriteString(MustFormatNamespace(node.(*parser.Namespace), opts)) case "Struct": - buf.WriteString(MustFormatStruct(node.(*parser.Struct))) + buf.WriteString(MustFormatStruct(node.(*parser.Struct), opts)) case "Union": - buf.WriteString(MustFormatUnion(node.(*parser.Union))) + buf.WriteString(MustFormatUnion(node.(*parser.Union), opts)) case "Exception": - buf.WriteString(MustFormatException(node.(*parser.Exception))) + buf.WriteString(MustFormatException(node.(*parser.Exception), opts)) case "Service": - buf.WriteString(MustFormatService(node.(*parser.Service))) + buf.WriteString(MustFormatService(node.(*parser.Service), opts)) case "Typedef": - buf.WriteString(MustFormatTypedef(node.(*parser.Typedef))) + buf.WriteString(MustFormatTypedef(node.(*parser.Typedef), opts)) case "Const": - buf.WriteString(MustFormatConst(node.(*parser.Const))) + buf.WriteString(MustFormatConst(node.(*parser.Const), opts)) case "Enum": - buf.WriteString(MustFormatEnum(node.(*parser.Enum))) + buf.WriteString(MustFormatEnum(node.(*parser.Enum), opts)) } } @@ -70,7 +70,7 @@ func FormatDocumentWithValidation(doc *parser.Document, selfValidation bool) (st } if len(doc.Comments) > 0 { - buf.WriteString(MustFormatComments(doc.Comments, "")) + buf.WriteString(MustFormatComments(opts, doc.Comments, "", "")) } res := buf.String() @@ -94,7 +94,7 @@ func FormatDocumentWithValidation(doc *parser.Document, selfValidation bool) (st // FormatDocumentWithValidationFull formats a document with self-validation using include resolution. // When includePaths is provided and non-empty, self-validation uses ParseRecursively to resolve includes. // When includePaths is empty, falls back to plain Parse for backward compatibility. -func FormatDocumentWithValidationFull(doc *parser.Document, selfValidation bool, includePaths []string, currentFile string) (string, error) { +func FormatDocumentWithValidationFull(doc *parser.Document, opts Options, selfValidation bool, includePaths []string, currentFile string) (string, error) { if doc.ChildrenBadNode() { return "", BadNodeError } @@ -114,25 +114,25 @@ func FormatDocumentWithValidationFull(doc *parser.Document, selfValidation bool, switch node.Type() { case "Include": - buf.WriteString(MustFormatInclude(node.(*parser.Include))) + buf.WriteString(MustFormatInclude(node.(*parser.Include), opts)) case "CPPInclude": - buf.WriteString(MustFormatCPPInclude(node.(*parser.CPPInclude))) + buf.WriteString(MustFormatCPPInclude(node.(*parser.CPPInclude), opts)) case "Namespace": - buf.WriteString(MustFormatNamespace(node.(*parser.Namespace))) + buf.WriteString(MustFormatNamespace(node.(*parser.Namespace), opts)) case "Struct": - buf.WriteString(MustFormatStruct(node.(*parser.Struct))) + buf.WriteString(MustFormatStruct(node.(*parser.Struct), opts)) case "Union": - buf.WriteString(MustFormatUnion(node.(*parser.Union))) + buf.WriteString(MustFormatUnion(node.(*parser.Union), opts)) case "Exception": - buf.WriteString(MustFormatException(node.(*parser.Exception))) + buf.WriteString(MustFormatException(node.(*parser.Exception), opts)) case "Service": - buf.WriteString(MustFormatService(node.(*parser.Service))) + buf.WriteString(MustFormatService(node.(*parser.Service), opts)) case "Typedef": - buf.WriteString(MustFormatTypedef(node.(*parser.Typedef))) + buf.WriteString(MustFormatTypedef(node.(*parser.Typedef), opts)) case "Const": - buf.WriteString(MustFormatConst(node.(*parser.Const))) + buf.WriteString(MustFormatConst(node.(*parser.Const), opts)) case "Enum": - buf.WriteString(MustFormatEnum(node.(*parser.Enum))) + buf.WriteString(MustFormatEnum(node.(*parser.Enum), opts)) } } @@ -144,7 +144,7 @@ func FormatDocumentWithValidationFull(doc *parser.Document, selfValidation bool, } if len(doc.Comments) > 0 { - buf.WriteString(MustFormatComments(doc.Comments, "")) + buf.WriteString(MustFormatComments(opts, doc.Comments, "", "")) } res := buf.String() @@ -176,7 +176,7 @@ func FormatDocumentWithValidationFull(doc *parser.Document, selfValidation bool, } } - if TrailingNewline && !strings.HasSuffix(res, "\n") { + if opts.TrailingNewline && !strings.HasSuffix(res, "\n") { res += "\n" } diff --git a/format/document_include_test.go b/format/document_include_test.go index c191acc..da60f3d 100644 --- a/format/document_include_test.go +++ b/format/document_include_test.go @@ -45,7 +45,8 @@ struct Person { } // Format with empty include paths (backward compatibility) - formatted, err := FormatDocumentWithValidationFull(doc, true, []string{}, mainFile) + opts := Options{} + formatted, err := FormatDocumentWithValidationFull(doc, opts, true, []string{}, mainFile) if err != nil { t.Fatalf("formatting failed: %v", err) } @@ -63,7 +64,7 @@ struct Person { } // Format with include paths (should work the same) - formatted2, err := FormatDocumentWithValidationFull(doc, true, []string{tmpDir}, mainFile) + formatted2, err := FormatDocumentWithValidationFull(doc, opts, true, []string{tmpDir}, mainFile) if err != nil { t.Fatalf("formatting with include paths failed: %v", err) } @@ -118,7 +119,8 @@ struct Entity { } // Format with include path - formatted, err := FormatDocumentWithValidationFull(doc, true, []string{includeDir}, mainFile) + opts := Options{} + formatted, err := FormatDocumentWithValidationFull(doc, opts, true, []string{includeDir}, mainFile) if err != nil { t.Fatalf("formatting failed: %v", err) } @@ -191,7 +193,8 @@ struct Container { } // Format with include path (nested includes should be resolved) - formatted, err := FormatDocumentWithValidationFull(doc, true, []string{includeDir}, mainFile) + opts := Options{} + formatted, err := FormatDocumentWithValidationFull(doc, opts, true, []string{includeDir}, mainFile) if err != nil { t.Fatalf("formatting failed: %v", err) } @@ -226,7 +229,8 @@ struct Data { } // Format without include paths (backward compatibility, no self-val) - formatted, err := FormatDocumentWithValidationFull(doc, false, []string{}, mainFile) + opts := Options{} + formatted, err := FormatDocumentWithValidationFull(doc, opts, false, []string{}, mainFile) if err != nil { t.Fatalf("formatting failed: %v", err) } @@ -266,7 +270,8 @@ struct Point { } // Format without self-validation and no include paths (backward compat path) - formatted, err := FormatDocumentWithValidationFull(doc, false, []string{}, mainFile) + opts := Options{} + formatted, err := FormatDocumentWithValidationFull(doc, opts, false, []string{}, mainFile) if err != nil { t.Fatalf("formatting failed: %v", err) } @@ -277,7 +282,7 @@ struct Point { } // Test with self-validation using fallback Parse path - formatted2, err := FormatDocumentWithValidationFull(doc, true, []string{}, "") + formatted2, err := FormatDocumentWithValidationFull(doc, opts, true, []string{}, "") if err != nil { t.Fatalf("formatting with empty currentFile failed: %v", err) } @@ -344,7 +349,8 @@ struct Container { } // Format with both include paths - formatted, err := FormatDocumentWithValidationFull(doc, true, []string{includeDir1, includeDir2}, mainFile) + opts := Options{} + formatted, err := FormatDocumentWithValidationFull(doc, opts, true, []string{includeDir1, includeDir2}, mainFile) if err != nil { t.Fatalf("formatting failed: %v", err) } diff --git a/format/document_test.go b/format/document_test.go index 8b31176..c133446 100644 --- a/format/document_test.go +++ b/format/document_test.go @@ -22,16 +22,16 @@ service A { bool func2(), }` - FieldLineComma = FieldLineCommaAdd + opts := Options{FieldLineComma: FieldLineCommaAdd} ast, err := parser.Parse("test.thrift", []byte(doc)) assert.NoError(t, err) assert.NotNil(t, ast) - formated, err := FormatDocument(ast.(*parser.Document)) + formated, err := FormatDocument(ast.(*parser.Document), opts) assert.Equal(t, expectedDoc, formated) - _, err = FormatDocumentWithValidation(ast.(*parser.Document), true) + _, err = FormatDocumentWithValidation(ast.(*parser.Document), opts, true) assert.NoError(t, err) // remove comma @@ -40,16 +40,16 @@ service A { bool func2() }` - FieldLineComma = FieldLineCommaRemove + opts = Options{FieldLineComma: FieldLineCommaRemove} ast, err = parser.Parse("test.thrift", []byte(doc)) assert.NoError(t, err) assert.NotNil(t, ast) - formated, err = FormatDocument(ast.(*parser.Document)) + formated, err = FormatDocument(ast.(*parser.Document), opts) assert.Equal(t, expectedDoc, formated) - _, err = FormatDocumentWithValidation(ast.(*parser.Document), true) + _, err = FormatDocumentWithValidation(ast.(*parser.Document), opts, true) assert.NoError(t, err) // disable @@ -58,16 +58,16 @@ service A { bool func2(); }` - FieldLineComma = FieldLineCommaDisable + opts = Options{FieldLineComma: FieldLineCommaDisable} ast, err = parser.Parse("test.thrift", []byte(doc)) assert.NoError(t, err) assert.NotNil(t, ast) - formated, err = FormatDocument(ast.(*parser.Document)) + formated, err = FormatDocument(ast.(*parser.Document), opts) assert.Equal(t, expectedDoc, formated) - _, err = FormatDocumentWithValidation(ast.(*parser.Document), true) + _, err = FormatDocumentWithValidation(ast.(*parser.Document), opts, true) assert.NoError(t, err) } @@ -76,10 +76,11 @@ func Test_FormatDocument(t *testing.T) { assert.NoError(t, err) assert.NotNil(t, ast) - formated, err := FormatDocument(ast.(*parser.Document)) + opts := Options{} + formated, err := FormatDocument(ast.(*parser.Document), opts) assert.Equal(t, expectedFormated, formated) - _, err = FormatDocumentWithValidation(ast.(*parser.Document), true) + _, err = FormatDocumentWithValidation(ast.(*parser.Document), opts, true) assert.NoError(t, err) } diff --git a/format/enum.go b/format/enum.go index da247ca..96a4ac8 100644 --- a/format/enum.go +++ b/format/enum.go @@ -23,21 +23,21 @@ type EnumFormatter struct { EndLineComments string } -func MustFormatEnum(enum *parser.Enum) string { - comments, annos := formatCommentsAndAnnos(enum.Comments, enum.Annotations, "") +func MustFormatEnum(enum *parser.Enum, opts Options) string { + comments, annos := formatCommentsAndAnnos(opts, enum.Comments, enum.Annotations, "") if len(enum.Comments) > 0 && lineDistance(enum.Comments[len(enum.Comments)-1], enum.EnumKeyword) > 1 { comments = comments + "\n" } f := EnumFormatter{ Comments: comments, - Enum: MustFormatKeyword(enum.EnumKeyword.Keyword), - Identifier: MustFormatIdentifier(enum.Name, ""), - LCUR: MustFormatKeyword(enum.LCurKeyword.Keyword), - EnumValues: MustFormatEnumValues(enum.Values, Indent), - RCUR: MustFormatKeyword(enum.RCurKeyword.Keyword), + Enum: MustFormatKeyword(opts, enum.EnumKeyword.Keyword), + Identifier: MustFormatIdentifier(opts, enum.Name, ""), + LCUR: MustFormatKeyword(opts, enum.LCurKeyword.Keyword), + EnumValues: MustFormatEnumValues(enum.Values, opts, opts.GetIndent()), + RCUR: MustFormatKeyword(opts, enum.RCurKeyword.Keyword), Annotations: annos, - EndLineComments: MustFormatEndLineComments(enum.EndLineComments, ""), + EndLineComments: MustFormatEndLineComments(opts, enum.EndLineComments, "", ""), } if len(enum.Values) > 0 { diff --git a/format/enumvalue.go b/format/enumvalue.go index a0542ec..6ba3e5a 100644 --- a/format/enumvalue.go +++ b/format/enumvalue.go @@ -10,7 +10,7 @@ import ( type enumValueGroup []string -func MustFormatEnumValues(values []*parser.EnumValue, indent string) string { +func MustFormatEnumValues(values []*parser.EnumValue, opts Options, indent string) string { buf := bytes.NewBuffer(nil) fmtCtx := &fmtContext{} @@ -24,10 +24,10 @@ func MustFormatEnumValues(values []*parser.EnumValue, indent string) string { eg = make(enumValueGroup, 0) } space := " " - if Align == AlignTypeField { + if opts.getAlign() == AlignTypeField { space = "\t" } - eg = append(eg, MustFormatEnumValue(v, space, indent)) + eg = append(eg, MustFormatEnumValue(v, opts, space, indent)) fmtCtx.preNode = values[i] } @@ -51,34 +51,34 @@ func MustFormatEnumValues(values []*parser.EnumValue, indent string) string { return buf.String() } -func MustFormatEnumValue(enumValue *parser.EnumValue, space, indent string) string { - comments, annos := formatCommentsAndAnnos(enumValue.Comments, enumValue.Annotations, indent) +func MustFormatEnumValue(enumValue *parser.EnumValue, opts Options, space, indent string) string { + comments, annos := formatCommentsAndAnnos(opts, enumValue.Comments, enumValue.Annotations, indent) if len(comments) > 0 && lineDistance(enumValue.Comments[len(enumValue.Comments)-1], enumValue.Name) > 1 { comments = comments + "\n" } buf := bytes.NewBufferString(comments) - buf.WriteString(indent + MustFormatIdentifier(enumValue.Name, "")) + buf.WriteString(indent + MustFormatIdentifier(opts, enumValue.Name, "")) if enumValue.ValueNode != nil { equalSpace := space - if Align == AlignTypeAssign { + if opts.getAlign() == AlignTypeAssign { equalSpace = "\t" } - buf.WriteString(fmt.Sprintf("%s%s%s%s", equalSpace, MustFormatKeyword(enumValue.EqualKeyword.Keyword), equalSpace, MustFormatConstValue(enumValue.ValueNode, indent, false))) + buf.WriteString(fmt.Sprintf("%s%s%s%s", equalSpace, MustFormatKeyword(opts, enumValue.EqualKeyword.Keyword), equalSpace, MustFormatConstValue(enumValue.ValueNode, opts, indent, false))) } buf.WriteString(annos) - if FieldLineComma == FieldLineCommaAdd { + if opts.getFieldLineComma() == FieldLineCommaAdd { buf.WriteString(",") - } else if FieldLineComma == FieldLineCommaDisable { + } else if opts.getFieldLineComma() == FieldLineCommaDisable { if enumValue.ListSeparatorKeyword != nil { - buf.WriteString(MustFormatKeyword(enumValue.ListSeparatorKeyword.Keyword)) + buf.WriteString(MustFormatKeyword(opts, enumValue.ListSeparatorKeyword.Keyword)) } } - buf.WriteString(MustFormatEndLineComments(enumValue.EndLineComments, "")) + buf.WriteString(MustFormatEndLineComments(opts, enumValue.EndLineComments, "", "")) return buf.String() } diff --git a/format/exception.go b/format/exception.go index cd37937..fd47529 100644 --- a/format/exception.go +++ b/format/exception.go @@ -23,20 +23,20 @@ type ExceptionFormatter struct { EndLineComments string } -func MustFormatException(excep *parser.Exception) string { - comments, annos := formatCommentsAndAnnos(excep.Comments, excep.Annotations, "") +func MustFormatException(excep *parser.Exception, opts Options) string { + comments, annos := formatCommentsAndAnnos(opts, excep.Comments, excep.Annotations, "") if len(excep.Comments) > 0 && lineDistance(excep.Comments[len(excep.Comments)-1], excep.ExceptionKeyword) > 1 { comments = comments + "\n" } f := ExceptionFormatter{ Comments: comments, - Exception: MustFormatKeyword(excep.ExceptionKeyword.Keyword), - Identifier: MustFormatIdentifier(excep.Name, ""), - LCUR: MustFormatKeyword(excep.LCurKeyword.Keyword), - Fields: MustFormatFields(excep.Fields, Indent), - RCUR: MustFormatKeyword(excep.RCurKeyword.Keyword), + Exception: MustFormatKeyword(opts, excep.ExceptionKeyword.Keyword), + Identifier: MustFormatIdentifier(opts, excep.Name, ""), + LCUR: MustFormatKeyword(opts, excep.LCurKeyword.Keyword), + Fields: MustFormatFields(excep.Fields, opts, opts.GetIndent()), + RCUR: MustFormatKeyword(opts, excep.RCurKeyword.Keyword), Annotations: annos, - EndLineComments: MustFormatEndLineComments(excep.EndLineComments, ""), + EndLineComments: MustFormatEndLineComments(opts, excep.EndLineComments, "", ""), } if len(excep.Fields) > 0 { diff --git a/format/field.go b/format/field.go index 80791e6..8497840 100644 --- a/format/field.go +++ b/format/field.go @@ -11,7 +11,7 @@ import ( type fieldGroup []string -func MustFormatFields(fields []*parser.Field, indent string) string { +func MustFormatFields(fields []*parser.Field, opts Options, indent string) string { buf := bytes.NewBuffer(nil) fmtCtx := &fmtContext{} @@ -24,10 +24,10 @@ func MustFormatFields(fields []*parser.Field, indent string) string { fg = make(fieldGroup, 0) } space := " " - if Align == AlignTypeField { + if opts.getAlign() == AlignTypeField { space = "\t" } - fg = append(fg, MustFormatField(field, space, indent, false)) + fg = append(fg, MustFormatField(field, opts, space, indent, false)) fmtCtx.preNode = field } @@ -51,10 +51,10 @@ func MustFormatFields(fields []*parser.Field, indent string) string { return buf.String() } -func MustFormatOneLineFields(fields []*parser.Field) string { +func MustFormatOneLineFields(fields []*parser.Field, opts Options) string { buf := bytes.NewBuffer(nil) for i, field := range fields { - buf.WriteString(MustFormatField(field, " ", "", true)) + buf.WriteString(MustFormatField(field, opts, " ", "", true)) if i < len(fields)-1 { buf.WriteString(" ") } @@ -63,8 +63,8 @@ func MustFormatOneLineFields(fields []*parser.Field) string { return buf.String() } -func MustFormatField(field *parser.Field, space string, indent string, oneline bool) string { - comments, annos := formatCommentsAndAnnos(field.Comments, field.Annotations, indent) +func MustFormatField(field *parser.Field, opts Options, space string, indent string, oneline bool) string { + comments, annos := formatCommentsAndAnnos(opts, field.Comments, field.Annotations, indent) if len(field.Comments) > 0 && lineDistance(field.Comments[len(field.Comments)-1], field.Index) > 1 { comments = comments + "\n" } @@ -72,63 +72,63 @@ func MustFormatField(field *parser.Field, space string, indent string, oneline b buf := bytes.NewBuffer([]byte(comments)) required := "" if field.RequiredKeyword != nil { - required = MustFormatKeyword(field.RequiredKeyword.Keyword) + space + required = MustFormatKeyword(opts, field.RequiredKeyword.Keyword) + space } value := "" if field.ConstValue != nil { equalSpace := space - if Align == AlignTypeAssign { + if opts.getAlign() == AlignTypeAssign { equalSpace = "\t" } - value = fmt.Sprintf("%s%s%s%s", equalSpace, MustFormatKeyword(field.EqualKeyword.Keyword), equalSpace, MustFormatConstValue(field.ConstValue, indent, false)) + value = fmt.Sprintf("%s%s%s%s", equalSpace, MustFormatKeyword(opts, field.EqualKeyword.Keyword), equalSpace, MustFormatConstValue(field.ConstValue, opts, indent, false)) } - str := fmt.Sprintf("%s%d:%s%s%s%s%s%s", indent, field.Index.Value, space, required, MustFormatFieldType(field.FieldType), space, field.Identifier.Name.Text, value) + str := fmt.Sprintf("%s%d:%s%s%s%s%s%s", indent, field.Index.Value, space, required, MustFormatFieldType(field.FieldType, opts), space, field.Identifier.Name.Text, value) buf.WriteString(str) buf.WriteString(annos) - if FieldLineComma == FieldLineCommaAdd && !oneline { + if opts.getFieldLineComma() == FieldLineCommaAdd && !oneline { buf.WriteString(",") - } else if FieldLineComma == FieldLineCommaDisable || oneline { - buf.WriteString(formatListSeparator(field.ListSeparatorKeyword)) + } else if opts.getFieldLineComma() == FieldLineCommaDisable || oneline { + buf.WriteString(formatListSeparator(opts, field.ListSeparatorKeyword)) } if len(field.EndLineComments) > 0 { - buf.WriteString(MustFormatEndLineComments(field.EndLineComments, "")) + buf.WriteString(MustFormatEndLineComments(opts, field.EndLineComments, "", "")) } // remove space at end of line return strings.TrimRight(buf.String(), " ") } -func MustFormatFieldType(ft *parser.FieldType) string { +func MustFormatFieldType(ft *parser.FieldType, opts Options) string { if ft == nil { return "" } annos := "" if ft.Annotations != nil { - annos = MustFormatAnnotations(ft.Annotations) + annos = MustFormatAnnotations(ft.Annotations, opts) if len(ft.Annotations.Annotations) > 0 { annos = " " + annos } } - tn := MustFormatTypeName(ft.TypeName) + tn := MustFormatTypeName(ft.TypeName, opts) switch ft.TypeName.Name { case "map": - return fmt.Sprintf("%s<%s,%s>%s", tn, MustFormatFieldType(ft.KeyType), MustFormatFieldType(ft.ValueType), annos) + return fmt.Sprintf("%s<%s,%s>%s", tn, MustFormatFieldType(ft.KeyType, opts), MustFormatFieldType(ft.ValueType, opts), annos) case "set": - return fmt.Sprintf("%s<%s>%s", tn, MustFormatFieldType(ft.KeyType), annos) + return fmt.Sprintf("%s<%s>%s", tn, MustFormatFieldType(ft.KeyType, opts), annos) case "list": - return fmt.Sprintf("%s<%s>%s", tn, MustFormatFieldType(ft.KeyType), annos) + return fmt.Sprintf("%s<%s>%s", tn, MustFormatFieldType(ft.KeyType, opts), annos) default: return tn + annos } } -func MustFormatTypeName(tn *parser.TypeName) string { - comments := MustFormatComments(tn.Comments, "") +func MustFormatTypeName(tn *parser.TypeName, opts Options) string { + comments := MustFormatComments(opts, tn.Comments, "", "") if len(tn.Comments) > 0 { comments = comments + " " } diff --git a/format/field_test.go b/format/field_test.go index a5b1ca5..669adb0 100644 --- a/format/field_test.go +++ b/format/field_test.go @@ -187,7 +187,8 @@ func TestMustFormatFieldType(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - assert.Equal(t, tt.want, MustFormatFieldType(tt.args.ft)) + opts := Options{} + assert.Equal(t, tt.want, MustFormatFieldType(tt.args.ft, opts)) }) } } diff --git a/format/function.go b/format/function.go index fcd0ad2..85ec3ca 100644 --- a/format/function.go +++ b/format/function.go @@ -6,14 +6,14 @@ import ( "github.com/joyme123/thrift-ls/parser" ) -func MustFormatFunctions(fns []*parser.Function, indent string) string { +func MustFormatFunctions(fns []*parser.Function, opts Options, indent string) string { buf := bytes.NewBuffer(nil) fmtCtx := &fmtContext{} for i := range fns { if needAddtionalLineForFuncs(fmtCtx.preNode, fns[i]) { buf.WriteString("\n") } - buf.WriteString(MustFormatFunction(fns[i], indent)) + buf.WriteString(MustFormatFunction(fns[i], opts, indent)) if i < len(fns)-1 { buf.WriteString("\n") } @@ -38,8 +38,8 @@ type FunctionFormatter struct { EndLineComments string } -func MustFormatFunction(fn *parser.Function, indent string) string { - comments, annos := formatCommentsAndAnnos(fn.Comments, fn.Annotations, indent) +func MustFormatFunction(fn *parser.Function, opts Options, indent string) string { + comments, annos := formatCommentsAndAnnos(opts, fn.Comments, fn.Annotations, indent) var firstNode parser.Node if fn.Void != nil { firstNode = fn.Void @@ -56,27 +56,27 @@ func MustFormatFunction(fn *parser.Function, indent string) string { } args := "" if len(fn.Arguments) > 0 { - args = MustFormatOneLineFields(fn.Arguments) + args = MustFormatOneLineFields(fn.Arguments, opts) } ft := "" if fn.Void != nil { - ft = MustFormatKeyword(fn.Void.Keyword) + ft = MustFormatKeyword(opts, fn.Void.Keyword) } else { - ft = MustFormatFieldType(fn.FunctionType) + ft = MustFormatFieldType(fn.FunctionType, opts) } sep := "" - if FieldLineComma == FieldLineCommaAdd { // add comma always + if opts.getFieldLineComma() == FieldLineCommaAdd { // add comma always sep = "," - } else if FieldLineComma == FieldLineCommaDisable { // add list separator + } else if opts.getFieldLineComma() == FieldLineCommaDisable { // add list separator if fn.ListSeparatorKeyword != nil { - sep = MustFormatKeyword(fn.ListSeparatorKeyword.Keyword) + sep = MustFormatKeyword(opts, fn.ListSeparatorKeyword.Keyword) } } // otherwise, sep will be removed - throws := MustFormatThrows(fn.Throws) + throws := MustFormatThrows(fn.Throws, opts) if fn.Throws != nil { throws = " " + throws } @@ -84,14 +84,14 @@ func MustFormatFunction(fn *parser.Function, indent string) string { f := &FunctionFormatter{ Oneway: oneway, FunctionType: ft, - Identifier: MustFormatIdentifier(fn.Name, ""), - LPAR: MustFormatKeyword(fn.LParKeyword.Keyword), + Identifier: MustFormatIdentifier(opts, fn.Name, ""), + LPAR: MustFormatKeyword(opts, fn.LParKeyword.Keyword), Args: args, - RPAR: MustFormatKeyword(fn.RParKeyword.Keyword), + RPAR: MustFormatKeyword(opts, fn.RParKeyword.Keyword), Throws: throws, Annotations: annos, ListSeparator: sep, - EndLineComments: MustFormatEndLineComments(fn.EndLineComments, ""), + EndLineComments: MustFormatEndLineComments(opts, fn.EndLineComments, "", ""), } fnStr := MustFormat(functionTpl, f) @@ -109,21 +109,22 @@ type ThrowFormatter struct { RPAR string } -func MustFormatThrows(throws *parser.Throws) string { +func MustFormatThrows(throws *parser.Throws, opts Options) string { if throws == nil { return "" } args := "" if len(throws.Fields) > 0 { - args = "\n" + MustFormatFields(throws.Fields, Indent+Indent) + Indent + indent := opts.GetIndent() + args = "\n" + MustFormatFields(throws.Fields, opts, indent+indent) + indent } f := &ThrowFormatter{ - Throw: MustFormatKeyword(throws.ThrowsKeyword.Keyword), - LPAR: MustFormatKeyword(throws.LParKeyword.Keyword), + Throw: MustFormatKeyword(opts, throws.ThrowsKeyword.Keyword), + LPAR: MustFormatKeyword(opts, throws.LParKeyword.Keyword), Fields: args, - RPAR: MustFormatKeyword(throws.RParKeyword.Keyword), + RPAR: MustFormatKeyword(opts, throws.RParKeyword.Keyword), } return MustFormat(throwTpl, f) diff --git a/format/identifier.go b/format/identifier.go index 995f914..0708b4e 100644 --- a/format/identifier.go +++ b/format/identifier.go @@ -6,8 +6,8 @@ import ( "github.com/joyme123/thrift-ls/parser" ) -func MustFormatIdentifier(id *parser.Identifier, indent string) string { - comments := MustFormatComments(id.Comments, indent) +func MustFormatIdentifier(opts Options, id *parser.Identifier, indent string) string { + comments := MustFormatComments(opts, id.Comments, indent, "") if comments != "" { comments = comments if lineDistance(id.Comments[len(id.Comments)-1], id.Name) >= 1 { diff --git a/format/include.go b/format/include.go index 6b64919..df01ced 100644 --- a/format/include.go +++ b/format/include.go @@ -13,33 +13,33 @@ type IncludeFormatter struct { EndLineComments string } -func MustFormatInclude(inc *parser.Include) string { - comments, _ := formatCommentsAndAnnos(inc.Comments, nil, "") +func MustFormatInclude(inc *parser.Include, opts Options) string { + comments, _ := formatCommentsAndAnnos(opts, inc.Comments, nil, "") if len(inc.Comments) > 0 && lineDistance(inc.Comments[len(inc.Comments)-1], inc.IncludeKeyword) > 1 { comments = comments + "\n" } f := &IncludeFormatter{ Comments: comments, - Include: MustFormatKeyword(inc.IncludeKeyword.Keyword), - Path: MustFormatLiteral(inc.Path, ""), - EndLineComments: MustFormatComments(inc.EndLineComments, ""), + Include: MustFormatKeyword(opts, inc.IncludeKeyword.Keyword), + Path: MustFormatLiteral(opts, inc.Path, ""), + EndLineComments: MustFormatComments(opts, inc.EndLineComments, "", ""), } return MustFormat(includeTpl, f) } -func MustFormatCPPInclude(inc *parser.CPPInclude) string { - comments, _ := formatCommentsAndAnnos(inc.Comments, nil, "") +func MustFormatCPPInclude(inc *parser.CPPInclude, opts Options) string { + comments, _ := formatCommentsAndAnnos(opts, inc.Comments, nil, "") if len(inc.Comments) > 0 && lineDistance(inc.Comments[len(inc.Comments)-1], inc.CPPIncludeKeyword) > 1 { comments = comments + "\n" } f := &IncludeFormatter{ Comments: comments, - Include: MustFormatKeyword(inc.CPPIncludeKeyword.Keyword), - Path: MustFormatLiteral(inc.Path, ""), - EndLineComments: MustFormatComments(inc.EndLineComments, ""), + Include: MustFormatKeyword(opts, inc.CPPIncludeKeyword.Keyword), + Path: MustFormatLiteral(opts, inc.Path, ""), + EndLineComments: MustFormatComments(opts, inc.EndLineComments, "", ""), } return MustFormat(includeTpl, f) diff --git a/format/keyword.go b/format/keyword.go index b26939c..d4053d0 100644 --- a/format/keyword.go +++ b/format/keyword.go @@ -6,10 +6,10 @@ import ( "github.com/joyme123/thrift-ls/parser" ) -func MustFormatKeyword(kw parser.Keyword) string { +func MustFormatKeyword(opts Options, kw parser.Keyword) string { if len(kw.Comments) > 0 { buf := bytes.NewBuffer(nil) - buf.WriteString(MustFormatComments(kw.Comments, "")) + buf.WriteString(MustFormatComments(opts, kw.Comments, "", "")) if lineDistance(kw.Comments[len(kw.Comments)-1], kw.Literal) >= 1 { buf.WriteString("\n") diff --git a/format/literal.go b/format/literal.go index cccc394..d22d099 100644 --- a/format/literal.go +++ b/format/literal.go @@ -7,10 +7,10 @@ import ( "github.com/joyme123/thrift-ls/parser" ) -func MustFormatLiteral(l *parser.Literal, indent string) string { +func MustFormatLiteral(opts Options, l *parser.Literal, indent string) string { if len(l.Comments) > 0 { buf := bytes.NewBuffer(nil) - buf.WriteString(MustFormatComments(l.Comments, indent)) + buf.WriteString(MustFormatComments(opts, l.Comments, indent, "")) if lineDistance(l.Comments[len(l.Comments)-1], l.Value) >= 1 { buf.WriteString("\n") diff --git a/format/namespace.go b/format/namespace.go index d899b9c..92f8159 100644 --- a/format/namespace.go +++ b/format/namespace.go @@ -15,19 +15,19 @@ type NamespaceFormatter struct { EndLineComments string } -func MustFormatNamespace(ns *parser.Namespace) string { - comments, annos := formatCommentsAndAnnos(ns.Comments, ns.Annotations, "") +func MustFormatNamespace(ns *parser.Namespace, opts Options) string { + comments, annos := formatCommentsAndAnnos(opts, ns.Comments, ns.Annotations, "") if len(ns.Comments) > 0 && lineDistance(ns.Comments[len(ns.Comments)-1], ns.NamespaceKeyword) > 1 { comments = comments + "\n" } f := &NamespaceFormatter{ Comments: comments, - Namespace: MustFormatKeyword(ns.NamespaceKeyword.Keyword), - Language: MustFormatIdentifier(&ns.Language.Identifier, ""), - Name: MustFormatIdentifier(ns.Name, ""), + Namespace: MustFormatKeyword(opts, ns.NamespaceKeyword.Keyword), + Language: MustFormatIdentifier(opts, &ns.Language.Identifier, ""), + Name: MustFormatIdentifier(opts, ns.Name, ""), Annotations: annos, - EndLineComments: MustFormatEndLineComments(ns.EndLineComments, ""), + EndLineComments: MustFormatEndLineComments(opts, ns.EndLineComments, "", ""), } return MustFormat(namespaceOneLineTpl, f) diff --git a/format/options.go b/format/options.go index 7c8c047..e187a16 100644 --- a/format/options.go +++ b/format/options.go @@ -6,11 +6,6 @@ import ( "strings" ) -var Indent = " " -var Align = "field" -var FieldLineComma = "disable" -var TrailingNewline = false - type Options struct { // Do not print reformatted sources to standard output. // If a file's formatting is different from thriftls's, overwrite it @@ -51,15 +46,13 @@ func (o *Options) SetFlags() { flag.BoolVar(&o.TrailingNewline, "trailingNewline", false, "Add trailing newline at end of file") } -func (o *Options) InitDefault() { - Indent = o.GetIndent() - +// InitDefault validates and sets default values for Options fields. +// It returns the Options with defaults applied for method chaining. +func (o *Options) InitDefault() Options { if o.Align == "" || (o.Align != AlignTypeField && o.Align != AlignTypeAssign && o.Align != AlignTypeDisable) { o.Align = "field" } - Align = o.Align - if o.FieldLineComma == "" || (o.FieldLineComma != FieldLineCommaAdd && o.FieldLineComma != FieldLineCommaRemove && @@ -67,8 +60,23 @@ func (o *Options) InitDefault() { o.FieldLineComma = "disable" } - FieldLineComma = o.FieldLineComma - TrailingNewline = o.TrailingNewline + return *o +} + +// getAlign returns the align option, using default if empty. +func (o Options) getAlign() string { + if o.Align == "" { + return AlignTypeField + } + return o.Align +} + +// getFieldLineComma returns the field line comma option, using default if empty. +func (o Options) getFieldLineComma() string { + if o.FieldLineComma == "" { + return FieldLineCommaDisable + } + return o.FieldLineComma } func (o *Options) GetIndent() string { diff --git a/format/service.go b/format/service.go index e7be726..9c7286a 100644 --- a/format/service.go +++ b/format/service.go @@ -26,28 +26,28 @@ type ServiceFormatter struct { ExtendServiceName string } -func MustFormatService(svc *parser.Service) string { - comments, annos := formatCommentsAndAnnos(svc.Comments, svc.Annotations, "") +func MustFormatService(svc *parser.Service, opts Options) string { + comments, annos := formatCommentsAndAnnos(opts, svc.Comments, svc.Annotations, "") if len(svc.Comments) > 0 && lineDistance(svc.Comments[len(svc.Comments)-1], svc.ServiceKeyword) > 1 { comments = comments + "\n" } f := ServiceFormatter{ Comments: comments, - Service: MustFormatKeyword(svc.ServiceKeyword.Keyword), - Identifier: MustFormatIdentifier(svc.Name, ""), - LCUR: MustFormatKeyword(svc.LCurKeyword.Keyword), - Functions: MustFormatFunctions(svc.Functions, Indent), - RCUR: MustFormatKeyword(svc.RCurKeyword.Keyword), + Service: MustFormatKeyword(opts, svc.ServiceKeyword.Keyword), + Identifier: MustFormatIdentifier(opts, svc.Name, ""), + LCUR: MustFormatKeyword(opts, svc.LCurKeyword.Keyword), + Functions: MustFormatFunctions(svc.Functions, opts, opts.GetIndent()), + RCUR: MustFormatKeyword(opts, svc.RCurKeyword.Keyword), Annotations: annos, - EndLineComments: MustFormatEndLineComments(svc.EndLineComments, ""), + EndLineComments: MustFormatEndLineComments(opts, svc.EndLineComments, "", ""), } if svc.ExtendsKeyword != nil { - f.Extends = " " + MustFormatKeyword(svc.ExtendsKeyword.Keyword) + f.Extends = " " + MustFormatKeyword(opts, svc.ExtendsKeyword.Keyword) } if svc.Extends != nil { - f.ExtendServiceName = " " + MustFormatIdentifier(svc.Extends, "") + f.ExtendServiceName = " " + MustFormatIdentifier(opts, svc.Extends, "") } if len(svc.Functions) > 0 { diff --git a/format/struct.go b/format/struct.go index ff06e6c..30e35f8 100644 --- a/format/struct.go +++ b/format/struct.go @@ -23,8 +23,8 @@ type StructFormatter struct { EndLineComments string } -func MustFormatStruct(st *parser.Struct) string { - comments, annos := formatCommentsAndAnnos(st.Comments, st.Annotations, "") +func MustFormatStruct(st *parser.Struct, opts Options) string { + comments, annos := formatCommentsAndAnnos(opts, st.Comments, st.Annotations, "") if len(st.Comments) > 0 && lineDistance(st.Comments[len(st.Comments)-1], st.StructKeyword) > 1 { comments = comments + "\n" @@ -32,13 +32,13 @@ func MustFormatStruct(st *parser.Struct) string { f := StructFormatter{ Comments: comments, - Struct: MustFormatKeyword(st.StructKeyword.Keyword), - Identifier: MustFormatIdentifier(st.Identifier, ""), - LCUR: MustFormatKeyword(st.LCurKeyword.Keyword), - Fields: MustFormatFields(st.Fields, Indent), - RCUR: MustFormatKeyword(st.RCurKeyword.Keyword), + Struct: MustFormatKeyword(opts, st.StructKeyword.Keyword), + Identifier: MustFormatIdentifier(opts, st.Identifier, ""), + LCUR: MustFormatKeyword(opts, st.LCurKeyword.Keyword), + Fields: MustFormatFields(st.Fields, opts, opts.GetIndent()), + RCUR: MustFormatKeyword(opts, st.RCurKeyword.Keyword), Annotations: annos, - EndLineComments: MustFormatEndLineComments(st.EndLineComments, ""), + EndLineComments: MustFormatEndLineComments(opts, st.EndLineComments, "", ""), } if len(st.Fields) > 0 { diff --git a/format/struct_test.go b/format/struct_test.go index 44730ea..6150679 100644 --- a/format/struct_test.go +++ b/format/struct_test.go @@ -211,7 +211,8 @@ struct Foo { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - assert.Equal(t, tt.want, MustFormatStruct(tt.args.st)) + opts := Options{} + assert.Equal(t, tt.want, MustFormatStruct(tt.args.st, opts)) }) } } diff --git a/format/typedef.go b/format/typedef.go index 7d95dd0..b95650d 100644 --- a/format/typedef.go +++ b/format/typedef.go @@ -16,8 +16,8 @@ type TypedefFormatter struct { EndLineComments string } -func MustFormatTypedef(td *parser.Typedef) string { - comments, annos := formatCommentsAndAnnos(td.Comments, td.Annotations, "") +func MustFormatTypedef(td *parser.Typedef, opts Options) string { + comments, annos := formatCommentsAndAnnos(opts, td.Comments, td.Annotations, "") if len(td.Comments) > 0 && lineDistance(td.Comments[len(td.Comments)-1], td.TypedefKeyword) > 1 { comments = comments + "\n" @@ -25,11 +25,11 @@ func MustFormatTypedef(td *parser.Typedef) string { f := &TypedefFormatter{ Comments: comments, - Typedef: MustFormatKeyword(td.TypedefKeyword.Keyword), - Type: MustFormatFieldType(td.T), - Name: MustFormatIdentifier(td.Alias, ""), + Typedef: MustFormatKeyword(opts, td.TypedefKeyword.Keyword), + Type: MustFormatFieldType(td.T, opts), + Name: MustFormatIdentifier(opts, td.Alias, ""), Annotations: annos, - EndLineComments: MustFormatEndLineComments(td.EndLineComments, ""), + EndLineComments: MustFormatEndLineComments(opts, td.EndLineComments, "", ""), } return MustFormat(typedefOneLineTpl, f) diff --git a/format/union.go b/format/union.go index cc6c263..4bf8304 100644 --- a/format/union.go +++ b/format/union.go @@ -23,8 +23,8 @@ type UnionFormatter struct { EndLineComments string } -func MustFormatUnion(union *parser.Union) string { - comments, annos := formatCommentsAndAnnos(union.Comments, union.Annotations, "") +func MustFormatUnion(union *parser.Union, opts Options) string { + comments, annos := formatCommentsAndAnnos(opts, union.Comments, union.Annotations, "") if len(union.Comments) > 0 && lineDistance(union.Comments[len(union.Comments)-1], union.UnionKeyword) > 1 { comments = comments + "\n" @@ -32,13 +32,13 @@ func MustFormatUnion(union *parser.Union) string { f := UnionFormatter{ Comments: comments, - Union: MustFormatKeyword(union.UnionKeyword.Keyword), - Identifier: MustFormatIdentifier(union.Name, ""), - LCUR: MustFormatKeyword(union.LCurKeyword.Keyword), - Fields: MustFormatFields(union.Fields, Indent), - RCUR: MustFormatKeyword(union.RCurKeyword.Keyword), + Union: MustFormatKeyword(opts, union.UnionKeyword.Keyword), + Identifier: MustFormatIdentifier(opts, union.Name, ""), + LCUR: MustFormatKeyword(opts, union.LCurKeyword.Keyword), + Fields: MustFormatFields(union.Fields, opts, opts.GetIndent()), + RCUR: MustFormatKeyword(opts, union.RCurKeyword.Keyword), Annotations: annos, - EndLineComments: MustFormatEndLineComments(union.EndLineComments, ""), + EndLineComments: MustFormatEndLineComments(opts, union.EndLineComments, "", ""), } if len(union.Fields) > 0 { diff --git a/format/utils.go b/format/utils.go index 7d6984b..ade3bf7 100644 --- a/format/utils.go +++ b/format/utils.go @@ -36,25 +36,25 @@ func MustFormat(tplText string, formatter any) string { return buf.String() } -func formatCommentsAndAnnos(comments []*parser.Comment, annotations *parser.Annotations, indent string) (string, string) { +func formatCommentsAndAnnos(opts Options, comments []*parser.Comment, annotations *parser.Annotations, indent string) (string, string) { commentsStr := "" if len(comments) > 0 { - commentsStr = MustFormatComments(comments, indent) + "\n" + commentsStr = MustFormatComments(opts, comments, indent, "") + "\n" } annos := "" if annotations != nil && len(annotations.Annotations) > 0 { - annos = " " + MustFormatAnnotations(annotations) + annos = " " + MustFormatAnnotations(annotations, opts) } return commentsStr, annos } -func formatListSeparator(sep *parser.ListSeparatorKeyword) string { +func formatListSeparator(opts Options, sep *parser.ListSeparatorKeyword) string { if sep == nil { return "" } - return MustFormatKeyword(sep.Keyword) + return MustFormatKeyword(opts, sep.Keyword) } func lineDistance(preNode parser.Node, currentNode parser.Node) int { diff --git a/format/utils_test.go b/format/utils_test.go index a8140c4..37c76f9 100644 --- a/format/utils_test.go +++ b/format/utils_test.go @@ -13,7 +13,8 @@ func TestEqualsAfterFormat(t *testing.T) { assert.NoError(t, err) assert.NotNil(t, ast) - formated, err := FormatDocument(ast.(*parser.Document)) + opts := Options{} + formated, err := FormatDocument(ast.(*parser.Document), opts) assert.NoError(t, err) type args struct { doc1 string diff --git a/lsp/codejump/hover.go b/lsp/codejump/hover.go index 632b608..24003dd 100644 --- a/lsp/codejump/hover.go +++ b/lsp/codejump/hover.go @@ -74,7 +74,9 @@ func hoverService(ctx context.Context, ss *cache.Snapshot, file uri.URI, ast *pa dstService := GetServiceNode(dstAst.AST(), identifier) if dstService != nil { - return format.MustFormatService(dstService), nil + // Use default options for hover display + opts := format.Options{} + return format.MustFormatService(dstService, opts), nil } return "", nil @@ -110,26 +112,29 @@ func hoverDefinition(ctx context.Context, ss *cache.Snapshot, file uri.URI, ast log.Errorf("parse error: %v", dstAst.Errors()) } + // Use default options for hover display + opts := format.Options{} + // struct, exception, enum or union dstException := GetExceptionNode(dstAst.AST(), identifier) if dstException != nil { - return format.MustFormatException(dstException), nil + return format.MustFormatException(dstException, opts), nil } dstStruct := GetStructNode(dstAst.AST(), identifier) if dstStruct != nil { - return format.MustFormatStruct(dstStruct), nil + return format.MustFormatStruct(dstStruct, opts), nil } dstEnum := GetEnumNode(dstAst.AST(), identifier) if dstEnum != nil { - return format.MustFormatEnum(dstEnum), nil + return format.MustFormatEnum(dstEnum, opts), nil } dstUnion := GetUnionNode(dstAst.AST(), identifier) if dstUnion != nil { - return format.MustFormatUnion(dstUnion), nil + return format.MustFormatUnion(dstUnion, opts), nil } dstTypedef := GetTypedefNode(dstAst.AST(), identifier) if dstTypedef != nil { - return format.MustFormatTypedef(dstTypedef), nil + return format.MustFormatTypedef(dstTypedef, opts), nil } return "", nil @@ -163,14 +168,17 @@ func hoverConstValue(ctx context.Context, ss *cache.Snapshot, file uri.URI, ast return "", err } + // Use default options for hover display + opts := format.Options{} + dstEnum := GetEnumNodeByEnumValue(dstAst.AST(), identifier) if dstEnum != nil { - return format.MustFormatEnum(dstEnum), nil + return format.MustFormatEnum(dstEnum, opts), nil } dstConst := GetConstNode(dstAst.AST(), identifier) if dstConst != nil { - return format.MustFormatConst(dstConst), nil + return format.MustFormatConst(dstConst, opts), nil } return "", nil diff --git a/lsp/format.go b/lsp/format.go index a313159..c168b14 100644 --- a/lsp/format.go +++ b/lsp/format.go @@ -9,8 +9,7 @@ import ( ) func (s *Server) formatting(ctx context.Context, params *protocol.DocumentFormattingParams) (result []protocol.TextEdit, err error) { - - // TODO: 支持 format options + opts := s.formatOpts document := params.TextDocument fileURI := document.URI @@ -40,7 +39,7 @@ func (s *Server) formatting(ctx context.Context, params *protocol.DocumentFormat return nil, pf.AggregatedError() } - formatted, err := format.FormatDocument(pf.AST()) + formatted, err := format.FormatDocument(pf.AST(), opts) if err != nil { return nil, err } diff --git a/lsp/impl_test.go b/lsp/impl_test.go index cd84d37..f031e92 100644 --- a/lsp/impl_test.go +++ b/lsp/impl_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/joyme123/protocol" + "github.com/joyme123/thrift-ls/format" "github.com/joyme123/thrift-ls/lsp/cache" "github.com/joyme123/thrift-ls/lsp/memoize" "github.com/stretchr/testify/assert" @@ -33,7 +34,7 @@ struct Test { store := &memoize.Store{} cache := cache.New(store, nil) - srv := NewServer(cache, nil) + srv := NewServer(cache, nil, format.Options{}) err = srv.DidOpen(ctx, params) assert.NoError(t, err) @@ -91,7 +92,7 @@ struct Test { store := &memoize.Store{} cache := cache.New(store, nil) - srv := NewServer(cache, nil) + srv := NewServer(cache, nil, format.Options{}) err = srv.DidOpen(ctx, openParams) assert.NoError(t, err) @@ -131,7 +132,7 @@ struct Test { 3: required string N }`, line: 5, - character: 28, + character: 28, wantLabel: "Name", wantPreselect: true, wantNewText: "Name", @@ -154,7 +155,7 @@ struct Test { store := &memoize.Store{} cache := cache.New(store, nil) - srv := NewServer(cache, nil) + srv := NewServer(cache, nil, format.Options{}) err = srv.DidOpen(ctx, openParams) assert.NoError(t, err) @@ -209,7 +210,7 @@ func Test_CompletionIncludeScope(t *testing.T) { includeSearch []string }{ { - name: "completion includes enum from included file", + name: "completion includes enum from included file", baseContent: `enum Name { ONE, TWO }`, testContent: `include "base.thrift" @@ -249,7 +250,7 @@ struct Test { store := &memoize.Store{} cache := cache.New(store, []string{"/tmp"}) - srv := NewServer(cache, nil) + srv := NewServer(cache, nil, format.Options{}) err = srv.DidOpen(ctx, baseParams) assert.NoError(t, err) @@ -296,13 +297,13 @@ func Test_CompletionNoGlobalPollution(t *testing.T) { ctx := context.TODO() for _, tt := range []struct { - name string - file1Content string - file2Content string - file1URI string - file2URI string - completionURI string - notWantLabels []string + name string + file1Content string + file2Content string + file1URI string + file2URI string + completionURI string + notWantLabels []string }{ { name: "completions in file1 should not include items from file2", @@ -353,7 +354,7 @@ struct Other { store := &memoize.Store{} cache := cache.New(store, nil) - srv := NewServer(cache, nil) + srv := NewServer(cache, nil, format.Options{}) err = srv.DidOpen(ctx, file1Params) assert.NoError(t, err) diff --git a/lsp/server.go b/lsp/server.go index f272c23..a199e2f 100644 --- a/lsp/server.go +++ b/lsp/server.go @@ -4,6 +4,7 @@ import ( "context" "github.com/joyme123/protocol" + "github.com/joyme123/thrift-ls/format" "github.com/joyme123/thrift-ls/lsp/cache" log "github.com/sirupsen/logrus" ) @@ -12,14 +13,16 @@ type Server struct { cache *cache.Cache session *cache.Session - client protocol.Client + client protocol.Client + formatOpts format.Options } -func NewServer(c *cache.Cache, client protocol.Client) *Server { +func NewServer(c *cache.Cache, client protocol.Client, formatOpts format.Options) *Server { return &Server{ - cache: c, - session: cache.NewSession(c), - client: client, + cache: c, + session: cache.NewSession(c), + client: client, + formatOpts: formatOpts, } } diff --git a/lsp/stream.go b/lsp/stream.go index 50c501a..4be5c43 100644 --- a/lsp/stream.go +++ b/lsp/stream.go @@ -4,6 +4,7 @@ import ( "context" "github.com/joyme123/protocol" + "github.com/joyme123/thrift-ls/format" "github.com/joyme123/thrift-ls/lsp/cache" "github.com/joyme123/thrift-ls/lsp/memoize" "go.lsp.dev/jsonrpc2" @@ -14,11 +15,13 @@ import ( type StreamServer struct { logger *zap.Logger - cache *cache.Cache + cache *cache.Cache + formatOpts format.Options } type Options struct { IncludePaths []string + Format format.Options } func NewStreamServer(opts *Options) *StreamServer { @@ -27,15 +30,16 @@ func NewStreamServer(opts *Options) *StreamServer { store := &memoize.Store{} return &StreamServer{ - cache: cache.New(store, opts.IncludePaths), - logger: logger, + cache: cache.New(store, opts.IncludePaths), + logger: logger, + formatOpts: opts.Format, } } func (s *StreamServer) ServeStream(ctx context.Context, conn jsonrpc2.Conn) error { client := protocol.ClientDispatcher(conn, s.logger) - server := NewServer(s.cache, client) + server := NewServer(s.cache, client, s.formatOpts) // Clients may or may not send a shutdown message. Make sure the server is // shut down. // TODO(rFindley): this shutdown should perhaps be on a disconnected context. diff --git a/lsp/symbols/field.go b/lsp/symbols/field.go index 1c6d34f..e448dcc 100644 --- a/lsp/symbols/field.go +++ b/lsp/symbols/field.go @@ -16,7 +16,9 @@ func FieldSymbol(field *parser.Field) *protocol.DocumentSymbol { if field.RequiredKeyword != nil { detail = field.RequiredKeyword.Literal.Text + " " } - detail += format.MustFormatFieldType(field.FieldType) + // Use default options for symbol display + opts := format.Options{} + detail += format.MustFormatFieldType(field.FieldType, opts) res := &protocol.DocumentSymbol{ Name: field.Identifier.Name.Text, diff --git a/lsp/symbols/typedef.go b/lsp/symbols/typedef.go index 6bdb0ec..293e9b2 100644 --- a/lsp/symbols/typedef.go +++ b/lsp/symbols/typedef.go @@ -8,9 +8,12 @@ import ( ) func TypedefSymbol(td *parser.Typedef) *protocol.DocumentSymbol { + // Use default options for symbol display + opts := format.Options{} + res := &protocol.DocumentSymbol{ Name: td.Alias.Name.Text, - Detail: format.MustFormatFieldType(td.T), + Detail: format.MustFormatFieldType(td.T, opts), Kind: protocol.SymbolKindTypeParameter, Range: lsputils.ASTNodeToRange(td.Alias.Name), SelectionRange: lsputils.ASTNodeToRange(td.Alias.Name), diff --git a/main.go b/main.go index 79ed5f4..ad3e5dc 100644 --- a/main.go +++ b/main.go @@ -53,7 +53,8 @@ func main_format(opt format.Options, file string, includePaths []string) error { fmt.Println(err) return err } - formated, err := format.FormatDocumentWithValidationFull(ast.(*parser.Document), true, includePaths, absFile) + + formated, err := format.FormatDocumentWithValidationFull(ast.(*parser.Document), opt, true, includePaths, absFile) if err != nil { fmt.Println(err) return err @@ -100,7 +101,7 @@ func main() { flag.Parse() opts := configInit(&formatOpts) - formatOpts.InitDefault() + formatOpts = formatOpts.InitDefault() tlog.Init(opts.LogLevel) if formatter { @@ -117,7 +118,10 @@ func main() { // panic(err) // } - ss := lsp.NewStreamServer(&lsp.Options{IncludePaths: opts.IncludePaths}) + ss := lsp.NewStreamServer(&lsp.Options{ + IncludePaths: opts.IncludePaths, + Format: formatOpts, + }) stream := jsonrpc2.NewStream(fakenet.NewConn("stdio", os.Stdin, os.Stdout)) conn := jsonrpc2.NewConn(stream) err := ss.ServeStream(ctx, conn) -- 2.51.2