Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions _fixtures/editorconfig_max_len.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package fixtures

import (
"fmt"
"time"
)

func _() {
veryLongString := fmt.Sprintf("The current time is %v and %d, %d, %d, %d, %d, %d", time.Now(), 1, 2, 3, 4, 5, 6)
anotherLongString := fmt.Sprintf("The current time is %v and %d, %d, %d, %d, %d, %d, %d, %d, %d", time.Now(), 1, 2, 3, 4, 5, 6, 7, 8, 9)

extremelyLongSlice := []string{"This is the first very long element in a slice", "This is the second very long element that contains a lot of text to make the line exceed 120 characters", "And this is the third one"}

someVeryLongMap := map[string]string{"ThisIsAVeryLongKeyThatWillMakeThisLineExceed120Characters": "AndThisIsAnEquallyLongValueThatWillContributeToMakingThisLineVeryVeryLongIndeed"}

_ = veryLongString
_ = anotherLongString
_ = extremelyLongSlice
_ = someVeryLongMap
}
38 changes: 38 additions & 0 deletions _fixtures/editorconfig_max_len__exp.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package fixtures

import (
"fmt"
"time"
)

func _() {
veryLongString := fmt.Sprintf("The current time is %v and %d, %d, %d, %d, %d, %d", time.Now(), 1, 2, 3, 4, 5, 6)
anotherLongString := fmt.Sprintf(
"The current time is %v and %d, %d, %d, %d, %d, %d, %d, %d, %d",
time.Now(),
1,
2,
3,
4,
5,
6,
7,
8,
9,
)

extremelyLongSlice := []string{
"This is the first very long element in a slice",
"This is the second very long element that contains a lot of text to make the line exceed 120 characters",
"And this is the third one",
}

someVeryLongMap := map[string]string{
"ThisIsAVeryLongKeyThatWillMakeThisLineExceed120Characters": "AndThisIsAnEquallyLongValueThatWillContributeToMakingThisLineVeryVeryLongIndeed",
}

_ = veryLongString
_ = anotherLongString
_ = extremelyLongSlice
_ = someVeryLongMap
}
8 changes: 6 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
module github.com/segmentio/golines

go 1.21
go 1.22.0

toolchain go1.24.2

