Skip to content

Commit

Permalink
Initial boilerplate for rewrite
Browse files Browse the repository at this point in the history
  • Loading branch information
bombsimon committed Nov 4, 2023
1 parent 4740984 commit e5501b7
Show file tree
Hide file tree
Showing 47 changed files with 150 additions and 5,713 deletions.
2 changes: 1 addition & 1 deletion LICENSE
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2018 Simon Sawert
Copyright (c) 2018 - 2023 Simon Sawert

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
85 changes: 21 additions & 64 deletions README.md
@@ -1,35 +1,21 @@
# wsl - Whitespace Linter

[![forthebadge](https://forthebadge.com/images/badges/made-with-go.svg)](https://forthebadge.com)
[![forthebadge](https://forthebadge.com/images/badges/built-with-love.svg)](https://forthebadge.com)

[![GitHub Actions](https://github.com/bombsimon/wsl/actions/workflows/go.yml/badge.svg)](https://github.com/bombsimon/wsl/actions/workflows/go.yml)
[![Coverage Status](https://coveralls.io/repos/github/bombsimon/wsl/badge.svg?branch=master)](https://coveralls.io/github/bombsimon/wsl?branch=master)

`wsl` is a linter that enforces a very **non scientific** vision of how to make
code more readable by enforcing empty lines at the right places.

**This linter is aggressive** and a lot of projects I've tested it on have
failed miserably. For this linter to be useful at all I want to be open to new
ideas, configurations and discussions! Also note that some of the warnings might
be bugs or unintentional false positives so I would love an
[issue](https://github.com/bombsimon/wsl/issues/new) to fix, discuss, change or
make something configurable!

## Installation

```sh
go install github.com/bombsimon/wsl/v4/cmd...@master
```
`wsl` (**w**hite**s**pace **l**inter) is a linter that wants you to use empty
lines to separate grouping of different types to increase readability. There are
also a few places where it encourages you to _remove_ whitespaces which is at
the start and the end of blocks.

## Usage

> **Note**: This linter provides a fixer that can fix most issues with the
`--fix` flag. However, currently `golangci-lint` [does not support suggested
fixes](https://github.com/golangci/golangci-lint/issues/1779) so the `--fix`
flag in `golangci-lint` will **not** work.
> `--fix` flag. However, currently `golangci-lint` [does not support suggested
> fixes][suggested-fixes-issue] so the `--fix`
> flag in `golangci-lint` will **not** work.
`wsl` uses the [analysis](https://pkg.go.dev/golang.org/x/tools/go/analysis)
`wsl` uses the [analysis]
package meaning it will operate on package level with the default analysis flags
and way of working.

Expand All @@ -40,55 +26,26 @@ wsl [flags] </path/to/package/...>
wsl --allow-cuddle-declarations --fix ./...
```

`wsl` is also integrated in [`golangci-lint`](https://golangci-lint.run)
`wsl` is also integrated in [`golangci-lint`][golangci-lint]

```sh
golangci-lint run --no-config --disable-all --enable wsl
```

## Issues and configuration

The linter suppers a few ways to configure it to satisfy more than one kind of
code style. These settings could be set either with flags or with YAML
configuration if used via `golangci-lint`.

The supported configuration can be found [in the
documentation](doc/configuration.md).
TODO

Below are the available checklist for any hit from `wsl`. If you do not see any,
feel free to raise an [issue](https://github.com/bombsimon/wsl/issues/new).
## See also

> **Note**: this linter doesn't take in consideration the issues that will be
> fixed with `go fmt -s` so ensure that the code is properly formatted before
> use.
- [`nlterutn`][nlreturn] - Use empty lines before `return`
- [`whitespace`][whitespace] - Don't use a blank newline at the start or end of
a block.
- [`gofumpt`][gofumpt] - Stricter formatter than `gofmt`.

* [Anonymous switch statements should never be cuddled](doc/rules.md#anonymous-switch-statements-should-never-be-cuddled)
* [Append only allowed to cuddle with appended value](doc/rules.md#append-only-allowed-to-cuddle-with-appended-value)
* [Assignments should only be cuddled with other assignments](doc/rules.md#assignments-should-only-be-cuddled-with-other-assignments)
* [Block should not end with a whitespace (or comment)](doc/rules.md#block-should-not-end-with-a-whitespace-or-comment)
* [Block should not start with a whitespace](doc/rules.md#block-should-not-start-with-a-whitespace)
* [Case block should end with newline at this size](doc/rules.md#case-block-should-end-with-newline-at-this-size)
* [Branch statements should not be cuddled if block has more than two lines](doc/rules.md#branch-statements-should-not-be-cuddled-if-block-has-more-than-two-lines)
* [Declarations should never be cuddled](doc/rules.md#declarations-should-never-be-cuddled)
* [Defer statements should only be cuddled with expressions on same variable](doc/rules.md#defer-statements-should-only-be-cuddled-with-expressions-on-same-variable)
* [Expressions should not be cuddled with blocks](doc/rules.md#expressions-should-not-be-cuddled-with-blocks)
* [Expressions should not be cuddled with declarations or returns](doc/rules.md#expressions-should-not-be-cuddled-with-declarations-or-returns)
* [For statement without condition should never be cuddled](doc/rules.md#for-statement-without-condition-should-never-be-cuddled)
* [For statements should only be cuddled with assignments used in the iteration](doc/rules.md#for-statements-should-only-be-cuddled-with-assignments-used-in-the-iteration)
* [Go statements can only invoke functions assigned on line above](doc/rules.md#go-statements-can-only-invoke-functions-assigned-on-line-above)
* [If statements should only be cuddled with assignments](doc/rules.md#if-statements-should-only-be-cuddled-with-assignments)
* [If statements should only be cuddled with assignments used in the if statement itself](doc/rules.md#if-statements-should-only-be-cuddled-with-assignments-used-in-the-if-statement-itself)
* [If statements that check an error must be cuddled with the statement that assigned the error](doc/rules.md#if-statements-that-check-an-error-must-be-cuddled-with-the-statement-that-assigned-the-error)
* [Only cuddled expressions if assigning variable or using from line above](doc/rules.md#only-cuddled-expressions-if-assigning-variable-or-using-from-line-above)
* [Only one cuddle assignment allowed before defer statement](doc/rules.md#only-one-cuddle-assignment-allowed-before-defer-statement)
* [Only one cuddle assignment allowed before for statement](doc/rules.md#only-one-cuddle-assignment-allowed-before-for-statement)
* [Only one cuddle assignment allowed before go statement](doc/rules.md#only-one-cuddle-assignment-allowed-before-go-statement)
* [Only one cuddle assignment allowed before if statement](doc/rules.md#only-one-cuddle-assignment-allowed-before-if-statement)
* [Only one cuddle assignment allowed before range statement](doc/rules.md#only-one-cuddle-assignment-allowed-before-range-statement)
* [Only one cuddle assignment allowed before switch statement](doc/rules.md#only-one-cuddle-assignment-allowed-before-switch-statement)
* [Only one cuddle assignment allowed before type switch statement](doc/rules.md#only-one-cuddle-assignment-allowed-before-type-switch-statement)
* [Ranges should only be cuddled with assignments used in the iteration](doc/rules.md#ranges-should-only-be-cuddled-with-assignments-used-in-the-iteration)
* [Return statements should not be cuddled if block has more than two lines](doc/rules.md#return-statements-should-not-be-cuddled-if-block-has-more-than-two-lines)
* [Short declarations should cuddle only with other short declarations](doc/rules.md#short-declaration-should-cuddle-only-with-other-short-declarations)
* [Switch statements should only be cuddled with variables switched](doc/rules.md#switch-statements-should-only-be-cuddled-with-variables-switched)
* [Type switch statements should only be cuddled with variables switched](doc/rules.md#type-switch-statements-should-only-be-cuddled-with-variables-switched)
[analysis]: https://pkg.go.dev/golang.org/x/tools/go/analysis
[gofumpt]: https://github.com/mvdan/gofumpt
[golangci-lint]: https://golangci-lint.run
[nlreturn]: https://github.com/ssgreg/nlreturn
[suggested-fixes-issue]: https://github.com/golangci/golangci-lint/issues/1779
[whitespace]: https://github.com/ultraware/whitespace
86 changes: 8 additions & 78 deletions analyzer.go
Expand Up @@ -19,24 +19,6 @@ func NewAnalyzer(config *Configuration) *analysis.Analyzer {
}
}

func defaultConfig() *Configuration {
return &Configuration{
AllowAssignAndAnythingCuddle: false,
AllowAssignAndCallCuddle: true,
AllowCuddleDeclaration: false,
AllowMultiLineAssignCuddle: true,
AllowSeparatedLeadingComment: false,
AllowTrailingComment: false,
ForceCuddleErrCheckAndAssign: false,
ForceExclusiveShortDeclarations: false,
StrictAppend: true,
AllowCuddleWithCalls: []string{"Lock", "RLock"},
AllowCuddleWithRHS: []string{"Unlock", "RUnlock"},
ErrorVariableNames: []string{"err"},
ForceCaseTrailingWhitespaceLimit: 0,
}
}

// wslAnalyzer is a wrapper around the configuration which is used to be able to
// set the configuration when creating the analyzer and later be able to update
// flags and running method.
Expand All @@ -47,29 +29,6 @@ type wslAnalyzer struct {
func (wa *wslAnalyzer) flags() flag.FlagSet {
flags := flag.NewFlagSet("", flag.ExitOnError)

// If we have a configuration set we're not running from the command line so
// we don't use any flags.
if wa.config != nil {
return *flags
}

wa.config = defaultConfig()

flags.BoolVar(&wa.config.AllowAssignAndAnythingCuddle, "allow-assign-and-anything", false, "Allow assignments and anything to be cuddled")
flags.BoolVar(&wa.config.AllowAssignAndCallCuddle, "allow-assign-and-call", true, "Allow assignments and calls to be cuddled (if using same variable/type)")
flags.BoolVar(&wa.config.AllowCuddleDeclaration, "allow-cuddle-declarations", false, "Allow declarations to be cuddled")
flags.BoolVar(&wa.config.AllowMultiLineAssignCuddle, "allow-multi-line-assign", true, "Allow cuddling with multi line assignments")
flags.BoolVar(&wa.config.AllowSeparatedLeadingComment, "allow-separated-leading-comment", false, "Allow empty newlines in leading comments")
flags.BoolVar(&wa.config.AllowTrailingComment, "allow-trailing-comment", false, "Allow blocks to end with a comment")
flags.BoolVar(&wa.config.ForceCuddleErrCheckAndAssign, "force-err-cuddling", false, "Force cuddling of error checks with error var assignment")
flags.BoolVar(&wa.config.ForceExclusiveShortDeclarations, "force-short-decl-cuddling", false, "Force short declarations to cuddle by themselves")
flags.BoolVar(&wa.config.StrictAppend, "strict-append", true, "Strict rules for append")
flags.IntVar(&wa.config.ForceCaseTrailingWhitespaceLimit, "force-case-trailing-whitespace", 0, "Force newlines for case blocks > this number.")

flags.Var(&multiStringValue{slicePtr: &wa.config.AllowCuddleWithCalls}, "allow-cuddle-with-calls", "Comma separated list of idents that can have cuddles after")
flags.Var(&multiStringValue{slicePtr: &wa.config.AllowCuddleWithRHS}, "allow-cuddle-with-rhs", "Comma separated list of idents that can have cuddles before")
flags.Var(&multiStringValue{slicePtr: &wa.config.ErrorVariableNames}, "error-variable-names", "Comma separated list of error variable names")

return *flags
}

Expand All @@ -80,23 +39,24 @@ func (wa *wslAnalyzer) run(pass *analysis.Pass) (interface{}, error) {
continue
}

processor := newProcessorWithConfig(file, pass.Fset, wa.config)
processor.parseAST()
wsl := New(file, pass.Fset, wa.config)
wsl.Run()

for pos, fix := range processor.result {
for pos, fix := range wsl.Issues {
textEdits := []analysis.TextEdit{}
for _, f := range fix.fixRanges {

for _, f := range fix.FixRanges {
textEdits = append(textEdits, analysis.TextEdit{
Pos: f.fixRangeStart,
End: f.fixRangeEnd,
Pos: f.FixRangeStart,
End: f.FixRangeEnd,
NewText: []byte("\n"),
})
}

pass.Report(analysis.Diagnostic{
Pos: pos,
Category: "whitespace",
Message: fix.reason,
Message: fix.Message,
SuggestedFixes: []analysis.SuggestedFix{
{
TextEdits: textEdits,
Expand All @@ -109,33 +69,3 @@ func (wa *wslAnalyzer) run(pass *analysis.Pass) (interface{}, error) {
//nolint:nilnil // A pass don't need to return anything.
return nil, nil
}

// multiStringValue is a flag that supports multiple values. It's implemented to
// contain a pointer to a string slice that will be overwritten when the flag's
// `Set` method is called.
type multiStringValue struct {
slicePtr *[]string
}

// Set implements the flag.Value interface and will overwrite the pointer to the
// slice with a new pointer after splitting the flag by comma.
func (m *multiStringValue) Set(value string) error {
s := []string{}

for _, v := range strings.Split(value, ",") {
s = append(s, strings.TrimSpace(v))
}

*m.slicePtr = s

return nil
}

// Set implements the flag.Value interface.
func (m *multiStringValue) String() string {
if m.slicePtr == nil {
return ""
}

return strings.Join(*m.slicePtr, ", ")
}

0 comments on commit e5501b7

Please sign in to comment.