diff --git a/internal/lsp/source/format.go b/internal/lsp/source/format.go index e9f12d2c..7a52cbcb 100644 --- a/internal/lsp/source/format.go +++ b/internal/lsp/source/format.go @@ -31,7 +31,15 @@ func Format(ctx context.Context, f GoFile, rng span.Range) ([]TextEdit, error) { } pkg := f.GetPackage(ctx) if hasListErrors(pkg.GetErrors()) || hasParseErrors(pkg.GetErrors()) { - return nil, fmt.Errorf("%s has parse errors, not formatting", f.URI()) + // Even if this package has list or parse errors, this file may not + // have any parse errors and can still be formatted. Using format.Node + // on an ast with errors may result in code being added or removed. + // Attempt to format the source of this file instead. + formatted, err := formatSource(ctx, f) + if err != nil { + return nil, err + } + return computeTextEdits(ctx, f, string(formatted)), nil } path, exact := astutil.PathEnclosingInterval(file, rng.Start, rng.End) if !exact || len(path) == 0 { @@ -52,6 +60,16 @@ func Format(ctx context.Context, f GoFile, rng span.Range) ([]TextEdit, error) { return computeTextEdits(ctx, f, buf.String()), nil } +func formatSource(ctx context.Context, file File) ([]byte, error) { + ctx, done := trace.StartSpan(ctx, "source.formatSource") + defer done() + data, _, err := file.Handle(ctx).Read(ctx) + if err != nil { + return nil, err + } + return format.Source(data) +} + // Imports formats a file using the goimports tool. func Imports(ctx context.Context, view View, f GoFile, rng span.Range) ([]TextEdit, error) { ctx, done := trace.StartSpan(ctx, "source.Imports") diff --git a/internal/lsp/testdata/noparse_format/parse_format.go.golden b/internal/lsp/testdata/noparse_format/parse_format.go.golden new file mode 100644 index 00000000..474ad90b --- /dev/null +++ b/internal/lsp/testdata/noparse_format/parse_format.go.golden @@ -0,0 +1,9 @@ +-- gofmt -- +package noparse_format //@format("package") + +func _() { + f() +} + +-- gofmt-d -- + diff --git a/internal/lsp/testdata/noparse_format/parse_format.go.in b/internal/lsp/testdata/noparse_format/parse_format.go.in new file mode 100644 index 00000000..4b98cf8d --- /dev/null +++ b/internal/lsp/testdata/noparse_format/parse_format.go.in @@ -0,0 +1,5 @@ +package noparse_format //@format("package") + +func _() { +f() +} \ No newline at end of file diff --git a/internal/lsp/tests/tests.go b/internal/lsp/tests/tests.go index 767d8175..d649864b 100644 --- a/internal/lsp/tests/tests.go +++ b/internal/lsp/tests/tests.go @@ -29,7 +29,7 @@ const ( ExpectedCompletionsCount = 144 ExpectedCompletionSnippetCount = 15 ExpectedDiagnosticsCount = 17 - ExpectedFormatCount = 5 + ExpectedFormatCount = 6 ExpectedImportCount = 2 ExpectedDefinitionsCount = 38 ExpectedTypeDefinitionsCount = 2