require (
github.com/dave/dst v0.27.3
github.com/dave/jennifer v1.7.0
github.com/editorconfig/editorconfig-core-go/v2 v2.6.3
github.com/fatih/structtag v1.2.0
github.com/pmezard/go-difflib v1.0.0
github.com/sirupsen/logrus v1.9.3
Expand All @@ -24,9 +27,10 @@ require (
github.com/onsi/ginkgo v1.10.2 // indirect
github.com/onsi/gomega v1.7.0 // indirect
golang.org/x/crypto v0.31.0 // indirect
golang.org/x/mod v0.14.0 // indirect
golang.org/x/mod v0.23.0 // indirect
golang.org/x/sys v0.28.0 // indirect
golang.org/x/tools v0.17.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
10 changes: 8 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,14 @@ github.com/dave/jennifer v1.7.0/go.mod h1:nXbxhEmQfOZhWml3D1cDK5M1FLnMSozpbFN/m3
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/editorconfig/editorconfig-core-go/v2 v2.6.3 h1:XVUp6qW3BIkmM3/1EkrHpa6bL56APOynfXcZEmIgOhs=
github.com/editorconfig/editorconfig-core-go/v2 v2.6.3/go.mod h1:ThHVc+hqbUsmE1wmK/MASpQEhCleWu1JDJDNhUOMy0c=
github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4=
github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
Expand Down Expand Up @@ -42,8 +46,8 @@ github.com/x-cray/logrus-prefixed-formatter v0.5.2 h1:00txxvfBM9muc0jiLIEAkAcIMJ
github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE=
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM=
golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
Expand All @@ -69,6 +73,8 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
Expand Down
9 changes: 7 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import (
kingpin "gopkg.in/alecthomas/kingpin.v2"
)

const defaultMaxLen = 100

var (
// these values are provided automatically by Goreleaser
// ref: https://goreleaser.com/customization/builds/
Expand Down Expand Up @@ -53,7 +55,7 @@ var (
"List files that would be reformatted by this tool").Short('l').Default("false").Bool()
maxLen = kingpin.Flag(
"max-len",
"Target maximum line length").Short('m').Default("100").Int()
fmt.Sprintf("Maximum line length, or .editorconfig or default to %d", defaultMaxLen)).Short('m').Int()
profile = kingpin.Flag(
"profile",
"Path to profile output").Default("").String()
Expand Down Expand Up @@ -118,6 +120,7 @@ func main() {
func run() error {
config := ShortenerConfig{
MaxLen: *maxLen,
CurrentMaxLen: 0,
TabLen: *tabLen,
KeepAnnotations: *keepAnnotations,
ShortenComments: *shortenComments,
Expand All @@ -127,6 +130,7 @@ func run() error {
BaseFormatterCmd: *baseFormatterCmd,
ChainSplitDots: *chainSplitDots,
}

shortener := NewShortener(config)

if len(*paths) == 0 {
Expand Down Expand Up @@ -219,7 +223,9 @@ func processFile(shortener *Shortener, path string) ([]byte, []byte, error) {
return nil, nil, err
}

shortener.SetCurrentMaxLen(path)
result, err := shortener.Shorten(contents)

return contents, result, err
}

Expand Down Expand Up @@ -258,5 +264,4 @@ func handleOutput(path string, contents []byte, result []byte) error {

fmt.Print(string(result))
return nil

}
47 changes: 42 additions & 5 deletions shortener.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@ import (
"os/exec"
"reflect"
"regexp"
"strconv"
"strings"

"github.com/dave/dst"
"github.com/dave/dst/decorator"
editorconfig "github.com/editorconfig/editorconfig-core-go/v2"
log "github.com/sirupsen/logrus"
)

Expand All @@ -36,6 +38,7 @@ const maxRounds = 20
// ShortenerConfig stores the configuration options exposed by a Shortener instance.
type ShortenerConfig struct {
MaxLen int // Max target width for each line
CurrentMaxLen int // Max target width for each line for the current file or context
TabLen int // Width of a tab character
KeepAnnotations bool // Whether to keep annotations in final result (for debugging only)
ShortenComments bool // Whether to shorten comments
Expand Down Expand Up @@ -86,9 +89,43 @@ func NewShortener(config ShortenerConfig) *Shortener {
s.baseFormatterArgs = []string{}
}

s.config.CurrentMaxLen = s.config.MaxLen
if s.config.CurrentMaxLen == 0 {
s.config.CurrentMaxLen = defaultMaxLen
}

return s
}

func (s *Shortener) SetCurrentMaxLen(path string) {
// defined by user
if s.config.MaxLen != 0 {
s.config.CurrentMaxLen = s.config.MaxLen

return
}

// Try to get .editorconfig definition for the given path
def, err := editorconfig.GetDefinitionForFilename(path)
if err != nil {
s.config.CurrentMaxLen = defaultMaxLen

return
}

defMaxLen, ok := def.Raw["max_line_length"]
if ok && defMaxLen != editorconfig.UnsetValue {
defMaxLenInt, err := strconv.Atoi(defMaxLen)
if err == nil {
s.config.CurrentMaxLen = defMaxLenInt

return
}
}

s.config.CurrentMaxLen = defaultMaxLen
}

// Shorten shortens the provided golang file content bytes.
func (s *Shortener) Shorten(contents []byte) ([]byte, error) {
if s.config.IgnoreGenerated && s.isGenerated(contents) {
Expand All @@ -101,7 +138,7 @@ func (s *Shortener) Shorten(contents []byte) ([]byte, error) {
// Do initial, non-line-length-aware formatting
contents, err = s.formatSrc(contents)
if err != nil {
return nil, fmt.Errorf("Error formatting source: %+v", err)
return nil, fmt.Errorf("error formatting source: %+v", err)
}

for {
Expand Down Expand Up @@ -234,15 +271,15 @@ func (s *Shortener) annotateLongLines(lines []string) ([]string, int) {
length := s.lineLen(line)

if prevLen > -1 {
if length <= s.config.MaxLen {
if length <= s.config.CurrentMaxLen {
// Shortening successful, remove previous annotation
annotatedLines = annotatedLines[:len(annotatedLines)-1]
} else if length < prevLen {
// Replace annotation with new length
annotatedLines[len(annotatedLines)-1] = CreateAnnotation(length)
linesToShorten++
}
} else if !s.isComment(line) && length > s.config.MaxLen {
} else if !s.isComment(line) && length > s.config.CurrentMaxLen {
annotatedLines = append(
annotatedLines,
CreateAnnotation(length),
Expand Down Expand Up @@ -282,7 +319,7 @@ func (s *Shortener) shortenCommentsFunc(contents []byte) []byte {
for _, line := range lines {
if s.isComment(line) && !IsAnnotation(line) &&
!s.isGoDirective(line) &&
s.lineLen(line) > s.config.MaxLen {
s.lineLen(line) > s.config.CurrentMaxLen {
start := strings.Index(line, "//")
prefix = line[0:(start + 2)]
trimmedLine := strings.Trim(line[(start+2):], " ")
Expand All @@ -292,7 +329,7 @@ func (s *Shortener) shortenCommentsFunc(contents []byte) []byte {
// Reflow the accumulated `words` before appending the unprocessed `line`.
currLineLen := 0
currLineWords := []string{}
maxCommentLen := s.config.MaxLen - s.lineLen(prefix)
maxCommentLen := s.config.CurrentMaxLen - s.lineLen(prefix)
for _, word := range words {
if currLineLen > 0 && currLineLen+1+len(word) > maxCommentLen {
cleanedLines = append(
Expand Down
Loading