Skip to content

Commit

Permalink
Merge branch 'max-line-length' into 'master'
Browse files Browse the repository at this point in the history
Max line length support

See merge request greut/eclint!6
  • Loading branch information
greut committed Nov 16, 2019
2 parents ddcae23 + ad72c46 commit 419d16a
Show file tree
Hide file tree
Showing 8 changed files with 224 additions and 50 deletions.
7 changes: 7 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ indent_style = space
indent_size = 4
insert_final_newline = true
trim_trailing_whitespace = true
max_line_length = 120

[*.md]
max_line_length = off

[{LICENSE,go.*}]
max_line_length = unset

[Dockerfile]
indent_size = 4
Expand Down
1 change: 1 addition & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,5 @@ linters:
- structcheck
- unused
- varcheck
- gosec
disable-all: true
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,14 @@ $ eclint -exclude "testdata/**/*"
- `indent_size`
- `indent_style`
- `insert_final_newline`
- `max_line_length` (when using tabs, specify the `tab_width` or `indent_size`)
- `trim_trailing_whitespace`
- [domain-specific properties](https://github.com/editorconfig/editorconfig/wiki/EditorConfig-Properties#ideas-for-domain-specific-properties)
- [domain-specific properties][dsl]
- `line_comment`
- `block_comment_start`, `block_comment`, `block_comment_end`

### More

- when not path is given, it searches for files via `git ls-files`
- `-exclude` to filter out some files
- unset / alter properties via the `eclint_` prefix
Expand Down Expand Up @@ -69,3 +73,5 @@ The methodology is to run the linter against some big repositories `time $(eclin
- [golangci-lint](https://github.com/golangci/golangci-lint), Go linters
- [goreleaser](https://goreleaser.com/)
- [klogr](https://github.com/kubernetes/klog/tree/master/klogr)

[dsl]: https://github.com/editorconfig/editorconfig/wiki/EditorConfig-Properties#ideas-for-domain-specific-properties
70 changes: 52 additions & 18 deletions lint.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ import (
"github.com/go-logr/logr"
)

// DefaultTabWidth sets the width of a tab used when counting the line length
const DefaultTabWidth = 8

// validate is where the validations rules are applied
func validate(r io.Reader, log logr.Logger, def *editorconfig.Definition) []error { //nolint:gocyclo
var buf *bytes.Buffer
Expand All @@ -21,6 +24,7 @@ func validate(r io.Reader, log logr.Logger, def *editorconfig.Definition) []erro
indentSize, _ := strconv.Atoi(def.IndentSize)

var lastLine []byte
var lastIndex int

var insideBlockComment bool
var blockCommentStart []byte
Expand All @@ -43,6 +47,19 @@ func validate(r io.Reader, log logr.Logger, def *editorconfig.Definition) []erro
}
}

maxLength := 0
tabWidth := def.TabWidth
if mll, ok := def.Raw["max_line_length"]; ok && mll != "off" && mll != "unset" {
ml, err := strconv.Atoi(mll)
if err != nil || ml < 0 {
return []error{fmt.Errorf("max_line_length expected a non-negative number, got %s", mll)}
}
maxLength = ml
if tabWidth <= 0 {
tabWidth = DefaultTabWidth
}
}

errs := readLines(r, func(index int, data []byte) error {
var err error

Expand All @@ -64,23 +81,25 @@ func validate(r io.Reader, log logr.Logger, def *editorconfig.Definition) []erro
// XXX not so nice hack
if ve, ok := err.(validationError); ok {
ve.line = lastLine
ve.index = index - 1
ve.index = lastIndex

lastLine = data
lastIndex = index

return ve
}
}

lastLine = data
lastIndex = index

if buf != nil && buf.Len() < bufSize {
if _, err := buf.Write(data); err != nil {
log.Error(err, "cannot write into file buffer", "line", index)
}
}

if err == nil && def.IndentStyle != "" && def.IndentStyle != "unset" {
if def.IndentStyle != "" && def.IndentStyle != "unset" {
if insideBlockComment && blockCommentEnd != nil {
insideBlockComment = !isBlockCommentEnd(blockCommentEnd, data)
}
Expand All @@ -102,43 +121,58 @@ func validate(r io.Reader, log logr.Logger, def *editorconfig.Definition) []erro
err = trimTrailingWhitespace(data)
}

if err == nil && maxLength > 0 && tabWidth > 0 {
err = maxLineLength(maxLength, tabWidth, data)
}

// Enrich the error with the line number
if err != nil {
if ve, ok := err.(validationError); ok {
ve.line = data
ve.index = index
return ve
}
return err
if ve, ok := err.(validationError); ok {
ve.line = data
ve.index = index
return ve
}

return nil
return err
})

if buf != nil && buf.Len() > 0 {
err := charset(def.Charset, buf.Bytes())
errs = append(errs, err)
if err != nil {
errs = append(errs, err)
}
}

if lastLine != nil && def.InsertFinalNewline != nil {
var err error
var lastChar byte
if len(lastLine) > 0 {
lastChar = lastLine[len(lastLine)-1]
}

if lastChar != 0x0 && lastChar != '\r' && lastChar != '\n' {
if lastChar != 0x0 && lastChar != cr && lastChar != lf {
if *def.InsertFinalNewline {
err := fmt.Errorf("missing the final newline")
errs = append(errs, err)
err = fmt.Errorf("missing the final newline")
}
} else {
if def.EndOfLine != "" {
err := endOfLine(def.EndOfLine, lastLine)
errs = append(errs, err)
err = endOfLine(def.EndOfLine, lastLine)
}

if err != nil {
if !*def.InsertFinalNewline {
err = fmt.Errorf("found an extraneous final newline")
} else {
err = nil
}
}
}

if !*def.InsertFinalNewline {
err := fmt.Errorf("found an extraneous final newline")
if err != nil {
if ve, ok := err.(validationError); ok {
ve.line = lastLine
ve.index = lastIndex
errs = append(errs, ve)
} else {
errs = append(errs, err)
}
}
Expand Down
23 changes: 17 additions & 6 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func walk(paths ...string) ([]string, error) {
}
mode := i.Mode()
if mode.IsRegular() && !mode.IsDir() {
log.V(4).Info("index %s\n", p)
log.V(4).Info("index %s", p)
files = append(files, p)
}
return nil
Expand Down Expand Up @@ -88,7 +88,12 @@ func main() {
flag.BoolVar(&flagVersion, "version", false, "print the version number")
flag.BoolVar(&noColors, "no_colors", false, "enable or disable colors")
flag.BoolVar(&summary, "summary", false, "enable the summary view")
flag.BoolVar(&showAllErrors, "show_all_errors", false, fmt.Sprintf("display all errors for each file (otherwise %d are kept)", showErrorQuantity))
flag.BoolVar(
&showAllErrors,
"show_all_errors",
false,
fmt.Sprintf("display all errors for each file (otherwise %d are kept)", showErrorQuantity),
)
flag.StringVar(&exclude, "exclude", "", "paths to exclude")
flag.Parse()

Expand Down Expand Up @@ -138,7 +143,9 @@ func main() {
if ve, ok := err.(validationError); ok {
log.V(4).Info("lint error", "error", ve)
if !summary {
fmt.Fprintf(stdout, "%s:%s: %s\n", au.Green(strconv.Itoa(ve.index)), au.Green(strconv.Itoa(ve.position)), ve.error)
vi := au.Green(strconv.Itoa(ve.index))
vp := au.Green(strconv.Itoa(ve.position))
fmt.Fprintf(stdout, "%s:%s: %s\n", vi, vp, ve.error)
l, err := errorAt(au, ve.line, ve.position-1)
if err != nil {
log.Error(err, "line formating failure", "error", ve)
Expand All @@ -152,7 +159,10 @@ func main() {
}

if d >= showErrorQuantity && len(errs) > d {
fmt.Fprintln(stdout, fmt.Sprintf(" ... skipping at most %s errors", au.BrightRed(strconv.Itoa(len(errs)-d))))
fmt.Fprintln(
stdout,
fmt.Sprintf(" ... skipping at most %s errors", au.BrightRed(strconv.Itoa(len(errs)-d))),
)
break
}

Expand Down Expand Up @@ -182,13 +192,14 @@ func errorAt(au aurora.Aurora, line []byte, position int) (string, error) {
}

for i := 0; i < position; i++ {
if line[i] != '\r' && line[i] != '\n' {
if line[i] != cr && line[i] != lf {
if err := b.WriteByte(line[i]); err != nil {
return "", err
}
}
}

// XXX this will break every non latin1 line.
s := " "
if position < len(line)-1 {
s = string(line[position : position+1])
Expand All @@ -198,7 +209,7 @@ func errorAt(au aurora.Aurora, line []byte, position int) (string, error) {
}

for i := position + 1; i < len(line); i++ {
if line[i] != '\r' && line[i] != '\n' {
if line[i] != cr && line[i] != lf {
if err := b.WriteByte(line[i]); err != nil {
return "", err
}
Expand Down
14 changes: 9 additions & 5 deletions scanner.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ type lineFunc func(int, []byte) error
func splitLines(data []byte, atEOF bool) (int, []byte, error) {
i := 0
for i < len(data) {
if data[i] == '\r' {
if data[i] == cr {
i++
if i < len(data) && data[i] == '\n' {
if i < len(data) && data[i] == lf {
i++
}
return i, data[0:i], nil
} else if data[i] == '\n' {
} else if data[i] == lf {
i++
return i, data[0:i], nil
}
Expand All @@ -40,15 +40,19 @@ func splitLines(data []byte, atEOF bool) (int, []byte, error) {

// readLines consumes the reader and emit each line via the lineFunc
//
// Line numbering starts at 1
// Line numbering starts at 1. Scanner is pretty smart an will reuse
// its memory structure. This is somehing we explicitly avoid by copying
// the content to a new slice.
func readLines(r io.Reader, fn lineFunc) []error {
errs := make([]error, 0)
sc := bufio.NewScanner(r)
sc.Split(splitLines)

i := 1
for sc.Scan() {
line := sc.Bytes()
l := sc.Bytes()
line := make([]byte, len(l))
copy(line, l)
if err := fn(i, line); err != nil {
errs = append(errs, err)
}
Expand Down
Loading

0 comments on commit 419d16a

Please sign in to comment.