diff --git a/cmd/vale/command.go b/cmd/vale/command.go index b61278c1..8f562076 100644 --- a/cmd/vale/command.go +++ b/cmd/vale/command.go @@ -77,7 +77,7 @@ func fix(args []string, flags *core.CLIFlags) error { return err } - resp, err := lint.ParseAlert(alert, cfg) + resp, err := check.ParseAlert(alert, cfg) if err != nil { return err } diff --git a/internal/lint/action.go b/internal/check/action.go similarity index 94% rename from internal/lint/action.go rename to internal/check/action.go index 4fb6ef21..47f63135 100644 --- a/internal/lint/action.go +++ b/internal/check/action.go @@ -1,4 +1,4 @@ -package lint +package check import ( "encoding/json" @@ -13,7 +13,6 @@ import ( "github.com/d5/tengo/v2/stdlib" "github.com/jdkato/twine/strcase" - "github.com/errata-ai/vale/v3/internal/check" "github.com/errata-ai/vale/v3/internal/core" ) @@ -43,7 +42,7 @@ func ParseAlert(s string, cfg *core.Config) (Solution, error) { return Solution{}, err } - suggestions, err := processAlert(body, cfg) + suggestions, err := FixAlert(body, cfg) if err != nil { resp.Error = err.Error() } @@ -52,7 +51,7 @@ func ParseAlert(s string, cfg *core.Config) (Solution, error) { return resp, nil } -func processAlert(alert core.Alert, cfg *core.Config) ([]string, error) { +func FixAlert(alert core.Alert, cfg *core.Config) ([]string, error) { action := alert.Action.Name if f, found := fixers[action]; found { return f(alert, cfg) @@ -118,7 +117,7 @@ func spelling(alert core.Alert, cfg *core.Config) ([]string, error) { name := strings.Split(alert.Check, ".") path := filepath.Join(cfg.StylesPath(), name[0], name[1]+".yml") - mgr, err := check.NewManager(cfg) + mgr, err := NewManager(cfg) if err != nil { return suggestions, err } @@ -130,7 +129,7 @@ func spelling(alert core.Alert, cfg *core.Config) ([]string, error) { } } - rule, ok := mgr.Rules()[alert.Check].(check.Spelling) + rule, ok := mgr.Rules()[alert.Check].(Spelling) if !ok { return suggestions, errors.New("unknown check") } diff --git a/internal/check/capitalization.go b/internal/check/capitalization.go index 819f41e9..df9f67dd 100644 --- a/internal/check/capitalization.go +++ b/internal/check/capitalization.go @@ -112,7 +112,7 @@ func NewCapitalization(cfg *core.Config, generic baseCheck, path string) (Capita } // Run checks the capitalization style of the provided text. -func (c Capitalization) Run(blk nlp.Block, _ *core.File) ([]core.Alert, error) { +func (c Capitalization) Run(blk nlp.Block, _ *core.File, cfg *core.Config) ([]core.Alert, error) { alerts := []core.Alert{} expected, matched := c.Check(blk.Text, c.exceptRe) @@ -126,7 +126,7 @@ func (c Capitalization) Run(blk nlp.Block, _ *core.File) ([]core.Alert, error) { } pos := []int{0, nlp.StrLen(blk.Text)} - a, err := makeAlert(c.Definition, pos, blk.Text) + a, err := makeAlert(c.Definition, pos, blk.Text, cfg) if err != nil { return alerts, err } diff --git a/internal/check/conditional.go b/internal/check/conditional.go index 981cd397..f435685b 100644 --- a/internal/check/conditional.go +++ b/internal/check/conditional.go @@ -58,7 +58,7 @@ func NewConditional(cfg *core.Config, generic baseCheck, path string) (Condition } // Run evaluates the given conditional statement. -func (c Conditional) Run(blk nlp.Block, f *core.File) ([]core.Alert, error) { +func (c Conditional) Run(blk nlp.Block, f *core.File, cfg *core.Config) ([]core.Alert, error) { alerts := []core.Alert{} txt := blk.Text @@ -94,7 +94,7 @@ func (c Conditional) Run(blk nlp.Block, f *core.File) ([]core.Alert, error) { if !core.StringInSlice(s, f.Sequences) && !isMatch(c.exceptRe, s) { // If we've found one (e.g., "WHO") and we haven't marked it as // being defined previously, send an Alert. - a, erra := makeAlert(c.Definition, loc, txt) + a, erra := makeAlert(c.Definition, loc, txt, cfg) if erra != nil { return alerts, erra } diff --git a/internal/check/consistency.go b/internal/check/consistency.go index 8f4d2a77..6fc0e162 100644 --- a/internal/check/consistency.go +++ b/internal/check/consistency.go @@ -78,7 +78,7 @@ func NewConsistency(cfg *core.Config, generic baseCheck, path string) (Consisten } // Run looks for inconsistent use of a user-defined regex. -func (o Consistency) Run(blk nlp.Block, f *core.File) ([]core.Alert, error) { +func (o Consistency) Run(blk nlp.Block, f *core.File, cfg *core.Config) ([]core.Alert, error) { alerts := []core.Alert{} loc := []int{} @@ -100,7 +100,7 @@ func (o Consistency) Run(blk nlp.Block, f *core.File) ([]core.Alert, error) { if matches != nil && core.AllStringsInSlice(s.subs, f.Sequences) { o.Name = o.Extends - a, err := makeAlert(o.Definition, loc, txt) + a, err := makeAlert(o.Definition, loc, txt, cfg) if err != nil { return alerts, err } diff --git a/internal/check/definition.go b/internal/check/definition.go index ec26de82..f7e2fefb 100755 --- a/internal/check/definition.go +++ b/internal/check/definition.go @@ -24,7 +24,7 @@ type FilterEnv struct { // Rule represents in individual writing construct to enforce. type Rule interface { - Run(blk nlp.Block, file *core.File) ([]core.Alert, error) + Run(blk nlp.Block, file *core.File, cfg *core.Config) ([]core.Alert, error) Fields() Definition Pattern() string } @@ -182,7 +182,7 @@ func re2Loc(s string, loc []int) (string, error) { return string(converted[loc[0]:loc[1]]), nil } -func makeAlert(chk Definition, loc []int, txt string) (core.Alert, error) { +func makeAlert(chk Definition, loc []int, txt string, cfg *core.Config) (core.Alert, error) { match, err := re2Loc(txt, loc) if err != nil { return core.Alert{}, err @@ -191,7 +191,24 @@ func makeAlert(chk Definition, loc []int, txt string) (core.Alert, error) { a := core.Alert{ Check: chk.Name, Severity: chk.Level, Span: loc, Link: chk.Link, Match: match, Action: chk.Action} - a.Message, a.Description = formatMessages(chk.Message, chk.Description, match) + + if chk.Action.Name != "" { + repl := match + + fixed, fixError := FixAlert(a, cfg) + if fixError != nil { + return core.Alert{}, fixError + } + + if len(fixed) == 1 { + repl = fixed[0] + } else if len(fixed) > 1 { + repl = core.ToSentence(fixed, "or") + } + a.Message, a.Description = formatMessages(chk.Message, chk.Description, match, repl) + } else { + a.Message, a.Description = formatMessages(chk.Message, chk.Description, match) + } return a, nil } diff --git a/internal/check/existence.go b/internal/check/existence.go index ff2ce671..0d4b29a1 100644 --- a/internal/check/existence.go +++ b/internal/check/existence.go @@ -74,7 +74,7 @@ func NewExistence(cfg *core.Config, generic baseCheck, path string) (Existence, // This is simplest of the available extension points: it looks for any matches // of its internal `pattern` (calculated from `NewExistence`) against the // provided text. -func (e Existence) Run(blk nlp.Block, _ *core.File) ([]core.Alert, error) { +func (e Existence) Run(blk nlp.Block, _ *core.File, cfg *core.Config) ([]core.Alert, error) { alerts := []core.Alert{} for _, loc := range e.pattern.FindAllStringIndex(blk.Text, -1) { @@ -85,7 +85,7 @@ func (e Existence) Run(blk nlp.Block, _ *core.File) ([]core.Alert, error) { observed := strings.TrimSpace(converted) if !isMatch(e.exceptRe, observed) { - a, erra := makeAlert(e.Definition, loc, blk.Text) + a, erra := makeAlert(e.Definition, loc, blk.Text, cfg) if erra != nil { return alerts, erra } diff --git a/internal/check/existence_test.go b/internal/check/existence_test.go index bb8af12c..243e1a33 100644 --- a/internal/check/existence_test.go +++ b/internal/check/existence_test.go @@ -39,7 +39,7 @@ func TestExistence(t *testing.T) { t.Fatal(err) } - alerts, _ := rule.Run(nlp.NewBlock("", "This is a test.", ""), file) + alerts, _ := rule.Run(nlp.NewBlock("", "This is a test.", ""), file, cfg) if len(alerts) != 1 { t.Errorf("expected one alert, not %v", alerts) } @@ -70,6 +70,6 @@ func FuzzExistence(f *testing.F) { f.Add("hello") f.Fuzz(func(t *testing.T, s string) { - _, _ = rule.Run(nlp.NewBlock("", s, ""), file) + _, _ = rule.Run(nlp.NewBlock("", s, ""), file, cfg) }) } diff --git a/internal/check/metric.go b/internal/check/metric.go index 2731b356..b66875f5 100644 --- a/internal/check/metric.go +++ b/internal/check/metric.go @@ -48,7 +48,7 @@ func NewMetric(_ *core.Config, generic baseCheck, path string) (Metric, error) { } // Run calculates the readability level of the given text. -func (o Metric) Run(_ nlp.Block, f *core.File) ([]core.Alert, error) { +func (o Metric) Run(_ nlp.Block, f *core.File, _ *core.Config) ([]core.Alert, error) { alerts := []core.Alert{} ctx := context.Background() diff --git a/internal/check/occurrence.go b/internal/check/occurrence.go index fd57cbcc..5128510d 100644 --- a/internal/check/occurrence.go +++ b/internal/check/occurrence.go @@ -51,7 +51,7 @@ func NewOccurrence(_ *core.Config, generic baseCheck, path string) (Occurrence, // Run checks the number of occurrences of a user-defined regex against a // certain threshold. -func (o Occurrence) Run(blk nlp.Block, _ *core.File) ([]core.Alert, error) { +func (o Occurrence) Run(blk nlp.Block, _ *core.File, cfg *core.Config) ([]core.Alert, error) { var a core.Alert var err error var alerts []core.Alert @@ -99,7 +99,7 @@ func (o Occurrence) Run(blk nlp.Block, _ *core.File) ([]core.Alert, error) { return alerts, nil } - a, err = makeAlert(o.Definition, span, txt) + a, err = makeAlert(o.Definition, span, txt, cfg) if err != nil { return alerts, err } diff --git a/internal/check/readability.go b/internal/check/readability.go index 4ce90446..c4659f37 100644 --- a/internal/check/readability.go +++ b/internal/check/readability.go @@ -41,7 +41,7 @@ func NewReadability(_ *core.Config, generic baseCheck, path string) (Readability } // Run calculates the readability level of the given text. -func (o Readability) Run(blk nlp.Block, _ *core.File) ([]core.Alert, error) { +func (o Readability) Run(blk nlp.Block, _ *core.File, _ *core.Config) ([]core.Alert, error) { var grade float64 var alerts []core.Alert diff --git a/internal/check/repetition.go b/internal/check/repetition.go index d03a929b..e284d661 100644 --- a/internal/check/repetition.go +++ b/internal/check/repetition.go @@ -61,7 +61,7 @@ func NewRepetition(cfg *core.Config, generic baseCheck, path string) (Repetition // Run executes the `repetition`-based rule. // // The rule looks for repeated matches of its regex -- such as "this this". -func (o Repetition) Run(blk nlp.Block, _ *core.File) ([]core.Alert, error) { +func (o Repetition) Run(blk nlp.Block, _ *core.File, cfg *core.Config) ([]core.Alert, error) { var curr, prev string var hit bool var ploc []int @@ -98,7 +98,7 @@ func (o Repetition) Run(blk nlp.Block, _ *core.File) ([]core.Alert, error) { if !strings.Contains(converted, "\n") && !isMatch(o.exceptRe, converted) { floc := []int{ploc[0], loc[1]} - a, erra := makeAlert(o.Definition, floc, txt) + a, erra := makeAlert(o.Definition, floc, txt, cfg) if erra != nil { return alerts, erra } diff --git a/internal/check/script.go b/internal/check/script.go index 59808674..c0a82133 100644 --- a/internal/check/script.go +++ b/internal/check/script.go @@ -47,7 +47,7 @@ func NewScript(cfg *core.Config, generic baseCheck, path string) (Script, error) } // Run executes the given script and returns its Alerts. -func (s Script) Run(blk nlp.Block, _ *core.File) ([]core.Alert, error) { +func (s Script) Run(blk nlp.Block, _ *core.File, _ *core.Config) ([]core.Alert, error) { var alerts []core.Alert script := tengo.NewScript([]byte(s.Script)) diff --git a/internal/check/sequence.go b/internal/check/sequence.go index 18809846..c79aaadc 100644 --- a/internal/check/sequence.go +++ b/internal/check/sequence.go @@ -225,7 +225,7 @@ func stepsToString(steps []string) string { } // Run looks for the user-defined sequence of tokens. -func (s Sequence) Run(blk nlp.Block, f *core.File) ([]core.Alert, error) { +func (s Sequence) Run(blk nlp.Block, f *core.File, _ *core.Config) ([]core.Alert, error) { var alerts []core.Alert var offset []string diff --git a/internal/check/spelling.go b/internal/check/spelling.go index 5cddb123..09fa0c9c 100644 --- a/internal/check/spelling.go +++ b/internal/check/spelling.go @@ -154,7 +154,7 @@ func NewSpelling(cfg *core.Config, generic baseCheck, path string) (Spelling, er } // Run performs spell-checking on the provided text. -func (s Spelling) Run(blk nlp.Block, _ *core.File) ([]core.Alert, error) { +func (s Spelling) Run(blk nlp.Block, _ *core.File, _ *core.Config) ([]core.Alert, error) { var alerts []core.Alert txt := blk.Text diff --git a/internal/check/substitution.go b/internal/check/substitution.go index bac5bf00..191f0428 100644 --- a/internal/check/substitution.go +++ b/internal/check/substitution.go @@ -93,7 +93,7 @@ func NewSubstitution(cfg *core.Config, generic baseCheck, path string) (Substitu // Run executes the `substitution`-based rule. // // The rule looks for one pattern and then suggests a replacement. -func (s Substitution) Run(blk nlp.Block, _ *core.File) ([]core.Alert, error) { +func (s Substitution) Run(blk nlp.Block, _ *core.File, cfg *core.Config) ([]core.Alert, error) { var alerts []core.Alert txt := blk.Text @@ -139,7 +139,7 @@ func (s Substitution) Run(blk nlp.Block, _ *core.File) ([]core.Alert, error) { s.Message = convertMessage(s.Message) } - a, aerr := makeAlert(s.Definition, loc, txt) + a, aerr := makeAlert(s.Definition, loc, txt, cfg) if aerr != nil { return alerts, aerr } diff --git a/internal/check/substitution_test.go b/internal/check/substitution_test.go index 9c7ebb53..b4e39c64 100644 --- a/internal/check/substitution_test.go +++ b/internal/check/substitution_test.go @@ -54,7 +54,7 @@ func TestIsDeterministic(t *testing.T) { t.Fatal(err) } - actual, err := rule.Run(nlp.NewBlock(text, text, "text"), &core.File{}) + actual, err := rule.Run(nlp.NewBlock(text, text, "text"), &core.File{}, &core.Config{}) if err != nil { t.Fatal(err) } @@ -85,7 +85,7 @@ func TestRegex(t *testing.T) { t.Fatal(err) } - actual, err := rule.Run(nlp.NewBlock(text, text, "text"), &core.File{}) + actual, err := rule.Run(nlp.NewBlock(text, text, "text"), &core.File{}, &core.Config{}) if err != nil { t.Fatal(err) } @@ -115,7 +115,7 @@ func TestRegexEscapedParens(t *testing.T) { t.Fatal(err) } - actual, err := rule.Run(nlp.NewBlock(text, text, "text"), &core.File{}) + actual, err := rule.Run(nlp.NewBlock(text, text, "text"), &core.File{}, &core.Config{}) if err != nil { t.Fatal(err) } diff --git a/internal/lint/lint.go b/internal/lint/lint.go index ad07a4b3..e2c85847 100755 --- a/internal/lint/lint.go +++ b/internal/lint/lint.go @@ -280,7 +280,7 @@ func (l *Linter) lintBlock(f *core.File, blk nlp.Block, lines, pad int, lookup b info := chk.Fields() - alerts, err := chk.Run(blk, f) + alerts, err := chk.Run(blk, f, l.Manager.Config) if err != nil { return err }