Skip to content

Commit

Permalink
First try at inclusive linter
Browse files Browse the repository at this point in the history
  • Loading branch information
caitlinelfring committed Aug 21, 2020
1 parent efb83f5 commit 0c4e30c
Show file tree
Hide file tree
Showing 8 changed files with 180 additions and 0 deletions.
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1 +1,15 @@
# woke

`woke` is an inclusive language linter.

It is still very much WIP. More to come...

## For testing the linter

Until I can write proper tests...

Words to avoid:
* Blacklist
* White-list
* whitelist
* blacklist
8 changes: 8 additions & 0 deletions default.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
rules:
- word: whitelist
regexp: \b(white-?list)\b
alternatives: allowlist

- word: blacklist
regexp: \b(black-?list)\b
alternatives: denylist,blocklist
5 changes: 5 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module github.com/caitlinelfring/woke

go 1.14

require gopkg.in/yaml.v2 v2.3.0
3 changes: 3 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
18 changes: 18 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package main

import (
"fmt"

"github.com/caitlinelfring/woke/pkg/config"
)

func main() {
c, _ := config.NewConfig("default.yaml")
results, err := c.Parse("README.md")
if err != nil {
panic(err)
}
for _, result := range results {
fmt.Println(result)
}
}
73 changes: 73 additions & 0 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package config

import (
"bufio"
"go/token"
"io/ioutil"
"os"

"github.com/caitlinelfring/woke/pkg/rule"
"gopkg.in/yaml.v2"
)

// Config contains a list of rules
type Config struct {
Rules []*rule.Rule `yaml:"rules"`
}

// NewConfig returns a config from the provided yaml file containing rules
func NewConfig(filename string) (*Config, error) {
var c Config
err := c.load(filename)
return &c, err
}

func (c *Config) load(filename string) error {
yamlFile, err := ioutil.ReadFile(filename)
if err != nil {
return err
}
return yaml.Unmarshal(yamlFile, c)
}

// Parse reads the file and returns results of places where rules are broken
func (c *Config) Parse(filename string) ([]*rule.Result, error) {
f, err := os.Open(filename)
if err != nil {
return nil, err
}
defer f.Close()

var results []*rule.Result

// Splits on newlines by default.
scanner := bufio.NewScanner(f)
scanner.Split(bufio.ScanLines)
line := 1
// https://golang.org/pkg/bufio/#Scanner.Scan
for scanner.Scan() {
text := scanner.Text()
for _, r := range c.Rules {
idx := r.Regexp.FindAllStringIndex(text, -1)
if idx == nil {
continue
}

for _, i := range idx {
result := rule.Result{
Rule: r,
Match: text[i[0]:i[1]],
Position: &token.Position{
Filename: filename,
Line: line,
Column: i[0],
},
}
results = append(results, &result)
}
}

line++
}
return results, scanner.Err()
}
21 changes: 21 additions & 0 deletions pkg/rule/result.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package rule

import (
"fmt"
"go/token"
)

type Result struct {
Rule *Rule
Match string
Position *token.Position
}

// Reason outputs the suggested alternatives for this rule
func (r *Result) Reason() string {
return fmt.Sprintf("Instead of '%s', consider the following alternative(s): '%s'", r.Match, r.Rule.Alternatives)
}

func (r *Result) String() string {
return fmt.Sprintf("[%s] %s", r.Position.String(), r.Reason())
}
38 changes: 38 additions & 0 deletions pkg/rule/rule.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package rule

import (
"fmt"
"regexp"

"gopkg.in/yaml.v2"
)

// Rule is a linter rule
type Rule struct {
Word string // `yaml:"word"`
Regexp *regexp.Regexp // `yaml:"regexp"`
Alternatives string // `yaml:"alternatives"`
}

func (r *Rule) String() string {
return r.Word
}

// compile-time check that Rule satisfies the yaml Unmarshaler
var _ yaml.Unmarshaler = (*Rule)(nil)

// UnmarshalYAML to enforce regexp at the unmarshal level
func (r *Rule) UnmarshalYAML(unmarshal func(interface{}) error) error {
a := make(map[string]string)
if err := unmarshal(a); err != nil {
return err
}
r.Alternatives = a["alternatives"]
r.Word = a["word"]
if re, ok := a["regexp"]; !ok {
r.Regexp = regexp.MustCompile(fmt.Sprintf(`(?i)\b(%s)\b`, r.Word))
} else {
r.Regexp = regexp.MustCompile(fmt.Sprintf(`(?i)%s`, re))
}
return nil
}

0 comments on commit 0c4e30c

Please sign in to comment.