- pkg/lsp: JSON-RPC 2.0 LSP server (formatting, diagnostics, codeAction quick-fixes) - cmd/pgtidy: lsp and config subcommands - pkg/diagnostics: TextFix struct for byte-range autofixes - pkg/lint: MIG001/MIG003 autofixes, ApplyFixes helper, --fix flag on lint command - editors/vscode: TypeScript extension with LanguageClient, showVersion/showConfig/formatDocument commands, logo - editors/datagrip: Gradle JetBrains plugin via LSP4IJ, pluginIcon - .goreleaser.yaml, .github/workflows: CI + release pipeline - Makefile: snapshot, release, vscode-compile, vscode-package targets - go.mod + all imports: module path updated to git.warky.dev/wdevs/pgtidy - assets: logo files (256px, 128px, 1024px, ico)
120 lines
2.4 KiB
Go
120 lines
2.4 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
|
|
"git.warky.dev/wdevs/pgtidy/pkg/config"
|
|
"git.warky.dev/wdevs/pgtidy/pkg/format"
|
|
"git.warky.dev/wdevs/pgtidy/pkg/parser"
|
|
)
|
|
|
|
// cmdFmt implements `pgtidy fmt`. It follows the gofmt model: with no flags it
|
|
// prints the formatted result to stdout; -w rewrites in place; -l lists files
|
|
// that differ; --check exits non-zero if any input is unformatted; -d prints a
|
|
// unified diff for each file that would change.
|
|
func cmdFmt(args []string, stdin io.Reader, stdout, stderr io.Writer) int {
|
|
var (
|
|
write bool
|
|
list bool
|
|
check bool
|
|
diff bool
|
|
files []string
|
|
)
|
|
for _, a := range args {
|
|
switch a {
|
|
case "-w", "--write":
|
|
write = true
|
|
case "-l", "--list":
|
|
list = true
|
|
case "--check":
|
|
check = true
|
|
case "-d", "--diff":
|
|
diff = true
|
|
case "-h", "--help":
|
|
usage(stdout)
|
|
return 0
|
|
default:
|
|
if len(a) > 1 && a[0] == '-' {
|
|
fmt.Fprintf(stderr, "pgtidy fmt: unknown flag %q\n", a)
|
|
return 2
|
|
}
|
|
files = append(files, a)
|
|
}
|
|
}
|
|
|
|
// Discover config: walk up from the working directory.
|
|
wd, err := os.Getwd()
|
|
if err != nil {
|
|
wd = "."
|
|
}
|
|
st, err := config.Load(wd)
|
|
if err != nil {
|
|
fmt.Fprintf(stderr, "%v\n", err)
|
|
return 2
|
|
}
|
|
|
|
// stdin → stdout when no files are given.
|
|
if len(files) == 0 {
|
|
src, err := io.ReadAll(stdin)
|
|
if err != nil {
|
|
fmt.Fprintf(stderr, "pgtidy: reading stdin: %v\n", err)
|
|
return 2
|
|
}
|
|
out := format.File(parser.Parse(string(src)), st)
|
|
switch {
|
|
case check:
|
|
if out != string(src) {
|
|
return 1
|
|
}
|
|
case diff:
|
|
io.WriteString(stdout, unifiedDiff(string(src), out, "stdin"))
|
|
default:
|
|
io.WriteString(stdout, out)
|
|
}
|
|
return 0
|
|
}
|
|
|
|
exit := 0
|
|
anyDiff := false
|
|
for _, path := range files {
|
|
src, err := os.ReadFile(path)
|
|
if err != nil {
|
|
fmt.Fprintf(stderr, "pgtidy: %v\n", err)
|
|
exit = 2
|
|
continue
|
|
}
|
|
out := format.File(parser.Parse(string(src)), st)
|
|
changed := out != string(src)
|
|
if changed {
|
|
anyDiff = true
|
|
}
|
|
switch {
|
|
case write:
|
|
if changed {
|
|
if err := os.WriteFile(path, []byte(out), 0o644); err != nil {
|
|
fmt.Fprintf(stderr, "pgtidy: writing %s: %v\n", path, err)
|
|
exit = 2
|
|
}
|
|
}
|
|
case list:
|
|
if changed {
|
|
fmt.Fprintln(stdout, path)
|
|
}
|
|
case diff:
|
|
if changed {
|
|
io.WriteString(stdout, unifiedDiff(string(src), out, path))
|
|
}
|
|
case check:
|
|
// handled after loop via anyDiff
|
|
default:
|
|
io.WriteString(stdout, out)
|
|
}
|
|
}
|
|
if check && anyDiff && exit == 0 {
|
|
return 1
|
|
}
|
|
return exit
|
|
}
|