func (s *Shortener) shortenCommentsFunc(contents []byte) []byte {
	cleanedLines := []string{}
	lines := strings.Split(string(contents), "\n")
	for _, line := range lines {
		if s.isComment(line) && !IsAnnotation(line) &&
			!s.isGoDirective(line) &&
			s.lineLen(line) > s.config.MaxLen {
			start := strings.Index(line, "//")
			prefix := line[0:(start + 2)]
			maxCommentLen := s.config.MaxLen - s.lineLen(prefix) - 1
			trimmedLine := strings.Trim(line[(start+2):], " ")
			words := strings.Split(trimmedLine, " ")
			currLineWords := []string{}
			currLineLen := 0
			for _, word := range words {
				if currLineLen > 0 && currLineLen+1+len(word) > maxCommentLen {
					cleanedLines = append(
						cleanedLines,
						fmt.Sprintf(
							"%s %s",
							prefix,
							strings.Join(currLineWords, " "),
						),
					)
					currLineWords = []string{}
					currLineLen = 0
				}
				currLineWords = append(currLineWords, word)
				if currLineLen == 0 {
					currLineLen = len(word)
				} else {
					currLineLen += 1 + len(word)
				}
			}
			if currLineLen > 0 {
				cleanedLines = append(
					cleanedLines,
					fmt.Sprintf(
						"%s %s",
						prefix,
						strings.Join(currLineWords, " "),
					),
				)
			}
		} else {
			cleanedLines = append(cleanedLines, line)
		}
	}
	return []byte(strings.Join(cleanedLines, "\n"))
}
shortenCommentsFunc attempts to shorten long comments in the provided source. As noted
in the repo README, this functionality has some quirks and is disabled by default.
package main
import (
	"bufio"
	"bytes"
	"fmt"
	"go/format"
	"go/token"
	"os"
	"os/exec"
	"reflect"
	"regexp"
	"strings"
	"github.com/dave/dst"
	"github.com/dave/dst/decorator"
	log "github.com/sirupsen/logrus"
)
var (
	generatedTerms = []string{
		"do not edit",
		"generated by",
	}
	goDirectiveLine = regexp.MustCompile(`\s* 
)
const maxRounds = 20
type ShortenerConfig struct {
	MaxLen          int     
	TabLen          int     
	KeepAnnotations bool    
	ShortenComments bool    
	ReformatTags    bool    
	IgnoreGenerated bool    
	DotFile         string  
	ChainSplitDots  bool    
	BaseFormatterCmd string
}
type Shortener struct {
	config ShortenerConfig
	baseFormatter     string
	baseFormatterArgs []string
}
func NewShortener(config ShortenerConfig) *Shortener {
	var formatterComponents []string
	if config.BaseFormatterCmd == "" {
		_, err := exec.LookPath("goimports")
		if err != nil {
			formatterComponents = []string{"gofmt"}
		} else {
			formatterComponents = []string{"goimports"}
		}
	} else {
		formatterComponents = strings.Split(config.BaseFormatterCmd, " ")
	}
	s := &Shortener{
		config:        config,
		baseFormatter: formatterComponents[0],
	}
	if len(formatterComponents) > 1 {
		s.baseFormatterArgs = formatterComponents[1:]
	} else {
		s.baseFormatterArgs = []string{}
	}
	return s
}
func (s *Shortener) Shorten(contents []byte) ([]byte, error) {
	if s.config.IgnoreGenerated && s.isGenerated(contents) {
		return contents, nil
	}
	round := 0
	var err error
	contents, err = s.formatSrc(contents)
	if err != nil {
		return nil, fmt.Errorf("Error formatting source: %+v", err)
	}
	for {
		log.Debugf("Starting round %d", round)
		lines := strings.Split(string(contents), "\n")
		annotatedLines, linesToShorten := s.annotateLongLines(lines)
		var stop bool
		if linesToShorten == 0 {
			if round == 0 {
				if !s.config.ReformatTags {
					stop = true
				} else if !HasMultiKeyTags(lines) {
					stop = true
				}
			} else {
				stop = true
			}
		}
		if stop {
			log.Debug("Nothing more to shorten or reformat, stopping")
			break
		}
		contents = []byte(strings.Join(annotatedLines, "\n"))
		result, err := decorator.Parse(contents)
		if err != nil {
			return nil, err
		}
		if s.config.DotFile != "" {
			dotFile, err := os.Create(s.config.DotFile)
			if err != nil {
				return nil, err
			}
			defer dotFile.Close()
			log.Debugf("Writing dot file output to %s", s.config.DotFile)
			err = CreateDot(result, dotFile)
			if err != nil {
				return nil, err
			}
		}
		for _, decl := range result.Decls {
			s.formatNode(decl)
		}
		output := bytes.NewBuffer([]byte{})
		err = decorator.Fprint(output, result)
		if err != nil {
			return nil, fmt.Errorf("Error parsing source: %+v", err)
		}
		contents = output.Bytes()
		round++
		if round > maxRounds {
			log.Debugf("Hit max rounds, stopping")
			break
		}
	}
	if !s.config.KeepAnnotations {
		contents = s.removeAnnotations(contents)
	}
	if s.config.ShortenComments {
		contents = s.shortenCommentsFunc(contents)
	}
	contents, err = s.formatSrc(contents)
	if err != nil {
		return nil, fmt.Errorf("Error formatting source: %+v", err)
	}
	return contents, nil
}
func (s *Shortener) formatSrc(contents []byte) ([]byte, error) {
	if s.baseFormatter == "gofmt" {
		return format.Source(contents)
	}
	cmd := exec.Command(s.baseFormatter, s.baseFormatterArgs...)
	stdinPipe, err := cmd.StdinPipe()
	if err != nil {
		return nil, err
	}
	outBuffer := &bytes.Buffer{}
	cmd.Stdout = outBuffer
	if err = cmd.Start(); err != nil {
		return nil, err
	}
	_, err = stdinPipe.Write(contents)
	if err != nil {
		return nil, err
	}
	stdinPipe.Close()
	err = cmd.Wait()
	if err != nil {
		return nil, err
	}
	return outBuffer.Bytes(), nil
}
func (s *Shortener) annotateLongLines(lines []string) ([]string, int) {
	annotatedLines := []string{}
	linesToShorten := 0
	prevLen := -1
	for _, line := range lines {
		length := s.lineLen(line)
		if prevLen > -1 {
			if length <= s.config.MaxLen {
				annotatedLines = annotatedLines[:len(annotatedLines)-1]
			} else if length < prevLen {
				annotatedLines[len(annotatedLines)-1] = CreateAnnotation(length)
				linesToShorten++
			}
		} else if !s.isComment(line) && length > s.config.MaxLen {
			annotatedLines = append(
				annotatedLines,
				CreateAnnotation(length),
			)
			linesToShorten++
		}
		annotatedLines = append(annotatedLines, line)
		prevLen = ParseAnnotation(line)
	}
	return annotatedLines, linesToShorten
}
func (s *Shortener) removeAnnotations(contents []byte) []byte {
	cleanedLines := []string{}
	lines := strings.Split(string(contents), "\n")
	for _, line := range lines {
		if !IsAnnotation(line) {
			cleanedLines = append(cleanedLines, line)
		}
	}
	return []byte(strings.Join(cleanedLines, "\n"))
}