uncrust/uncrust.go

125 lines
2.5 KiB
Go

package main
import (
"bytes"
"flag"
"fmt"
"log"
"os"
"os/exec"
"path/filepath"
"regexp"
"strconv"
"strings"
"9fans.net/go/acme"
"github.com/akedrou/textdiff"
)
var (
fl_lang = flag.String("l", "C", "Language")
fl_verbose = flag.Bool("v", false, "Verbosity on")
pos_rx = regexp.MustCompile(`File: stdin, orig line is ([\d]+), orig col is ([\d])`)
)
func main() {
flag.Parse()
samfile := os.Getenv("samfile")
id := os.Getenv("winid")
if "" == id {
log.Fatalf("no $winid set, not running in acme?")
}
wid, e := strconv.Atoi(id)
if e != nil {
log.Fatalf("couldn't parse $winid: %s\n", e)
}
win, e := acme.Open(wid, nil)
if e != nil {
log.Fatalf("couldn't open acme window id %d: %s\n", wid, e)
}
body, e := win.ReadAll("body")
if e != nil {
log.Fatalf("couldn't read body of window id %d: %s\n", wid, e)
}
args := []string{"-l", *fl_lang}
stderr := &bytes.Buffer{}
stdout := &bytes.Buffer{}
cmd := exec.Command("uncrustify", args...)
cmd.Stdin = bytes.NewBuffer(body)
cmd.Stdout = stdout
cmd.Stderr = stderr
samfile = filepath.Base(samfile)
if e = cmd.Run(); e != nil {
if _, ok := e.(*exec.ExitError); ok {
str := stderr.String()
if len(samfile) != 0 {
str = strings.ReplaceAll(str, "stdin, open_line is ", samfile+":")
str = pos_rx.ReplaceAllString(str, fmt.Sprintf("%s:$1:#$2", samfile))
}
log.Fatal("error: " + str)
}
log.Fatalf("error: %s\n", e)
return
}
after := stdout.String()
before := string(body)
edits := textdiff.Strings(before, after)
textdiff.SortEdits(edits)
// We have to calculate the rune position from the byte position.
rcount := 0
idx := 0
for p := range before {
for i := range edits[idx:] {
if edits[i].Start == p {
edits[i].Start = rcount
}
if edits[i].End == p {
edits[i].End = rcount
idx++
}
}
rcount++
if idx == len(edits) {
break
}
}
if len(edits) > 0 {
if *fl_verbose {
log.Printf("Applying %d diffs:\n%s\n", len(edits), pretty(edits, samfile))
}
win.Write("ctl", []byte("mark"))
win.Write("ctl", []byte("nomark"))
}
for i := len(edits) - 1; i >= 0; i-- {
edit := edits[i]
addr := fmt.Sprintf("#%d,#%d", edit.Start, edit.End)
e = win.Addr(addr)
if e != nil {
log.Fatalf("error writing to acme/%d/addr: %s\n", wid, e)
}
win.Write("data", []byte(edit.New))
}
}
func pretty(e []textdiff.Edit, p string) string {
b := &bytes.Buffer{}
for _, e := range e {
fmt.Fprintf(b, "%s:#%d,#%d: »%s«\n", p, e.Start, e.End, e.New)
}
return b.String()
}