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(`.* orig line is ([\d]+), orig col is ([\d]+)`) lin_rx = regexp.MustCompile(`.* orig line is ([\d]+)`) grb_rx = regexp.MustCompile(`stdin:([\d]+) Garbage in col ([\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{"--replace", "--no-backup", "-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() str += "\n" + stdout.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)) str = lin_rx.ReplaceAllString(str, fmt.Sprintf("%s:$1", samfile)) str = grb_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 sidx, eidx := 0, 0 for p := range before { for i := range edits[sidx:] { if edits[sidx+i].Start == p { edits[sidx+i].Start = rcount sidx += i break } } for i := range edits[eidx:] { if edits[i+eidx].End == p { edits[i+eidx].End = rcount eidx += i break } } rcount++ if eidx == 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 %q to acme/%d/addr: %s\n", addr, 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() }