Skip to content

Commit

Permalink
refactor: more API cleaning
Browse files Browse the repository at this point in the history
This package is still somewhat of a mess, but we're getting closer to a
presentable API.
  • Loading branch information
jdkato committed Oct 4, 2020
1 parent 4c8e5f9 commit 57b8c7b
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 106 deletions.
23 changes: 11 additions & 12 deletions action/action.go
Expand Up @@ -50,14 +50,14 @@ func CompileRule(config *config.Config, path string) error {
} else {
fName := filepath.Base(path)

mgr := check.Manager{
AllChecks: make(map[string]check.Rule),
Config: config,
Scopes: make(map[string]struct{}),
// Create our check manager:
mgr, err := check.NewManager(config)
if err != nil {
return err
}

if core.CheckError(mgr.LoadCheck(fName, path), true) {
for _, v := range mgr.AllChecks {
if core.CheckError(mgr.AddRuleFromSource(fName, path), true) {
for _, v := range mgr.Rules() {
fmt.Print(v.Pattern())
}
}
Expand All @@ -79,17 +79,16 @@ func TestRule(args []string) error {
cfg.InExt = ".txt" // default value

// Create our check manager:
mgr := check.Manager{
AllChecks: make(map[string]check.Rule),
Config: cfg,
Scopes: make(map[string]struct{}),
mgr, err := check.NewManager(cfg)
if err != nil {
return err
}

err = mgr.LoadCheck(args[0], "Test.Rule")
err = mgr.AddRuleFromSource(args[0], "Test.Rule")
if err != nil {
return err
}
linter := lint.Linter{CheckManager: &mgr}
linter := lint.Linter{CheckManager: mgr}

linted, err := linter.Lint([]string{args[1]}, "*")
if err != nil {
Expand Down
47 changes: 23 additions & 24 deletions check/definition.go
Expand Up @@ -11,6 +11,28 @@ import (
"gopkg.in/yaml.v2"
)

// Rule represents in individual writing construct to enforce.
type Rule interface {
Run(text string, file *core.File) []core.Alert
Fields() Definition
Pattern() string
}

// Definition holds the common attributes of rule definitions.
type Definition struct {
Action core.Action
Code bool
Description string
Extends string
Level string
Limit int
Link string
Message string
Name string
Scope string
Selector core.Selector
}

var defaultStyles = []string{"Vale"}
var extensionPoints = []string{
"capitalization",
Expand Down Expand Up @@ -56,30 +78,7 @@ var defaultRules = map[string]map[string]interface{}{

type baseCheck map[string]interface{}

// Rule represents in individual writing construct to enforce.
type Rule interface {
Run(text string, file *core.File) []core.Alert
Fields() Definition
Pattern() string
}

// Definition holds the common attributes of rule definitions.
type Definition struct {
Action core.Action
Code bool
Description string
Extends string
Level string
Limit int
Link string
Message string
Name string
Scope string
Selector core.Selector
}

// BuildRule creates a Rule from a `baseCheck`.
func BuildRule(cfg *config.Config, generic baseCheck) (Rule, error) {
func buildRule(cfg *config.Config, generic baseCheck) (Rule, error) {
name := generic["extends"].(string)

switch name {
Expand Down
4 changes: 2 additions & 2 deletions check/existence.go
Expand Up @@ -17,7 +17,7 @@ type Existence struct {
// defined.
Append bool
// `ignorecase` (`bool`): Makes all matches case-insensitive.
Ignorecase bool
IgnoreCase bool
// `nonword` (`bool`): Removes the default word boundaries (`\b`).
Nonword bool
// `raw` (`array`): A list of tokens to be concatenated into a pattern.
Expand Down Expand Up @@ -46,7 +46,7 @@ func NewExistence(cfg *config.Config, generic baseCheck) (Existence, error) {

regex := makeRegexp(
cfg.WordTemplate,
rule.Ignorecase,
rule.IgnoreCase,
func() bool { return !rule.Nonword && len(rule.Tokens) > 0 },
func() string { return strings.Join(rule.Raw, "") },
rule.Append)
Expand Down
139 changes: 76 additions & 63 deletions check/manager.go
Expand Up @@ -20,9 +20,11 @@ const (

// Manager controls the loading and validating of the check extension points.
type Manager struct {
AllChecks map[string]Rule
Config *config.Config
Scopes map[string]struct{}
Config *config.Config

scopes map[string]struct{}
rules map[string]Rule
styles []string
}

// NewManager creates a new Manager and loads the rule definitions (that is,
Expand All @@ -31,34 +33,23 @@ func NewManager(config *config.Config) (*Manager, error) {
var path string

mgr := Manager{
AllChecks: make(map[string]Rule),
Config: config,
Scopes: make(map[string]struct{}),
Config: config,

rules: make(map[string]Rule),
scopes: make(map[string]struct{}),
}

// loadedStyles keeps track of the styles we've loaded as we go.
loadedStyles := []string{}
err := mgr.loadDefaultRules()
if mgr.Config.StylesPath == "" {
// If we're not given a StylesPath, there's nothing left to look for.
err := mgr.loadDefaultRules(loadedStyles)
return &mgr, err
}

// Load our global `BasedOnStyles` ...
loaded, err := mgr.loadStyles(mgr.Config.GBaseStyles, loadedStyles)
// Load our styles ...
err = mgr.loadStyles(mgr.Config.Styles)
if err != nil {
return &mgr, err
}
loadedStyles = append(loadedStyles, loaded...)

// Load our section-specific `BasedOnStyles` ...
for _, styles := range mgr.Config.SBaseStyles {
loaded, err := mgr.loadStyles(styles, loadedStyles)
if err != nil {
return &mgr, err
}
loadedStyles = append(loadedStyles, loaded...)
}

for _, chk := range mgr.Config.Checks {
// Load any remaining individual rules.
Expand All @@ -67,42 +58,69 @@ func NewManager(config *config.Config) (*Manager, error) {
continue
}
parts := strings.Split(chk, ".")
if !core.StringInSlice(parts[0], loadedStyles) && !core.StringInSlice(parts[0], defaultStyles) {
if !mgr.hasStyle(parts[0]) {
// If this rule isn't part of an already-loaded style, we load it
// individually.
fName := parts[1] + ".yml"
path = filepath.Join(mgr.Config.StylesPath, parts[0], fName)
if err = mgr.LoadCheck(fName, path); err != nil {
if err = mgr.AddRuleFromSource(fName, path); err != nil {
return &mgr, err
}
}
}

// Finally, after reading the user's `StylesPath`, we load our built-in
// styles:
err = mgr.loadDefaultRules(loadedStyles)
return &mgr, err
}

// LoadCheck adds the given external check to the manager.
func (mgr *Manager) LoadCheck(fName string, fp string) error {
if strings.HasSuffix(fName, ".yml") {
f, err := mgr.Config.FsWrapper.ReadFile(fp)
// AddRule adds the given rule to the manager.
func (mgr *Manager) AddRule(name string, rule Rule) error {
if _, found := mgr.rules[name]; !found {
mgr.rules[name] = rule
return nil
}
return fmt.Errorf("the rule '%s' has already been added", name)
}

// AddRuleFromSource adds the given rule to the manager.
func (mgr *Manager) AddRuleFromSource(name, path string) error {
if strings.HasSuffix(name, ".yml") {
f, err := mgr.Config.FsWrapper.ReadFile(path)
if err != nil {
return fmt.Errorf("failed to load rule '%s'.\n\n%v", fName, err)
return fmt.Errorf("failed to load rule '%s'.\n\n%v", name, err)
}

style := filepath.Base(filepath.Dir(fp))
chkName := style + "." + strings.Split(fName, ".")[0]
if _, ok := mgr.AllChecks[chkName]; !ok {
if err = mgr.addCheck(f, chkName, fp); err != nil {
style := filepath.Base(filepath.Dir(path))
chkName := style + "." + strings.Split(name, ".")[0]
if _, ok := mgr.rules[chkName]; !ok {
if err = mgr.addCheck(f, chkName, path); err != nil {
return err
}
}
}
return nil
}

// Rules are all of the Manager's compiled `Rule`s.
func (mgr *Manager) Rules() map[string]Rule {
return mgr.rules
}

// HasScope returns `true` if the manager has a rule that applies to `scope`.
func (mgr *Manager) HasScope(scope string) bool {
_, found := mgr.scopes[scope]
return found
}

func (mgr *Manager) addStyle(path string) error {
return mgr.Config.FsWrapper.Walk(path,
func(fp string, fi os.FileInfo, err error) error {
if err != nil || fi.IsDir() {
return err
}
return mgr.AddRuleFromSource(fi.Name(), fp)
})
}

func (mgr *Manager) addCheck(file []byte, chkName, path string) error {
// Load the rule definition.
generic, err := parse(file, path)
Expand All @@ -123,31 +141,20 @@ func (mgr *Manager) addCheck(file []byte, chkName, path string) error {
generic["scope"] = "text"
}

rule, err := BuildRule(mgr.Config, generic)
rule, err := buildRule(mgr.Config, generic)
if err != nil {
return err
}

base := strings.Split(generic["scope"].(string), ".")[0]
mgr.Scopes[base] = struct{}{}
mgr.scopes[base] = struct{}{}

mgr.AllChecks[chkName] = rule
return nil
return mgr.AddRule(chkName, rule)
}

func (mgr *Manager) loadExternalStyle(path string) error {
return mgr.Config.FsWrapper.Walk(path,
func(fp string, fi os.FileInfo, err error) error {
if err != nil || fi.IsDir() {
return err
}
return mgr.LoadCheck(fi.Name(), fp)
})
}

func (mgr *Manager) loadDefaultRules(loaded []string) error {
func (mgr *Manager) loadDefaultRules() error {
for _, style := range defaultStyles {
if core.StringInSlice(style, loaded) {
if core.StringInSlice(style, mgr.styles) {
// The user has a style on their `StylesPath` with the same
// name as a built-in style.
//
Expand Down Expand Up @@ -181,25 +188,26 @@ func (mgr *Manager) loadDefaultRules(loaded []string) error {
return nil
}

func (mgr *Manager) loadStyles(styles []string, loaded []string) ([]string, error) {
func (mgr *Manager) loadStyles(styles []string) error {
var found []string

baseDir := mgr.Config.StylesPath
for _, style := range styles {
p := filepath.Join(baseDir, style)
if core.StringInSlice(style, loaded) || core.StringInSlice(style, defaultStyles) {
if mgr.hasStyle(style) {
// We've already loaded this style.
continue
} else if has, _ := mgr.Config.FsWrapper.DirExists(p); !has {
return found, errors.New("missing style: '" + style + "'")
return errors.New("missing style: '" + style + "'")
}
if err := mgr.loadExternalStyle(p); err != nil {
return found, err
if err := mgr.addStyle(p); err != nil {
return err
}
found = append(found, style)
}

return found, nil
mgr.styles = append(mgr.styles, found...)
return nil
}

func (mgr *Manager) loadVocabRules() {
Expand All @@ -210,21 +218,26 @@ func (mgr *Manager) loadVocabRules() {
vocab["swap"].(map[string]string)[strings.ToLower(term)] = term
}
}
rule, _ := BuildRule(mgr.Config, vocab)
mgr.AllChecks["Vale.Terms"] = rule
rule, _ := buildRule(mgr.Config, vocab)
mgr.rules["Vale.Terms"] = rule
}

if len(mgr.Config.RejectedTokens) > 0 {
avoid := defaultRules["Avoid"]
for term := range mgr.Config.RejectedTokens {
avoid["tokens"] = append(avoid["tokens"].([]string), term)
}
rule, _ := BuildRule(mgr.Config, avoid)
mgr.AllChecks["Vale.Avoid"] = rule
rule, _ := buildRule(mgr.Config, avoid)
mgr.rules["Vale.Avoid"] = rule
}

if mgr.Config.LTPath != "" {
rule, _ := BuildRule(mgr.Config, defaultRules["Grammar"])
mgr.AllChecks["LanguageTool.Grammar"] = rule
rule, _ := buildRule(mgr.Config, defaultRules["Grammar"])
mgr.rules["LanguageTool.Grammar"] = rule
}
}

func (mgr *Manager) hasStyle(name string) bool {
styles := append(mgr.styles, defaultStyles...)
return core.StringInSlice(name, styles)
}
7 changes: 2 additions & 5 deletions lint/lint.go
Expand Up @@ -238,10 +238,7 @@ func (l *Linter) lintProse(f *core.File, ctx, txt, raw string, lnTotal, lnLength
text := core.PrepText(txt)
rawText := core.PrepText(raw)

_, hasP := l.CheckManager.Scopes["paragraph"]
_, hasS := l.CheckManager.Scopes["sentence"]

if hasP || hasS {
if l.CheckManager.HasScope("paragraph") || l.CheckManager.HasScope("sentence") {
senScope := "sentence" + f.RealExt
hasCtx := ctx != ""
for _, p := range strings.SplitAfter(text, "\n\n") {
Expand Down Expand Up @@ -287,7 +284,7 @@ func (l *Linter) lintText(f *core.File, blk core.Block, lines int, pad int) {
hasCode := core.StringInSlice(f.NormedExt, []string{".md", ".adoc", ".rst"})

results := make(chan core.Alert)
for name, chk := range l.CheckManager.AllChecks {
for name, chk := range l.CheckManager.Rules() {
if chk.Fields().Code && hasCode && !l.CheckManager.Config.Simple {
txt = blk.Raw
} else {
Expand Down

0 comments on commit 57b8c7b

Please sign in to comment.