From df4a93489ad499b05384bbff4c3bc571a4186f8e Mon Sep 17 00:00:00 2001 From: Joseph Kato Date: Wed, 25 Dec 2024 08:11:56 -0700 Subject: [PATCH 01/15] refactor: only fetch library when needed #932 WIP --- cmd/vale/library.go | 33 ++++++++++++++++++++++++++++ cmd/vale/sync.go | 44 ++++++-------------------------------- internal/core/util_test.go | 8 +++++-- 3 files changed, 45 insertions(+), 40 deletions(-) create mode 100644 cmd/vale/library.go diff --git a/cmd/vale/library.go b/cmd/vale/library.go new file mode 100644 index 00000000..e8fcf618 --- /dev/null +++ b/cmd/vale/library.go @@ -0,0 +1,33 @@ +package main + +import "encoding/json" + +var library = "https://raw.githubusercontent.com/errata-ai/styles/master/library.json" + +func getLibrary(_ string) ([]Style, error) { + styles := []Style{} + + resp, err := fetchJSON(library) + if err != nil { + return styles, err + } else if err = json.Unmarshal(resp, &styles); err != nil { + return styles, err + } + + return styles, err +} + +func inLibrary(name, path string) string { + lookup, err := getLibrary(path) + if err != nil { + return "" + } + + for _, entry := range lookup { + if name == entry.Name { + return entry.URL + } + } + + return "" +} diff --git a/cmd/vale/sync.go b/cmd/vale/sync.go index 7f825d39..56f6bdd9 100644 --- a/cmd/vale/sync.go +++ b/cmd/vale/sync.go @@ -1,7 +1,6 @@ package main import ( - "encoding/json" "fmt" "os" "path/filepath" @@ -13,8 +12,6 @@ import ( "github.com/errata-ai/vale/v3/internal/core" ) -var library = "https://raw.githubusercontent.com/errata-ai/styles/master/library.json" - func initPath(cfg *core.Config) error { // The first entry is always the default `StylesPath`. stylesPath := cfg.StylesPath() @@ -36,29 +33,13 @@ func initPath(cfg *core.Config) error { } func readPkg(pkg, path string, idx int) error { - lookup, err := getLibrary(path) - if err != nil { - return err - } - - found := false - for _, entry := range lookup { - if pkg == entry.Name { - found = true - if err = download(pkg, entry.URL, path, idx); err != nil { - return err - } + if core.IsPhrase(pkg) && !core.IsDir(pkg) { + entry := inLibrary(pkg, path) + if entry != "" { + return download(pkg, entry, path, idx) } } - - if !found { - name := fileNameWithoutExt(pkg) - if err = loadPkg(name, pkg, path, idx); err != nil { - return err - } - } - - return nil + return loadPkg(fileNameWithoutExt(pkg), pkg, path, idx) } func loadPkg(name, urlOrPath, styles string, index int) error { @@ -96,7 +77,7 @@ func download(name, url, styles string, index int) error { if err = fetch(url, dir); err != nil { if strings.Contains(err.Error(), "unsupported protocol scheme") { - err = fmt.Errorf("'%s' is not a valid URL or the local file doesn't exist", url) + err = fmt.Errorf("'%s' is not a valid URL or the directory doesn't exist", url) } return core.NewE100("download", err) } @@ -194,16 +175,3 @@ func moveAsset(name, old, new string) error { //nolint:predeclared return cp.Copy(src, dst) } - -func getLibrary(_ string) ([]Style, error) { - styles := []Style{} - - resp, err := fetchJSON(library) - if err != nil { - return styles, err - } else if err = json.Unmarshal(resp, &styles); err != nil { - return styles, err - } - - return styles, err -} diff --git a/internal/core/util_test.go b/internal/core/util_test.go index 1daa332d..5876ed6d 100755 --- a/internal/core/util_test.go +++ b/internal/core/util_test.go @@ -38,8 +38,12 @@ func TestPrepText(t *testing.T) { func TestPhrase(t *testing.T) { rawToPrepped := map[string]bool{ - "test suite": true, - "test[ ]?suite": false, + "test suite": true, + "test[ ]?suite": false, + "Google": true, + "write-good": true, + "https://vale.sh/explorer": false, + "Google.zip": false, } for input, output := range rawToPrepped { result := IsPhrase(input) From 0c4f8806d75ef7490d4c92e9d8f8a3239356b2bc Mon Sep 17 00:00:00 2001 From: Joseph Kato Date: Wed, 25 Dec 2024 09:20:57 -0700 Subject: [PATCH 02/15] fix: handle regex-based replacements --- internal/check/manager.go | 4 +--- testdata/features/misc.feature | 1 + testdata/fixtures/vocab/Basic/test.md | 6 +++++- .../vocab/styles/config/vocabularies/Basic/accept.txt | 9 +++++---- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/internal/check/manager.go b/internal/check/manager.go index adbe55e8..f94b3533 100755 --- a/internal/check/manager.go +++ b/internal/check/manager.go @@ -272,9 +272,7 @@ func (mgr *Manager) loadVocabRules() { if len(mgr.Config.AcceptedTokens) > 0 { vocab := defaultRules["Terms"] for _, term := range mgr.Config.AcceptedTokens { - if core.IsPhrase(term) { - vocab["swap"].(map[string]string)[strings.ToLower(term)] = term - } + vocab["swap"].(map[string]string)[strings.ToLower(term)] = term } if level, ok := mgr.Config.RuleToLevel["Vale.Terms"]; ok { vocab["level"] = level diff --git a/testdata/features/misc.feature b/testdata/features/misc.feature index 62a2b696..ddbe33b7 100755 --- a/testdata/features/misc.feature +++ b/testdata/features/misc.feature @@ -11,6 +11,7 @@ Feature: Misc test.md:19:1:Vale.Terms:Use 'ABCDEF' instead of 'ABCDEf'. test.md:21:1:Vale.Terms:Use 'ABC-DEF' instead of 'ABC-DEf'. test.md:23:1:Vale.Terms:Use 'PLuG' instead of 'plug'. + test.md:27:17:Vale.Terms:Use '[Oo]bservability' instead of 'oBservability'. """ Scenario: Multiple Vocabs diff --git a/testdata/fixtures/vocab/Basic/test.md b/testdata/fixtures/vocab/Basic/test.md index 97ad5f5c..ab9e21fb 100755 --- a/testdata/fixtures/vocab/Basic/test.md +++ b/testdata/fixtures/vocab/Basic/test.md @@ -22,4 +22,8 @@ ABC-DEf plug -Github \ No newline at end of file +Github + +This is no good oBservability. + +This Observability and observability are okay. diff --git a/testdata/fixtures/vocab/styles/config/vocabularies/Basic/accept.txt b/testdata/fixtures/vocab/styles/config/vocabularies/Basic/accept.txt index f2672456..b5f0fc7b 100755 --- a/testdata/fixtures/vocab/styles/config/vocabularies/Basic/accept.txt +++ b/testdata/fixtures/vocab/styles/config/vocabularies/Basic/accept.txt @@ -1,9 +1,10 @@ +(?i)GitHub +[Oo]bservability [pP]y.*\b -definately # This is a comment +ABC-DEF +ABCDEF +definately Documentarians Log4j -ABCDEF -ABC-DEF PLuG -(?i)GitHub \ No newline at end of file From bab86b3fc50481f3da4804cdb284cf2705bfa619 Mon Sep 17 00:00:00 2001 From: Joseph Kato Date: Wed, 1 Jan 2025 17:07:46 -0800 Subject: [PATCH 03/15] feat: allow escaping "|" in substitution rules --- internal/check/substitution.go | 30 ++++++++++++++++++++++++++--- internal/check/substitution_test.go | 27 ++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 3 deletions(-) diff --git a/internal/check/substitution.go b/internal/check/substitution.go index e1aa0892..5834d151 100644 --- a/internal/check/substitution.go +++ b/internal/check/substitution.go @@ -5,9 +5,9 @@ import ( "sort" "strings" - "github.com/errata-ai/regexp2" "golang.org/x/exp/maps" + "github.com/errata-ai/regexp2" "github.com/errata-ai/vale/v3/internal/core" "github.com/errata-ai/vale/v3/internal/nlp" ) @@ -123,8 +123,7 @@ func (s Substitution) Run(blk nlp.Block, _ *core.File, cfg *core.Config) ([]core if !same && !isMatch(s.exceptRe, observed) { action := s.Fields().Action if action.Name == "replace" && len(action.Params) == 0 { - action.Params = strings.Split(expected, "|") - + action.Params = getOptions(expected) if s.Capitalize && observed == core.CapFirst(observed) { cased := []string{} for _, param := range action.Params { @@ -205,3 +204,28 @@ func subMsg(s Substitution, index int, observed string) (string, error) { msgRe := regexp2.MustCompileStd(msg) return msgRe.Replace(observed, expected, -1, -1) } + +// getOptions returns a slice of options from a match. +// +// For example, given the match "a|b|c", this function will return +// []string{"a", "b", "c"}. +// +// This allows the user to specify multiple options for a single match. +// +// https://vale.sh/docs/checks/substitution#multiple-suggestions +func getOptions(match string) []string { + options := []string{} + + // We want to ignore any escaped pipes, so make a temporary substitution: + // + // TODO: Add support for `.Split` in `regexp2`. + temp := strings.ReplaceAll(match, `\|`, "PIPE") + + for _, option := range strings.Split(temp, "|") { + if option != "" { + options = append(options, strings.ReplaceAll(option, "PIPE", `|`)) + } + } + + return options +} diff --git a/internal/check/substitution_test.go b/internal/check/substitution_test.go index b4e39c64..c0e41bc4 100644 --- a/internal/check/substitution_test.go +++ b/internal/check/substitution_test.go @@ -126,3 +126,30 @@ func TestRegexEscapedParens(t *testing.T) { t.Fatalf("Expected message `%s`, got `%s`", expected, message) } } + +func TestOptions(t *testing.T) { + cases := map[string][]string{ + "foo|bar": {"foo", "bar"}, + "foo|bar|baz": {"foo", "bar", "baz"}, + "|foo|": {"foo"}, + `\|foo\|`: {"|foo|"}, + `\|foo\||bar`: {"|foo|", "bar"}, + "foo|bar|": {"foo", "bar"}, + "foo|": {"foo"}, + "|": {}, + `\|`: {"|"}, + } + + for pattern, expected := range cases { + actual := getOptions(pattern) + if len(actual) != len(expected) { + t.Fatalf("Expected %d options, got %v", len(expected), actual) + } + + for i, opt := range expected { + if actual[i] != opt { + t.Fatalf("Expected '%s', got '%s'", opt, actual[i]) + } + } + } +} From 0bac7ed81c3f8452ea0bb138f14ddc0e655fb3b4 Mon Sep 17 00:00:00 2001 From: Joseph Kato Date: Wed, 1 Jan 2025 18:39:15 -0800 Subject: [PATCH 04/15] refactor: ensure JSON errors always include the same keys --- cmd/vale/error.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/cmd/vale/error.go b/cmd/vale/error.go index 076b61d3..227dfba9 100644 --- a/cmd/vale/error.go +++ b/cmd/vale/error.go @@ -68,11 +68,17 @@ func ShowError(err error, style string, out io.Writer) { if failed != nil { data = struct { - Code string + Line int + Path string Text string + Code string + Span int }{ + Line: 0, + Path: "", Text: core.StripANSI(err.Error()), Code: "E100", + Span: 0, } } else { data = struct { From 772c4f6a992b7ff2525d09d06bc0e302d40d17e4 Mon Sep 17 00:00:00 2001 From: Joseph Kato Date: Mon, 6 Jan 2025 22:16:03 -0800 Subject: [PATCH 05/15] feat: add support for JSON, YAML, and TOML --- go.mod | 14 +- go.sum | 28 ++-- internal/core/blueprint.go | 124 ++++++++++++++++++ internal/core/config.go | 24 ++-- internal/core/file.go | 7 +- internal/core/format.go | 10 +- internal/core/ini.go | 16 +++ internal/core/util.go | 15 +++ internal/lint/ast.go | 4 +- internal/lint/code.go | 24 ++++ internal/lint/data.go | 104 +++++++++++++++ internal/lint/fragment.go | 7 + internal/lint/lint.go | 19 +++ internal/nlp/provider.go | 7 +- testdata/features/blueprints.feature | 16 +++ testdata/features/styles.feature | 2 + testdata/fixtures/blueprints/.vale.ini | 19 +++ testdata/fixtures/blueprints/API.yml | 30 +++++ testdata/fixtures/blueprints/Rule.yml | 13 ++ testdata/fixtures/blueprints/test.py | 40 ++++++ testdata/styles/OpenAPI/Titles.yml | 5 + testdata/styles/config/blueprints/OpenAPI.yml | 6 + testdata/styles/config/blueprints/Python.yml | 3 + testdata/styles/config/blueprints/Vale.yml | 3 + 24 files changed, 504 insertions(+), 36 deletions(-) create mode 100644 internal/core/blueprint.go create mode 100644 internal/lint/data.go create mode 100644 testdata/features/blueprints.feature create mode 100644 testdata/fixtures/blueprints/.vale.ini create mode 100644 testdata/fixtures/blueprints/API.yml create mode 100644 testdata/fixtures/blueprints/Rule.yml create mode 100644 testdata/fixtures/blueprints/test.py create mode 100644 testdata/styles/OpenAPI/Titles.yml create mode 100644 testdata/styles/config/blueprints/OpenAPI.yml create mode 100644 testdata/styles/config/blueprints/Python.yml create mode 100644 testdata/styles/config/blueprints/Vale.yml diff --git a/go.mod b/go.mod index eabaf108..8e8755f7 100644 --- a/go.mod +++ b/go.mod @@ -20,15 +20,17 @@ require ( github.com/niklasfasching/go-org v1.6.6 github.com/olekukonko/tablewriter v0.0.4 github.com/otiai10/copy v1.7.0 + github.com/pelletier/go-toml/v2 v2.2.3 github.com/pterm/pterm v0.12.76 github.com/remeh/sizedwaitgroup v1.0.0 github.com/smacker/go-tree-sitter v0.0.0-20240514083259-c5d1f3f5f99e github.com/spf13/pflag v1.0.5 - github.com/stretchr/testify v1.8.4 + github.com/stretchr/testify v1.9.0 + github.com/tomwright/dasel/v2 v2.8.1 github.com/yuin/goldmark v1.5.6 golang.org/x/exp v0.0.0-20231006140011-7918f672742d - golang.org/x/net v0.23.0 - golang.org/x/sys v0.18.0 + golang.org/x/net v0.25.0 + golang.org/x/sys v0.20.0 gopkg.in/yaml.v2 v2.4.0 ) @@ -64,9 +66,9 @@ require ( github.com/ulikunitz/xz v0.5.10 // indirect github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect - golang.org/x/crypto v0.21.0 // indirect - golang.org/x/term v0.18.0 // indirect - golang.org/x/text v0.14.0 // indirect + golang.org/x/crypto v0.23.0 // indirect + golang.org/x/term v0.20.0 // indirect + golang.org/x/text v0.15.0 // indirect gopkg.in/neurosnap/sentences.v1 v1.0.7 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 328d74ad..d4412185 100644 --- a/go.sum +++ b/go.sum @@ -119,6 +119,8 @@ github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6 github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo= github.com/otiai10/mint v1.3.3 h1:7JgpsBaN0uMkyju4tbYHu0mnM55hNKVYLsXmwr15NQI= github.com/otiai10/mint v1.3.3/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc= +github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= +github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= github.com/pierrec/lz4/v4 v4.1.2 h1:qvY3YFXRQE/XB8MlLzJH7mSzBs74eA2gg52YTk6jUPM= github.com/pierrec/lz4/v4 v4.1.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -159,8 +161,10 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.4/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/tomwright/dasel/v2 v2.8.1 h1:mo5SlL0V2d3a0uPsD9Rrndn0cHWpbNDheB4+Fm++z8k= +github.com/tomwright/dasel/v2 v2.8.1/go.mod h1:6bNDNAnmGEtGpuIvksuQwiNcAgQ87pmzndynsqTNglc= github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8= @@ -176,8 +180,8 @@ github.com/yuin/goldmark v1.5.6/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5ta golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= -golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= -golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= @@ -187,8 +191,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= -golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -204,24 +208,24 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= -golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= -golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= +golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= +golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= diff --git a/internal/core/blueprint.go b/internal/core/blueprint.go new file mode 100644 index 00000000..f96bc9e1 --- /dev/null +++ b/internal/core/blueprint.go @@ -0,0 +1,124 @@ +package core + +import ( + "encoding/json" + "errors" + "fmt" + "os" + + "github.com/pelletier/go-toml/v2" + "github.com/tomwright/dasel/v2" + "gopkg.in/yaml.v2" +) + +type DaselValue = map[string]any + +var blueprintEngines = []string{"tree-sitter", "dasel", "command"} + +// A Query is a single query that we want to run against a document. +// +// The result of the query is optionally assigned the given scope. +type Query struct { + Scope string `yaml:"scope"` + Operation string `yaml:"operation"` +} + +// A Blueprint is a set of queries that we want to run against a document. +// +// The supported engines are: +// +// - `tree-sitter` +// - `dasel` +// - `command` +type Blueprint struct { + Engine string `yaml:"engine"` + Steps []Query `yaml:"steps"` +} + +// A ScopedValues is a value that has been assigned a scope. +type ScopedValues struct { + Scope string + Values []string +} + +// NewBlueprint creates a new blueprint from the given path. +func NewBlueprint(path string) (*Blueprint, error) { + var blueprint Blueprint + + data, err := os.ReadFile(path) + if err != nil { + return nil, err + } + + err = yaml.Unmarshal(data, &blueprint) + if err != nil { + return nil, err + } + + if blueprint.Engine == "" { + return nil, fmt.Errorf("missing parser") + } else if !StringInSlice(blueprint.Engine, blueprintEngines) { + return nil, fmt.Errorf("unsupported parser: %s", blueprint.Engine) + } + + if len(blueprint.Steps) == 0 { + return nil, fmt.Errorf("missing queries") + } + + return &blueprint, nil +} + +func (b *Blueprint) Apply(f *File) ([]ScopedValues, error) { + found := []ScopedValues{} + + value, err := fileToValue(f) + if err != nil { + return nil, NewE100(f.Path, err) + } + + for _, s := range b.Steps { + selected, err := dasel.Select(value, s.Operation) + if err != nil { + return found, err + } + + values := []string{} + for _, v := range selected { + values = append(values, v.String()) + } + + found = append(found, ScopedValues{ + Scope: s.Scope, + Values: values, + }) + } + + return found, nil +} + +func fileToValue(f *File) (DaselValue, error) { + var value DaselValue + + contents := []byte(f.Content) + switch f.RealExt { + case ".json": + err := json.Unmarshal(contents, &value) + if err != nil { + return nil, err + } + case ".yml": + err := yaml.Unmarshal(contents, &value) + if err != nil { + return nil, err + } + case ".toml": + err := toml.Unmarshal(contents, &value) + if err != nil { + return nil, err + } + default: + return nil, errors.New("unsupported file type") + } + + return value, nil +} diff --git a/internal/core/config.go b/internal/core/config.go index b10d42dd..4b4878bb 100644 --- a/internal/core/config.go +++ b/internal/core/config.go @@ -33,13 +33,14 @@ var ( // PipeDir is the default location for Vale's configuration pipeline. PipeDir = ".vale-config" - VocabDir = filepath.Join(ConfigDir, "vocabularies") - DictDir = filepath.Join(ConfigDir, "dictionaries") - TmplDir = filepath.Join(ConfigDir, "templates") - IgnoreDir = filepath.Join(ConfigDir, "ignore") - ActionDir = filepath.Join(ConfigDir, "actions") - FilterDir = filepath.Join(ConfigDir, "filters") - ScriptDir = filepath.Join(ConfigDir, "scripts") + VocabDir = filepath.Join(ConfigDir, "vocabularies") + DictDir = filepath.Join(ConfigDir, "dictionaries") + TmplDir = filepath.Join(ConfigDir, "templates") + IgnoreDir = filepath.Join(ConfigDir, "ignore") + ActionDir = filepath.Join(ConfigDir, "actions") + FilterDir = filepath.Join(ConfigDir, "filters") + ScriptDir = filepath.Join(ConfigDir, "scripts") + BlueprintsDir = filepath.Join(ConfigDir, "blueprints") ) // ConfigDirs is a list of all directories that contain user-defined, non-style @@ -52,6 +53,7 @@ var ConfigDirs = []string{ ActionDir, ScriptDir, FilterDir, + BlueprintsDir, } // ConfigVars is a list of all supported environment variables. @@ -210,9 +212,10 @@ type Config struct { AcceptedTokens []string `json:"-"` // Project-specific vocabulary (okay) RejectedTokens []string `json:"-"` // Project-specific vocabulary (avoid) - FallbackPath string `json:"-"` - SecToPat map[string]glob.Glob `json:"-"` - Styles []string `json:"-"` + FallbackPath string `json:"-"` + SecToPat map[string]glob.Glob `json:"-"` + Styles []string `json:"-"` + Blueprints map[string]*Blueprint `json:"-"` NLPEndpoint string // An external API to call for NLP-related work. @@ -241,6 +244,7 @@ func NewConfig(flags *CLIFlags) (*Config, error) { cfg.TokenIgnores = make(map[string][]string) cfg.CommentDelimiters = make(map[string][2]string) cfg.FormatToLang = make(map[string]string) + cfg.Blueprints = make(map[string]*Blueprint) cfg.Paths = []string{} cfg.ConfigFiles = []string{} diff --git a/internal/core/file.go b/internal/core/file.go index de63b119..ae181c10 100755 --- a/internal/core/file.go +++ b/internal/core/file.go @@ -92,10 +92,11 @@ func NewFile(src string, config *Config) (*File, error) { baseStyles := config.GBaseStyles checks := make(map[string]bool) + names := []string{} for _, fp := range filepaths { for _, sec := range config.StyleKeys { if pat, found := config.SecToPat[sec]; found && pat.Match(fp) { - baseStyles = config.SBaseStyles[sec] + names = append(names, config.SBaseStyles[sec]...) } } @@ -108,6 +109,10 @@ func NewFile(src string, config *Config) (*File, error) { } } + if len(names) > 0 { + baseStyles = UniqueStrings(names) + } + lang := "en" for syntax, code := range config.FormatToLang { sec, err := glob.Compile(syntax) diff --git a/internal/core/format.go b/internal/core/format.go index c3fd2beb..ad0dc9da 100755 --- a/internal/core/format.go +++ b/internal/core/format.go @@ -88,7 +88,9 @@ var FormatByExtension = map[string][]string{ `\.(?:ts|tsx)$`: {".ts", "code"}, `\.(?:txt)$`: {".txt", "text"}, `\.(?:xml)$`: {".xml", "markup"}, - `\.(?:yaml|yml)$`: {".yml", "code"}, + `\.(?:yaml|yml)$`: {".yml", "data"}, + `\.(?:json)$`: {".json", "data"}, + `\.(?:toml)$`: {".toml", "data"}, } // FormatFromExt takes a file extension and returns its [normExt, format] @@ -101,6 +103,12 @@ func FormatFromExt(path string, mapping map[string]string) (string, string) { if kind == "code" && getFormat("."+format) == "markup" { // NOTE: This is a special case of embedded markup within code. return "." + format, "fragment" + } else if kind == "data" && getFormat("."+format) == "markup" { + // NOTE: This is a special case of embedded markup within data. + // + // Unlike code, data formats are *always* linted as a fragment with + // the default format as plain text. + return "." + format, "data" } base = format } diff --git a/internal/core/ini.go b/internal/core/ini.go index 12d537bf..740e0860 100644 --- a/internal/core/ini.go +++ b/internal/core/ini.go @@ -138,6 +138,22 @@ var syntaxOpts = map[string]func(string, *ini.Section, *Config) error{ cfg.FormatToLang[label] = sec.Key("Lang").String() return nil }, + "Blueprint": func(label string, sec *ini.Section, cfg *Config) error { //nolint:unparam + name := sec.Key("Blueprint").String() + + path := FindConfigAsset(cfg, name+".yml", BlueprintsDir) + if path == "" { + return fmt.Errorf("blueprint '%s' not found", name) + } + + blueprint, err := NewBlueprint(path) + if err != nil { + return err + } + + cfg.Blueprints[label] = blueprint + return nil + }, } var globalOpts = map[string]func(*ini.Section, *Config){ diff --git a/internal/core/util.go b/internal/core/util.go index af735a68..9c13fc97 100755 --- a/internal/core/util.go +++ b/internal/core/util.go @@ -349,3 +349,18 @@ func FindProcess(pid int) *os.Process { return p } + +// UniqueStrings returns a new slice with all duplicate strings removed. +func UniqueStrings(slice []string) []string { + keys := make(map[string]bool) + list := []string{} + + for _, entry := range slice { + if _, value := keys[entry]; !value { + keys[entry] = true + list = append(list, entry) + } + } + + return list +} diff --git a/internal/lint/ast.go b/internal/lint/ast.go index 221b2ba2..0418131f 100644 --- a/internal/lint/ast.go +++ b/internal/lint/ast.go @@ -150,14 +150,14 @@ func (l *Linter) lintScope(f *core.File, state *walker, txt string) error { f.Metrics[strings.TrimPrefix(scope, "text.")]++ txt = strings.TrimLeft(txt, " ") - b := state.block(txt, scope+f.RealExt) + b := state.block(txt, scope+l.metaScope+f.RealExt) return l.lintBlock(f, b, state.lines, 0, false) } } f.Summary.WriteString(txt + "\n\n") - b := state.block(txt, "txt") + b := state.block(txt, "text"+l.metaScope+f.RealExt) return l.lintProse(f, b, state.lines) } diff --git a/internal/lint/code.go b/internal/lint/code.go index 25bed436..fa300bf8 100755 --- a/internal/lint/code.go +++ b/internal/lint/code.go @@ -8,10 +8,27 @@ import ( "strings" "github.com/errata-ai/vale/v3/internal/core" + "github.com/errata-ai/vale/v3/internal/glob" "github.com/errata-ai/vale/v3/internal/lint/code" "github.com/errata-ai/vale/v3/internal/nlp" ) +func updateQueries(f *core.File, blueprints map[string]*core.Blueprint) ([]string, error) { + for syntax, blueprint := range blueprints { + sec, err := glob.Compile(syntax) + if err != nil { + return nil, err + } else if sec.Match(f.Path) { + found := []string{} + for _, query := range blueprint.Steps { + found = append(found, query.Operation) + } + return found, nil + } + } + return []string{}, nil +} + func (l *Linter) lintCode(f *core.File) error { lang, err := code.GetLanguageFromExt(f.RealExt) if err != nil { @@ -20,6 +37,13 @@ func (l *Linter) lintCode(f *core.File) error { } ignored := l.Manager.Config.IgnoredScopes + found, err := updateQueries(f, l.Manager.Config.Blueprints) + if err != nil { + return err + } else if len(found) > 0 { + lang.Queries = found + } + comments, err := code.GetComments([]byte(f.Content), lang) if err != nil { return err diff --git a/internal/lint/data.go b/internal/lint/data.go new file mode 100644 index 00000000..dab02da6 --- /dev/null +++ b/internal/lint/data.go @@ -0,0 +1,104 @@ +package lint + +import ( + "fmt" + "strings" + + "github.com/errata-ai/vale/v3/internal/core" + "github.com/errata-ai/vale/v3/internal/glob" +) + +func (l *Linter) lintData(f *core.File) error { + for syntax, blueprint := range l.Manager.Config.Blueprints { + sec, err := glob.Compile(syntax) + if err != nil { + return err + } else if sec.Match(f.Path) { + found, err := blueprint.Apply(f) + if err != nil { + return core.NewE201FromTarget( + err.Error(), + fmt.Sprintf("Blueprint = %s", blueprint), + l.Manager.Config.RootINI, + ) + } + return l.lintScopedValues(f, found) + } + } + return nil +} + +func (l *Linter) lintScopedValues(f *core.File, values []core.ScopedValues) error { + var err error + // We want to set up our processing servers as if we were dealing with + // a directory since we likely have many fragments to convert. + l.HasDir = true + + wholeFile := f.Content + last := 0 + index := 0 + + for _, matches := range values { + l.SetMetaScope(matches.Scope) + for _, v := range matches.Values { + i, line := findLineBySubstring(wholeFile, v, index) + if i == 0 { + return core.NewE100(f.Path, fmt.Errorf("'%s' not found", v)) + } + + index = i + f.SetText(v) + + switch f.NormedExt { + case ".md": + err = l.lintMarkdown(f) + case ".rst": + err = l.lintRST(f) + case ".xml": + err = l.lintADoc(f) + case ".html": + err = l.lintHTML(f) + case ".org": + err = l.lintOrg(f) + default: + err = l.lintLines(f) + } + + size := len(f.Alerts) + if size != last { + padding := strings.Index(line, v) + f.Alerts = adjustPos(f.Alerts, last, i, padding) + } + last = size + } + } + + return err +} + +func findLineBySubstring(s, sub string, last int) (int, string) { + if strings.Count(sub, "\n") > 0 { + sub = strings.Split(sub, "\n")[0] + } + + for i, line := range strings.Split(s, "\n") { + if i >= last && strings.Contains(line, sub) { + return i + 1, line + } + } + + return 0, "" +} + +func adjustPos(alerts []core.Alert, last, line, padding int) []core.Alert { + for i := range alerts { + if i >= last { + alerts[i].Line += line - 1 + alerts[i].Span = []int{ + alerts[i].Span[0] + padding, + alerts[i].Span[1] + padding, + } + } + } + return alerts +} diff --git a/internal/lint/fragment.go b/internal/lint/fragment.go index 4be6dd9d..50c7e230 100644 --- a/internal/lint/fragment.go +++ b/internal/lint/fragment.go @@ -58,6 +58,13 @@ func (l *Linter) lintFragments(f *core.File) error { return err } + found, err := updateQueries(f, l.Manager.Config.Blueprints) + if err != nil { + return err + } else if len(found) > 0 { + lang.Queries = found + } + comments, err := code.GetComments([]byte(f.Content), lang) if err != nil { return err diff --git a/internal/lint/lint.go b/internal/lint/lint.go index 98de9518..52a7b430 100755 --- a/internal/lint/lint.go +++ b/internal/lint/lint.go @@ -25,6 +25,7 @@ type Linter struct { client *http.Client HasDir bool nonGlobal bool + metaScope string } type lintResult struct { @@ -69,6 +70,18 @@ func (l *Linter) LintString(src string) ([]*core.File, error) { return []*core.File{linted.file}, linted.err } +// SetMetaScope sets an optional meta scope. +// +// A meta scope is a string that is appended to the end of each check's scope +// providing extra context for the check. +func (l *Linter) SetMetaScope(scope string) { + if scope != "" { + l.metaScope = "." + scope + } else { + l.metaScope = "" + } +} + // Lint src according to its format. func (l *Linter) Lint(input []string, pat string) ([]*core.File, error) { var linted []*core.File @@ -190,6 +203,10 @@ func (l *Linter) lintFile(src string) lintResult { file.NLP = l.Manager.AssignNLP(file) simple := l.Manager.Config.Flags.Simple + // NOTE: This is a sanity check to ensure that we don't run any checks that + // we actually have a blueprint to apply. + hasBlueprints := len(l.Manager.Config.Blueprints) > 0 + if file.Format == "markup" && !simple { //nolint:gocritic switch file.NormedExt { case ".adoc": @@ -207,6 +224,8 @@ func (l *Linter) lintFile(src string) lintResult { case ".org": err = l.lintOrg(file) } + } else if file.Format == "data" && !simple && hasBlueprints { + err = l.lintData(file) } else if file.Format == "code" && !simple { err = l.lintCode(file) } else if file.Format == "fragment" && !simple { diff --git a/internal/nlp/provider.go b/internal/nlp/provider.go index 207b779b..6b7ea318 100644 --- a/internal/nlp/provider.go +++ b/internal/nlp/provider.go @@ -88,12 +88,11 @@ func (n *Info) doNLP(blk *Block, seg segmenter) ([]Block, error) { ctx := blk.Context idx := blk.Line - ext := n.Scope if n.Splitting { for _, p := range strings.SplitAfter(blk.Text, "\n\n") { blks = append( - blks, NewLinedBlock(ctx, p, "paragraph"+ext, idx, nil)) + blks, NewLinedBlock(ctx, p, "paragraph."+blk.Scope, idx, nil)) } } @@ -102,13 +101,13 @@ func (n *Info) doNLP(blk *Block, seg segmenter) ([]Block, error) { s = strings.TrimSpace(s) if s != "" { blks = append( - blks, NewLinedBlock(ctx, s, "sentence"+ext, idx, nil)) + blks, NewLinedBlock(ctx, s, "sentence."+blk.Scope, idx, nil)) } } } blks = append( - blks, NewLinedBlock(ctx, blk.Text, "text"+ext, idx, nil)) + blks, NewLinedBlock(ctx, blk.Text, blk.Scope, idx, nil)) return blks, nil } diff --git a/testdata/features/blueprints.feature b/testdata/features/blueprints.feature new file mode 100644 index 00000000..9accfbf4 --- /dev/null +++ b/testdata/features/blueprints.feature @@ -0,0 +1,16 @@ +Feature: Blueprints + Scenario: YAML + When I test "blueprints" + Then the output should contain exactly: + """ + API.yml:3:10:OpenAPI.Titles:'sample API' should be capitalized + API.yml:4:25:Vale.Spelling:Did you really mean 'multiline'? + API.yml:9:70:Vale.Spelling:Did you really mean 'serrver'? + API.yml:13:17:Vale.Spelling:Did you really mean 'serrver'? + API.yml:15:70:Vale.Spelling:Did you really mean 'serrver'? + Rule.yml:3:39:Vale.Repetition:'can' is repeated! + test.py:1:3:vale.Annotations:'FIXME' left in text + test.py:11:3:vale.Annotations:'XXX' left in text + test.py:13:16:vale.Annotations:'XXX' left in text + test.py:14:14:vale.Annotations:'NOTE' left in text + """ diff --git a/testdata/features/styles.feature b/testdata/features/styles.feature index 2cd87254..8123f234 100755 --- a/testdata/features/styles.feature +++ b/testdata/features/styles.feature @@ -60,7 +60,9 @@ Feature: Styles test.rst:3:1:demo.SentenceLength:Sentences should be less than 25 words test.rst:5:28:demo.Ending-Preposition:Don't end a sentence with 'by.' test.rst:9:1:demo.ParagraphLength:Paragraphs should be less than 150 words + test.rst:13:62:demo.Spelling:Inconsistent spelling of 'centre' test.rst:20:25:demo.Spelling:Inconsistent spelling of 'center' + test.rst:24:10:demo.Spelling:Inconsistent spelling of 'color' test.rst:24:32:demo.Spelling:Inconsistent spelling of 'colour' test.rst:32:1:Limit.Rule:Don't use 'hey'. """ diff --git a/testdata/fixtures/blueprints/.vale.ini b/testdata/fixtures/blueprints/.vale.ini new file mode 100644 index 00000000..a18968a0 --- /dev/null +++ b/testdata/fixtures/blueprints/.vale.ini @@ -0,0 +1,19 @@ +StylesPath = ../../styles +MinAlertLevel = suggestion + +[formats] +yml = md + +[*.py] +vale.Annotations = YES + +Blueprint = Python + +[*.yml] +BasedOnStyles = Vale, OpenAPI + +[API.yml] +Blueprint = OpenAPI + +[Rule.yml] +Blueprint = Vale diff --git a/testdata/fixtures/blueprints/API.yml b/testdata/fixtures/blueprints/API.yml new file mode 100644 index 00000000..77608473 --- /dev/null +++ b/testdata/fixtures/blueprints/API.yml @@ -0,0 +1,30 @@ +openapi: 3.0.0 +info: + title: sample API + description: Optional multiline or single-line description in [CommonMark](http://commonmark.org/help/) or HTML. + version: 0.1.9 + +servers: + - url: http://api.example.com/v1 + description: Optional server description, e.g. Main (production) serrver + - url: http://staging-api.example.com + description: | + Optional server description, e.g. + Internal staging serrver for testing + - url: http://api.example.com/v2 + description: Optional server description, e.g. Main (production) serrver + +paths: + /users: + get: + summary: Returns a list of users. + description: Optional extended description in CommonMark or HTML. + responses: + "200": # status code + description: A JSON array of user names + content: + application/json: + schema: + type: array + items: + type: string diff --git a/testdata/fixtures/blueprints/Rule.yml b/testdata/fixtures/blueprints/Rule.yml new file mode 100644 index 00000000..0aee02ef --- /dev/null +++ b/testdata/fixtures/blueprints/Rule.yml @@ -0,0 +1,13 @@ +extends: existence +message: "Avoid using '%s'" +description: "Bro culture terminology can can really reduce the number of people likely to show interest." +ignorecase: true +level: error +tokens: + - "brogramm(?:er|ers|ing)" + - "crank" + - "crush" + - "hard[ -]*core" + - "hella" + - "mak(?:e|ing) it rain" + - "skillz" diff --git a/testdata/fixtures/blueprints/test.py b/testdata/fixtures/blueprints/test.py new file mode 100644 index 00000000..de59a786 --- /dev/null +++ b/testdata/fixtures/blueprints/test.py @@ -0,0 +1,40 @@ +# FIXME: + +def FIXME(): + """ + FIXME: + """ + print(""" +FIXME: This should *not* be linted. +""") + +# XXX: This should be flagged! + +NOTE = False # XXX: +XXX = True # NOTE: + +r""" +NOTE: + + + + + +XXX: +""" + +def NOTE(): + ''' + NOTE: + ''' + XXX = ''' +XXX: This should *not* be linted. +''' + +def foo(self): + """NOTE This is the start of a block. + + TODO: Assume that a file is modified since an invalid timestamp as per RFC + 2616, section 14.25. GMT + """ + invalid_date = 'FIXME: Mon, 28 May 999999999999 28:25:26 GMT' diff --git a/testdata/styles/OpenAPI/Titles.yml b/testdata/styles/OpenAPI/Titles.yml new file mode 100644 index 00000000..f1ca8734 --- /dev/null +++ b/testdata/styles/OpenAPI/Titles.yml @@ -0,0 +1,5 @@ +extends: existence +message: "'%s' should be capitalized" +scope: title +raw: + - "^[a-z].+" diff --git a/testdata/styles/config/blueprints/OpenAPI.yml b/testdata/styles/config/blueprints/OpenAPI.yml new file mode 100644 index 00000000..ad9555db --- /dev/null +++ b/testdata/styles/config/blueprints/OpenAPI.yml @@ -0,0 +1,6 @@ +engine: dasel +steps: + - scope: title + operation: info.title + - operation: info.description + - operation: servers.all().description diff --git a/testdata/styles/config/blueprints/Python.yml b/testdata/styles/config/blueprints/Python.yml new file mode 100644 index 00000000..a9eceb1c --- /dev/null +++ b/testdata/styles/config/blueprints/Python.yml @@ -0,0 +1,3 @@ +engine: tree-sitter +steps: + - operation: (comment)+ @comment diff --git a/testdata/styles/config/blueprints/Vale.yml b/testdata/styles/config/blueprints/Vale.yml new file mode 100644 index 00000000..e5859790 --- /dev/null +++ b/testdata/styles/config/blueprints/Vale.yml @@ -0,0 +1,3 @@ +engine: dasel +steps: + - operation: description From 153d5f6e8848655456dd14bf8f412d2a8c0100c0 Mon Sep 17 00:00:00 2001 From: Joseph Kato Date: Wed, 8 Jan 2025 16:57:52 -0800 Subject: [PATCH 06/15] fix: #944 --- internal/lint/rst.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/lint/rst.go b/internal/lint/rst.go index 6bea5593..981caa57 100644 --- a/internal/lint/rst.go +++ b/internal/lint/rst.go @@ -14,7 +14,7 @@ import ( // // reCodeBlock is used to convert Sphinx-style code directives to the regular // `::` for rst2html, including the use of runtime options (e.g., :caption:). -var reCodeBlock = regexp.MustCompile(`.. (?:raw|code(?:-block)?):: (?:[\w-]+)(?:\s+:\w+: .+)*`) +var reCodeBlock = regexp.MustCompile(`\.\. (?:raw|code(?:-block)?):: *(?:[\w-]+)?(?:\s+:\w+:.*)*`) // We replace custom directives with `.. code::`. // From 84f0615901eba9504e56c914d0447447d0743ed8 Mon Sep 17 00:00:00 2001 From: Joseph Kato Date: Wed, 8 Jan 2025 17:03:25 -0800 Subject: [PATCH 07/15] refactor: fix golangci --- internal/core/blueprint.go | 6 +++--- internal/core/ini.go | 2 +- internal/lint/data.go | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/internal/core/blueprint.go b/internal/core/blueprint.go index f96bc9e1..490611d6 100644 --- a/internal/core/blueprint.go +++ b/internal/core/blueprint.go @@ -77,9 +77,9 @@ func (b *Blueprint) Apply(f *File) ([]ScopedValues, error) { } for _, s := range b.Steps { - selected, err := dasel.Select(value, s.Operation) - if err != nil { - return found, err + selected, verr := dasel.Select(value, s.Operation) + if verr != nil { + return found, verr } values := []string{} diff --git a/internal/core/ini.go b/internal/core/ini.go index 740e0860..02afa585 100644 --- a/internal/core/ini.go +++ b/internal/core/ini.go @@ -138,7 +138,7 @@ var syntaxOpts = map[string]func(string, *ini.Section, *Config) error{ cfg.FormatToLang[label] = sec.Key("Lang").String() return nil }, - "Blueprint": func(label string, sec *ini.Section, cfg *Config) error { //nolint:unparam + "Blueprint": func(label string, sec *ini.Section, cfg *Config) error { name := sec.Key("Blueprint").String() path := FindConfigAsset(cfg, name+".yml", BlueprintsDir) diff --git a/internal/lint/data.go b/internal/lint/data.go index dab02da6..1ee0b3fc 100644 --- a/internal/lint/data.go +++ b/internal/lint/data.go @@ -14,10 +14,10 @@ func (l *Linter) lintData(f *core.File) error { if err != nil { return err } else if sec.Match(f.Path) { - found, err := blueprint.Apply(f) - if err != nil { + found, berr := blueprint.Apply(f) + if berr != nil { return core.NewE201FromTarget( - err.Error(), + berr.Error(), fmt.Sprintf("Blueprint = %s", blueprint), l.Manager.Config.RootINI, ) From 59a3c55397a75637678e75ddeac52fb268e03c76 Mon Sep 17 00:00:00 2001 From: Joseph Kato Date: Wed, 8 Jan 2025 17:16:36 -0800 Subject: [PATCH 08/15] chore: upgrade /x/crypto and /x/net --- go.mod | 10 +++++----- go.sum | 20 ++++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/go.mod b/go.mod index 8e8755f7..73a143d3 100644 --- a/go.mod +++ b/go.mod @@ -29,8 +29,8 @@ require ( github.com/tomwright/dasel/v2 v2.8.1 github.com/yuin/goldmark v1.5.6 golang.org/x/exp v0.0.0-20231006140011-7918f672742d - golang.org/x/net v0.25.0 - golang.org/x/sys v0.20.0 + golang.org/x/net v0.34.0 + golang.org/x/sys v0.29.0 gopkg.in/yaml.v2 v2.4.0 ) @@ -66,9 +66,9 @@ require ( github.com/ulikunitz/xz v0.5.10 // indirect github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect - golang.org/x/crypto v0.23.0 // indirect - golang.org/x/term v0.20.0 // indirect - golang.org/x/text v0.15.0 // indirect + golang.org/x/crypto v0.32.0 // indirect + golang.org/x/term v0.28.0 // indirect + golang.org/x/text v0.21.0 // indirect gopkg.in/neurosnap/sentences.v1 v1.0.7 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index d4412185..b4e9df12 100644 --- a/go.sum +++ b/go.sum @@ -180,8 +180,8 @@ github.com/yuin/goldmark v1.5.6/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5ta golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= -golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= -golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= +golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= @@ -191,8 +191,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= +golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -208,24 +208,24 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= +golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= -golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= +golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg= +golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= -golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= From acda5adba72e1872e6906d2f1a8665bfd60649a1 Mon Sep 17 00:00:00 2001 From: Joseph Kato Date: Wed, 8 Jan 2025 17:22:56 -0800 Subject: [PATCH 09/15] refactor: operation -> query --- internal/core/blueprint.go | 14 +++++++------- internal/lint/code.go | 2 +- testdata/styles/config/blueprints/OpenAPI.yml | 6 +++--- testdata/styles/config/blueprints/Python.yml | 2 +- testdata/styles/config/blueprints/Vale.yml | 2 +- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/internal/core/blueprint.go b/internal/core/blueprint.go index 490611d6..bec1ec0b 100644 --- a/internal/core/blueprint.go +++ b/internal/core/blueprint.go @@ -15,12 +15,12 @@ type DaselValue = map[string]any var blueprintEngines = []string{"tree-sitter", "dasel", "command"} -// A Query is a single query that we want to run against a document. +// A Step is a single query that we want to run against a document. // // The result of the query is optionally assigned the given scope. -type Query struct { - Scope string `yaml:"scope"` - Operation string `yaml:"operation"` +type Step struct { + Scope string `yaml:"scope"` + Query string `yaml:"query"` } // A Blueprint is a set of queries that we want to run against a document. @@ -31,8 +31,8 @@ type Query struct { // - `dasel` // - `command` type Blueprint struct { - Engine string `yaml:"engine"` - Steps []Query `yaml:"steps"` + Engine string `yaml:"engine"` + Steps []Step `yaml:"steps"` } // A ScopedValues is a value that has been assigned a scope. @@ -77,7 +77,7 @@ func (b *Blueprint) Apply(f *File) ([]ScopedValues, error) { } for _, s := range b.Steps { - selected, verr := dasel.Select(value, s.Operation) + selected, verr := dasel.Select(value, s.Query) if verr != nil { return found, verr } diff --git a/internal/lint/code.go b/internal/lint/code.go index fa300bf8..065725aa 100755 --- a/internal/lint/code.go +++ b/internal/lint/code.go @@ -21,7 +21,7 @@ func updateQueries(f *core.File, blueprints map[string]*core.Blueprint) ([]strin } else if sec.Match(f.Path) { found := []string{} for _, query := range blueprint.Steps { - found = append(found, query.Operation) + found = append(found, query.Query) } return found, nil } diff --git a/testdata/styles/config/blueprints/OpenAPI.yml b/testdata/styles/config/blueprints/OpenAPI.yml index ad9555db..adc11460 100644 --- a/testdata/styles/config/blueprints/OpenAPI.yml +++ b/testdata/styles/config/blueprints/OpenAPI.yml @@ -1,6 +1,6 @@ engine: dasel steps: - scope: title - operation: info.title - - operation: info.description - - operation: servers.all().description + query: info.title + - query: info.description + - query: servers.all().description diff --git a/testdata/styles/config/blueprints/Python.yml b/testdata/styles/config/blueprints/Python.yml index a9eceb1c..b3a32a92 100644 --- a/testdata/styles/config/blueprints/Python.yml +++ b/testdata/styles/config/blueprints/Python.yml @@ -1,3 +1,3 @@ engine: tree-sitter steps: - - operation: (comment)+ @comment + - query: (comment)+ @comment diff --git a/testdata/styles/config/blueprints/Vale.yml b/testdata/styles/config/blueprints/Vale.yml index e5859790..6522e6e2 100644 --- a/testdata/styles/config/blueprints/Vale.yml +++ b/testdata/styles/config/blueprints/Vale.yml @@ -1,3 +1,3 @@ engine: dasel steps: - - operation: description + - query: description From 75ff56277a21c8beb948add0b5640f6969447565 Mon Sep 17 00:00:00 2001 From: Kursat Aktas Date: Thu, 9 Jan 2025 05:30:50 +0300 Subject: [PATCH 10/15] Introducing Vale Guru on Gurubase.io (#924) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ec664dfc..93d2cb1e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Vale: Your style, our editor [![Build status](https://ci.appveyor.com/api/projects/status/snk0oo6ih1nwuf6r?svg=true)](https://ci.appveyor.com/project/jdkato/vale) [![GitHub All Releases](https://img.shields.io/github/downloads/errata-ai/vale/total?logo=GitHub&color=ff69b4)](https://github.com/errata-ai/vale/releases) [![Docker Pulls](https://img.shields.io/docker/pulls/jdkato/vale?color=orange&logo=docker&logoColor=white)](https://hub.docker.com/r/jdkato/vale) [![Chocolatey](https://img.shields.io/chocolatey/dt/vale?color=white&label=chocolatey&logo=chocolatey)](https://community.chocolatey.org/packages/vale) [![Homebrew](https://img.shields.io/homebrew/installs/dy/vale?color=yellow&label=homebrew&logo=homebrew)](https://formulae.brew.sh/formula/vale) +# Vale: Your style, our editor [![Build status](https://ci.appveyor.com/api/projects/status/snk0oo6ih1nwuf6r?svg=true)](https://ci.appveyor.com/project/jdkato/vale) [![GitHub All Releases](https://img.shields.io/github/downloads/errata-ai/vale/total?logo=GitHub&color=ff69b4)](https://github.com/errata-ai/vale/releases) [![Docker Pulls](https://img.shields.io/docker/pulls/jdkato/vale?color=orange&logo=docker&logoColor=white)](https://hub.docker.com/r/jdkato/vale) [![Chocolatey](https://img.shields.io/chocolatey/dt/vale?color=white&label=chocolatey&logo=chocolatey)](https://community.chocolatey.org/packages/vale) [![Homebrew](https://img.shields.io/homebrew/installs/dy/vale?color=yellow&label=homebrew&logo=homebrew)](https://formulae.brew.sh/formula/vale) [![Gurubase](https://img.shields.io/badge/Gurubase-Ask%20Vale%20Guru-006BFF)](https://gurubase.io/g/vale)

Vale is a command-line tool that brings code-like linting to prose. It's fast, cross-platform (Windows, macOS, and Linux), and highly customizable. From 462ecd70863116b5d8220aef2180c369edf3e178 Mon Sep 17 00:00:00 2001 From: Joseph Kato Date: Wed, 8 Jan 2025 18:38:34 -0800 Subject: [PATCH 11/15] Update accept.txt --- .github/styles/config/vocabularies/Vale/accept.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/styles/config/vocabularies/Vale/accept.txt b/.github/styles/config/vocabularies/Vale/accept.txt index 9da9b5a1..c5003b30 100644 --- a/.github/styles/config/vocabularies/Vale/accept.txt +++ b/.github/styles/config/vocabularies/Vale/accept.txt @@ -16,6 +16,7 @@ endtab endtabs flesch gunning fog +Gurubase haskell Homebrew ignorecase From 36e249cce8639b4d7e5c0c45b4d3615c0c2d2d55 Mon Sep 17 00:00:00 2001 From: Joseph Kato Date: Thu, 9 Jan 2025 12:45:10 -0800 Subject: [PATCH 12/15] refactor: re-name struct fields --- internal/core/blueprint.go | 25 +++++++++---------- internal/lint/code.go | 4 +-- testdata/styles/config/blueprints/OpenAPI.yml | 10 ++++---- testdata/styles/config/blueprints/Python.yml | 4 +-- testdata/styles/config/blueprints/Vale.yml | 4 +-- 5 files changed, 23 insertions(+), 24 deletions(-) diff --git a/internal/core/blueprint.go b/internal/core/blueprint.go index bec1ec0b..5d82bb12 100644 --- a/internal/core/blueprint.go +++ b/internal/core/blueprint.go @@ -13,14 +13,13 @@ import ( type DaselValue = map[string]any -var blueprintEngines = []string{"tree-sitter", "dasel", "command"} +var blueprintEngines = []string{"tree-sitter", "dasel"} -// A Step is a single query that we want to run against a document. -// -// The result of the query is optionally assigned the given scope. -type Step struct { - Scope string `yaml:"scope"` - Query string `yaml:"query"` +// A Scope is a single query that we want to run against a document. +type Scope struct { + Name string `yaml:"name"` + Expr string `yaml:"expr"` + Type string `yaml:"type"` } // A Blueprint is a set of queries that we want to run against a document. @@ -31,8 +30,8 @@ type Step struct { // - `dasel` // - `command` type Blueprint struct { - Engine string `yaml:"engine"` - Steps []Step `yaml:"steps"` + Engine string `yaml:"engine"` + Scopes []Scope `yaml:"scopes"` } // A ScopedValues is a value that has been assigned a scope. @@ -61,7 +60,7 @@ func NewBlueprint(path string) (*Blueprint, error) { return nil, fmt.Errorf("unsupported parser: %s", blueprint.Engine) } - if len(blueprint.Steps) == 0 { + if len(blueprint.Scopes) == 0 { return nil, fmt.Errorf("missing queries") } @@ -76,8 +75,8 @@ func (b *Blueprint) Apply(f *File) ([]ScopedValues, error) { return nil, NewE100(f.Path, err) } - for _, s := range b.Steps { - selected, verr := dasel.Select(value, s.Query) + for _, s := range b.Scopes { + selected, verr := dasel.Select(value, s.Expr) if verr != nil { return found, verr } @@ -88,7 +87,7 @@ func (b *Blueprint) Apply(f *File) ([]ScopedValues, error) { } found = append(found, ScopedValues{ - Scope: s.Scope, + Scope: s.Name, Values: values, }) } diff --git a/internal/lint/code.go b/internal/lint/code.go index 065725aa..a2bcd0cc 100755 --- a/internal/lint/code.go +++ b/internal/lint/code.go @@ -20,8 +20,8 @@ func updateQueries(f *core.File, blueprints map[string]*core.Blueprint) ([]strin return nil, err } else if sec.Match(f.Path) { found := []string{} - for _, query := range blueprint.Steps { - found = append(found, query.Query) + for _, query := range blueprint.Scopes { + found = append(found, query.Expr) } return found, nil } diff --git a/testdata/styles/config/blueprints/OpenAPI.yml b/testdata/styles/config/blueprints/OpenAPI.yml index adc11460..6fef35de 100644 --- a/testdata/styles/config/blueprints/OpenAPI.yml +++ b/testdata/styles/config/blueprints/OpenAPI.yml @@ -1,6 +1,6 @@ engine: dasel -steps: - - scope: title - query: info.title - - query: info.description - - query: servers.all().description +scopes: + - name: title + expr: info.title + - expr: info.description + - expr: servers.all().description diff --git a/testdata/styles/config/blueprints/Python.yml b/testdata/styles/config/blueprints/Python.yml index b3a32a92..6d817dfa 100644 --- a/testdata/styles/config/blueprints/Python.yml +++ b/testdata/styles/config/blueprints/Python.yml @@ -1,3 +1,3 @@ engine: tree-sitter -steps: - - query: (comment)+ @comment +scopes: + - expr: (comment)+ @comment diff --git a/testdata/styles/config/blueprints/Vale.yml b/testdata/styles/config/blueprints/Vale.yml index 6522e6e2..49c84b4d 100644 --- a/testdata/styles/config/blueprints/Vale.yml +++ b/testdata/styles/config/blueprints/Vale.yml @@ -1,3 +1,3 @@ engine: dasel -steps: - - query: description +scopes: + - expr: description From f65c0bfe0ef31762a2685dfaef5e43b40af3da79 Mon Sep 17 00:00:00 2001 From: Joseph Kato Date: Thu, 9 Jan 2025 13:25:49 -0800 Subject: [PATCH 13/15] refactor: support assigning markup type per-scope --- internal/core/blueprint.go | 2 ++ internal/core/file.go | 5 ++++ internal/lint/data.go | 23 ++++++++++--------- internal/lint/lint.go | 4 ++-- testdata/fixtures/blueprints/.vale.ini | 3 --- testdata/styles/config/blueprints/OpenAPI.yml | 4 ++++ 6 files changed, 25 insertions(+), 16 deletions(-) diff --git a/internal/core/blueprint.go b/internal/core/blueprint.go index 5d82bb12..ce63dec3 100644 --- a/internal/core/blueprint.go +++ b/internal/core/blueprint.go @@ -37,6 +37,7 @@ type Blueprint struct { // A ScopedValues is a value that has been assigned a scope. type ScopedValues struct { Scope string + Format string Values []string } @@ -89,6 +90,7 @@ func (b *Blueprint) Apply(f *File) ([]ScopedValues, error) { found = append(found, ScopedValues{ Scope: s.Name, Values: values, + Format: s.Type, }) } diff --git a/internal/core/file.go b/internal/core/file.go index ae181c10..cf572dbb 100755 --- a/internal/core/file.go +++ b/internal/core/file.go @@ -271,6 +271,11 @@ func (f *File) SetText(s string) { f.history = map[string]int{} } +// SetNormedExt sets the normalized extension of a File. +func (f *File) SetNormedExt(ext string) { + f.NormedExt = "." + ext +} + // AddAlert calculates the in-text location of an Alert and adds it to a File. func (f *File) AddAlert(a Alert, blk nlp.Block, lines, pad int, lookup bool) { ctx := blk.Context diff --git a/internal/lint/data.go b/internal/lint/data.go index 1ee0b3fc..6340a16f 100644 --- a/internal/lint/data.go +++ b/internal/lint/data.go @@ -38,28 +38,29 @@ func (l *Linter) lintScopedValues(f *core.File, values []core.ScopedValues) erro last := 0 index := 0 - for _, matches := range values { - l.SetMetaScope(matches.Scope) - for _, v := range matches.Values { + for _, match := range values { + l.SetMetaScope(match.Scope) + for _, v := range match.Values { i, line := findLineBySubstring(wholeFile, v, index) if i == 0 { return core.NewE100(f.Path, fmt.Errorf("'%s' not found", v)) } - index = i + f.SetText(v) + f.SetNormedExt(match.Format) - switch f.NormedExt { - case ".md": + switch match.Format { + case "md": err = l.lintMarkdown(f) - case ".rst": + case "rst": err = l.lintRST(f) - case ".xml": - err = l.lintADoc(f) - case ".html": + case "html": err = l.lintHTML(f) - case ".org": + case "org": err = l.lintOrg(f) + case "adoc": + err = l.lintADoc(f) default: err = l.lintLines(f) } diff --git a/internal/lint/lint.go b/internal/lint/lint.go index 52a7b430..01cb02ac 100755 --- a/internal/lint/lint.go +++ b/internal/lint/lint.go @@ -274,12 +274,12 @@ func (l *Linter) lintProse(f *core.File, blk nlp.Block, lines int) error { } func (l *Linter) lintTxt(f *core.File) error { - block := nlp.NewBlock("", f.Content, "text"+f.RealExt) + block := nlp.NewBlock("", f.Content, "text"+l.metaScope+f.RealExt) return l.lintProse(f, block, len(f.Lines)) } func (l *Linter) lintLines(f *core.File) error { - block := nlp.NewBlock("", f.Content, "text"+f.RealExt) + block := nlp.NewBlock("", f.Content, "text"+l.metaScope+f.RealExt) return l.lintBlock(f, block, len(f.Lines), 0, true) } diff --git a/testdata/fixtures/blueprints/.vale.ini b/testdata/fixtures/blueprints/.vale.ini index a18968a0..e943c220 100644 --- a/testdata/fixtures/blueprints/.vale.ini +++ b/testdata/fixtures/blueprints/.vale.ini @@ -1,9 +1,6 @@ StylesPath = ../../styles MinAlertLevel = suggestion -[formats] -yml = md - [*.py] vale.Annotations = YES diff --git a/testdata/styles/config/blueprints/OpenAPI.yml b/testdata/styles/config/blueprints/OpenAPI.yml index 6fef35de..608142b2 100644 --- a/testdata/styles/config/blueprints/OpenAPI.yml +++ b/testdata/styles/config/blueprints/OpenAPI.yml @@ -2,5 +2,9 @@ engine: dasel scopes: - name: title expr: info.title + - expr: info.description + type: md + - expr: servers.all().description + type: md From eeaa7e3fe15e72e17518c2eb1d1676d1e8b60bc4 Mon Sep 17 00:00:00 2001 From: Joseph Kato Date: Thu, 9 Jan 2025 16:51:05 -0800 Subject: [PATCH 14/15] feat: support custom scopes with `engine: tree-stitter` --- internal/check/definition.go | 7 ++++-- internal/lint/code.go | 14 +++++------ internal/lint/code/c.go | 9 +++++--- internal/lint/code/comments.go | 4 ++-- internal/lint/code/cpp.go | 3 ++- internal/lint/code/css.go | 3 ++- internal/lint/code/go.go | 3 ++- internal/lint/code/jl.go | 7 +++--- internal/lint/code/js.go | 3 ++- internal/lint/code/lang.go | 2 +- internal/lint/code/proto.go | 3 ++- internal/lint/code/py.go | 23 ++++++++++--------- internal/lint/code/query.go | 10 +++++--- internal/lint/code/rb.go | 3 ++- internal/lint/code/rs.go | 3 ++- internal/lint/code/ts.go | 3 ++- internal/lint/code/tsx.go | 3 ++- internal/lint/code/yaml.go | 3 ++- internal/lint/fragment.go | 1 + testdata/features/blueprints.feature | 3 ++- testdata/fixtures/blueprints/.vale.ini | 6 ++++- testdata/fixtures/blueprints/test.py | 5 ++-- testdata/styles/Scopes/Code.yml | 5 ++++ .../styles/{OpenAPI => Scopes}/Titles.yml | 0 testdata/styles/config/blueprints/Python.yml | 2 ++ 25 files changed, 82 insertions(+), 46 deletions(-) create mode 100644 testdata/styles/Scopes/Code.yml rename testdata/styles/{OpenAPI => Scopes}/Titles.yml (100%) diff --git a/internal/check/definition.go b/internal/check/definition.go index eea789df..03ff89c2 100755 --- a/internal/check/definition.go +++ b/internal/check/definition.go @@ -408,9 +408,12 @@ func checkScopes(scopes []string, path string) error { fmt.Sprintf("scope '%v' is no longer supported; use 'raw' instead.", scope), "scope", path) - } else if !core.StringInSlice(scope, allowedScopes) { + } + + // No spaces + if strings.Contains(scope, " ") { return core.NewE201FromTarget( - fmt.Sprintf("'%v' is not a valid scope; must be one of %v", scope, allowedScopes), + fmt.Sprintf("scope '%v' contains spaces.", scope), "scope", path) } diff --git a/internal/lint/code.go b/internal/lint/code.go index a2bcd0cc..56405540 100755 --- a/internal/lint/code.go +++ b/internal/lint/code.go @@ -13,20 +13,19 @@ import ( "github.com/errata-ai/vale/v3/internal/nlp" ) -func updateQueries(f *core.File, blueprints map[string]*core.Blueprint) ([]string, error) { +func updateQueries(f *core.File, blueprints map[string]*core.Blueprint) ([]core.Scope, error) { + var found []core.Scope + for syntax, blueprint := range blueprints { sec, err := glob.Compile(syntax) if err != nil { return nil, err } else if sec.Match(f.Path) { - found := []string{} - for _, query := range blueprint.Scopes { - found = append(found, query.Expr) - } - return found, nil + found = blueprint.Scopes } } - return []string{}, nil + + return found, nil } func (l *Linter) lintCode(f *core.File) error { @@ -52,6 +51,7 @@ func (l *Linter) lintCode(f *core.File) error { last := 0 for _, comment := range comments { + l.SetMetaScope(comment.Scope) if core.StringInSlice("comment", ignored) { continue } else if core.StringInSlice(comment.Scope, ignored) { diff --git a/internal/lint/code/c.go b/internal/lint/code/c.go index a57a3dd5..d14058ad 100644 --- a/internal/lint/code/c.go +++ b/internal/lint/code/c.go @@ -3,14 +3,17 @@ package code import ( "regexp" + "github.com/errata-ai/vale/v3/internal/core" "github.com/smacker/go-tree-sitter/c" ) func C() *Language { return &Language{ - Delims: regexp.MustCompile(`//|/\*|\*/`), - Parser: c.GetLanguage(), - Queries: []string{`(comment)+ @comment`}, + Delims: regexp.MustCompile(`//|/\*|\*/`), + Parser: c.GetLanguage(), + Queries: []core.Scope{ + {Name: "", Expr: "(comment) @comment", Type: ""}, + }, Padding: cStyle, } } diff --git a/internal/lint/code/comments.go b/internal/lint/code/comments.go index 90e1c694..9c45dd5b 100644 --- a/internal/lint/code/comments.go +++ b/internal/lint/code/comments.go @@ -110,11 +110,11 @@ func GetComments(source []byte, lang *Language) ([]Comment, error) { engine := NewQueryEngine(tree, lang) for _, query := range lang.Queries { - q, qErr := sitter.NewQuery([]byte(query), lang.Parser) + q, qErr := sitter.NewQuery([]byte(query.Expr), lang.Parser) if qErr != nil { return comments, qErr } - comments = append(comments, engine.run(q, source)...) + comments = append(comments, engine.run(query.Name, q, source)...) } if len(lang.Queries) > 1 { diff --git a/internal/lint/code/cpp.go b/internal/lint/code/cpp.go index 03bafbcb..01a79063 100644 --- a/internal/lint/code/cpp.go +++ b/internal/lint/code/cpp.go @@ -3,6 +3,7 @@ package code import ( "regexp" + "github.com/errata-ai/vale/v3/internal/core" "github.com/smacker/go-tree-sitter/cpp" ) @@ -10,7 +11,7 @@ func Cpp() *Language { return &Language{ Delims: regexp.MustCompile(`//|/\*!?|\*/`), Parser: cpp.GetLanguage(), - Queries: []string{`(comment)+ @comment`}, + Queries: []core.Scope{{Name: "", Expr: "(comment) @comment", Type: ""}}, Padding: cStyle, } } diff --git a/internal/lint/code/css.go b/internal/lint/code/css.go index bd9d7ea6..7cec2eb6 100644 --- a/internal/lint/code/css.go +++ b/internal/lint/code/css.go @@ -3,6 +3,7 @@ package code import ( "regexp" + "github.com/errata-ai/vale/v3/internal/core" "github.com/smacker/go-tree-sitter/css" ) @@ -10,7 +11,7 @@ func CSS() *Language { return &Language{ Delims: regexp.MustCompile(`/\*!?|\*/`), Parser: css.GetLanguage(), - Queries: []string{`(comment)+ @comment`}, + Queries: []core.Scope{{Name: "", Expr: "(comment) @comment", Type: ""}}, Padding: func(s string) int { return computePadding(s, []string{"/*"}) }, diff --git a/internal/lint/code/go.go b/internal/lint/code/go.go index 0f07a4d6..b7f46846 100644 --- a/internal/lint/code/go.go +++ b/internal/lint/code/go.go @@ -3,6 +3,7 @@ package code import ( "regexp" + "github.com/errata-ai/vale/v3/internal/core" "github.com/smacker/go-tree-sitter/golang" ) @@ -10,7 +11,7 @@ func Go() *Language { return &Language{ Delims: regexp.MustCompile(`//|/\*|\*/`), Parser: golang.GetLanguage(), - Queries: []string{`(comment)+ @comment`}, + Queries: []core.Scope{{Name: "", Expr: "(comment) @comment", Type: ""}}, Padding: cStyle, } } diff --git a/internal/lint/code/jl.go b/internal/lint/code/jl.go index ab2c3d41..9adc0daf 100644 --- a/internal/lint/code/jl.go +++ b/internal/lint/code/jl.go @@ -3,6 +3,7 @@ package code import ( "regexp" + "github.com/errata-ai/vale/v3/internal/core" "github.com/jdkato/go-tree-sitter-julia/julia" ) @@ -10,9 +11,9 @@ func Julia() *Language { return &Language{ Delims: regexp.MustCompile(`#|#=|=#`), Parser: julia.GetLanguage(), - Queries: []string{ - `(line_comment)+ @comment`, - `(block_comment)+ @comment`, + Queries: []core.Scope{ + {Name: "", Expr: "(line_comment)+ @comment", Type: ""}, + {Name: "", Expr: "(block_comment)+ @comment", Type: ""}, }, Padding: func(s string) int { return computePadding(s, []string{"#", `#=`, `=#`}) diff --git a/internal/lint/code/js.go b/internal/lint/code/js.go index 9c9e6275..9d23312d 100644 --- a/internal/lint/code/js.go +++ b/internal/lint/code/js.go @@ -3,6 +3,7 @@ package code import ( "regexp" + "github.com/errata-ai/vale/v3/internal/core" "github.com/smacker/go-tree-sitter/javascript" ) @@ -11,7 +12,7 @@ func JavaScript() *Language { Delims: regexp.MustCompile(`//|/\*\*?|\*/`), Parser: javascript.GetLanguage(), //Cutset: " *", - Queries: []string{`(comment)+ @comment`}, + Queries: []core.Scope{{Name: "", Expr: "(comment) @comment", Type: ""}}, Padding: cStyle, } } diff --git a/internal/lint/code/lang.go b/internal/lint/code/lang.go index 1ac3fe20..21a134d0 100644 --- a/internal/lint/code/lang.go +++ b/internal/lint/code/lang.go @@ -16,7 +16,7 @@ type padding func(string) int type Language struct { Delims *regexp.Regexp Parser *sitter.Language - Queries []string + Queries []core.Scope Cutset string Padding padding } diff --git a/internal/lint/code/proto.go b/internal/lint/code/proto.go index 34e62b40..e16781ce 100644 --- a/internal/lint/code/proto.go +++ b/internal/lint/code/proto.go @@ -3,6 +3,7 @@ package code import ( "regexp" + "github.com/errata-ai/vale/v3/internal/core" "github.com/smacker/go-tree-sitter/protobuf" ) @@ -10,7 +11,7 @@ func Protobuf() *Language { return &Language{ Delims: regexp.MustCompile(`//|/\*|\*/`), Parser: protobuf.GetLanguage(), - Queries: []string{`(comment)+ @comment`}, + Queries: []core.Scope{{Name: "", Expr: "(comment) @comment", Type: ""}}, Padding: cStyle, } } diff --git a/internal/lint/code/py.go b/internal/lint/code/py.go index 70e6eb7b..fff177e0 100644 --- a/internal/lint/code/py.go +++ b/internal/lint/code/py.go @@ -3,6 +3,7 @@ package code import ( "regexp" + "github.com/errata-ai/vale/v3/internal/core" "github.com/smacker/go-tree-sitter/python" ) @@ -10,19 +11,19 @@ func Python() *Language { return &Language{ Delims: regexp.MustCompile(`#|"""|'''`), Parser: python.GetLanguage(), - Queries: []string{ - `(comment)+ @comment`, - // Function docstring - `((function_definition + Queries: []core.Scope{ + {Name: "", Expr: `(comment)+ @comment`, Type: ""}, + // Function docstrings + {Name: "", Expr: `((function_definition body: (block . (expression_statement (string) @docstring))) - (#offset! @docstring 0 3 0 -3))`, - // Class docstring - `((class_definition + (#offset! @docstring 0 3 0 -3))`, Type: ""}, + // Class docstrings + {Name: "", Expr: `((class_definition body: (block . (expression_statement (string) @docstring))) - (#offset! @docstring 0 3 0 -3))`, - // Module docstring - `((module . (expression_statement (string) @docstring)) - (#offset! @docstring 0 3 0 -3))`, + (#offset! @docstring 0 3 0 -3))`, Type: ""}, + // Module docstrings + {Name: "", Expr: `((module . (expression_statement (string) @docstring)) + (#offset! @docstring 0 3 0 -3))`, Type: ""}, }, Padding: func(s string) int { return computePadding(s, []string{"#", `"""`, "'''"}) diff --git a/internal/lint/code/query.go b/internal/lint/code/query.go index 7afa18f6..f10ca4a1 100644 --- a/internal/lint/code/query.go +++ b/internal/lint/code/query.go @@ -26,9 +26,13 @@ func NewQueryEngine(tree *sitter.Tree, lang *Language) *QueryEngine { } } -func (qe *QueryEngine) run(q *sitter.Query, source []byte) []Comment { +func (qe *QueryEngine) run(meta string, q *sitter.Query, source []byte) []Comment { var comments []Comment + if meta != "" { + meta = "." + meta + } + qc := sitter.NewQueryCursor() qc.Exec(q, qe.tree.RootNode()) @@ -43,9 +47,9 @@ func (qe *QueryEngine) run(q *sitter.Query, source []byte) []Comment { rText := c.Node.Content(source) cText := qe.lang.Delims.ReplaceAllString(rText, "") - scope := "text.comment.line" + scope := "text.comment" + meta + ".line" if strings.Count(cText, "\n") > 1 { - scope = "text.comment.block" + scope = "text.comment" + meta + ".block" buf := bytes.Buffer{} for _, line := range strings.Split(cText, "\n") { diff --git a/internal/lint/code/rb.go b/internal/lint/code/rb.go index 46f89d98..ffc8caa3 100644 --- a/internal/lint/code/rb.go +++ b/internal/lint/code/rb.go @@ -3,6 +3,7 @@ package code import ( "regexp" + "github.com/errata-ai/vale/v3/internal/core" "github.com/smacker/go-tree-sitter/ruby" ) @@ -10,7 +11,7 @@ func Ruby() *Language { return &Language{ Delims: regexp.MustCompile(`#|=begin|=end`), Parser: ruby.GetLanguage(), - Queries: []string{`(comment)+ @comment`}, + Queries: []core.Scope{{Name: "", Expr: "(comment) @comment", Type: ""}}, Padding: func(s string) int { return computePadding(s, []string{"#", `=begin`, `=end`}) }, diff --git a/internal/lint/code/rs.go b/internal/lint/code/rs.go index 0f4e2ecc..e131fdb3 100644 --- a/internal/lint/code/rs.go +++ b/internal/lint/code/rs.go @@ -3,6 +3,7 @@ package code import ( "regexp" + "github.com/errata-ai/vale/v3/internal/core" "github.com/smacker/go-tree-sitter/rust" ) @@ -10,7 +11,7 @@ func Rust() *Language { return &Language{ Delims: regexp.MustCompile(`/{2,3}!?`), Parser: rust.GetLanguage(), - Queries: []string{`(line_comment)+ @comment`}, + Queries: []core.Scope{{Name: "", Expr: `(line_comment)+ @comment`, Type: ""}}, Padding: func(s string) int { return computePadding(s, []string{"//", "//!", "///"}) }, diff --git a/internal/lint/code/ts.go b/internal/lint/code/ts.go index f7c09504..0a699de2 100644 --- a/internal/lint/code/ts.go +++ b/internal/lint/code/ts.go @@ -3,6 +3,7 @@ package code import ( "regexp" + "github.com/errata-ai/vale/v3/internal/core" "github.com/smacker/go-tree-sitter/typescript/typescript" ) @@ -10,7 +11,7 @@ func TypeScript() *Language { return &Language{ Delims: regexp.MustCompile(`//|/\*|\*/`), Parser: typescript.GetLanguage(), - Queries: []string{`(comment)+ @comment`}, + Queries: []core.Scope{{Name: "", Expr: "(comment) @comment", Type: ""}}, Padding: cStyle, } } diff --git a/internal/lint/code/tsx.go b/internal/lint/code/tsx.go index ff7f60e3..e991349a 100644 --- a/internal/lint/code/tsx.go +++ b/internal/lint/code/tsx.go @@ -3,6 +3,7 @@ package code import ( "regexp" + "github.com/errata-ai/vale/v3/internal/core" "github.com/smacker/go-tree-sitter/typescript/tsx" ) @@ -10,7 +11,7 @@ func Tsx() *Language { return &Language{ Delims: regexp.MustCompile(`//|/\*|\*/`), Parser: tsx.GetLanguage(), - Queries: []string{`(comment)+ @comment`}, + Queries: []core.Scope{{Name: "", Expr: "(comment) @comment", Type: ""}}, Padding: cStyle, } } diff --git a/internal/lint/code/yaml.go b/internal/lint/code/yaml.go index 5686a43c..955e7f81 100644 --- a/internal/lint/code/yaml.go +++ b/internal/lint/code/yaml.go @@ -3,6 +3,7 @@ package code import ( "regexp" + "github.com/errata-ai/vale/v3/internal/core" "github.com/smacker/go-tree-sitter/yaml" ) @@ -10,7 +11,7 @@ func YAML() *Language { return &Language{ Delims: regexp.MustCompile(`#`), Parser: yaml.GetLanguage(), - Queries: []string{`(comment)+ @comment`}, + Queries: []core.Scope{{Name: "", Expr: "(comment) @comment", Type: ""}}, Padding: func(s string) int { return computePadding(s, []string{"#"}) }, diff --git a/internal/lint/fragment.go b/internal/lint/fragment.go index 50c7e230..13f498d9 100644 --- a/internal/lint/fragment.go +++ b/internal/lint/fragment.go @@ -72,6 +72,7 @@ func (l *Linter) lintFragments(f *core.File) error { last := 0 for _, comment := range comments { + l.SetMetaScope(comment.Scope) f.SetText(comment.Text) switch f.NormedExt { diff --git a/testdata/features/blueprints.feature b/testdata/features/blueprints.feature index 9accfbf4..8248261b 100644 --- a/testdata/features/blueprints.feature +++ b/testdata/features/blueprints.feature @@ -3,12 +3,13 @@ Feature: Blueprints When I test "blueprints" Then the output should contain exactly: """ - API.yml:3:10:OpenAPI.Titles:'sample API' should be capitalized + API.yml:3:10:Scopes.Titles:'sample API' should be capitalized API.yml:4:25:Vale.Spelling:Did you really mean 'multiline'? API.yml:9:70:Vale.Spelling:Did you really mean 'serrver'? API.yml:13:17:Vale.Spelling:Did you really mean 'serrver'? API.yml:15:70:Vale.Spelling:Did you really mean 'serrver'? Rule.yml:3:39:Vale.Repetition:'can' is repeated! + test.py:1:3:Scopes.Code:'FIXME' should not be capitalized test.py:1:3:vale.Annotations:'FIXME' left in text test.py:11:3:vale.Annotations:'XXX' left in text test.py:13:16:vale.Annotations:'XXX' left in text diff --git a/testdata/fixtures/blueprints/.vale.ini b/testdata/fixtures/blueprints/.vale.ini index e943c220..8ac724bc 100644 --- a/testdata/fixtures/blueprints/.vale.ini +++ b/testdata/fixtures/blueprints/.vale.ini @@ -1,13 +1,17 @@ StylesPath = ../../styles MinAlertLevel = suggestion +[formats] +py = md + [*.py] vale.Annotations = YES +Scopes.Code = YES Blueprint = Python [*.yml] -BasedOnStyles = Vale, OpenAPI +BasedOnStyles = Vale, Scopes [API.yml] Blueprint = OpenAPI diff --git a/testdata/fixtures/blueprints/test.py b/testdata/fixtures/blueprints/test.py index de59a786..e3cb95c0 100644 --- a/testdata/fixtures/blueprints/test.py +++ b/testdata/fixtures/blueprints/test.py @@ -18,8 +18,6 @@ def FIXME(): - - XXX: """ @@ -38,3 +36,6 @@ def foo(self): 2616, section 14.25. GMT """ invalid_date = 'FIXME: Mon, 28 May 999999999999 28:25:26 GMT' + + +# `FIXME`: diff --git a/testdata/styles/Scopes/Code.yml b/testdata/styles/Scopes/Code.yml new file mode 100644 index 00000000..3adfae37 --- /dev/null +++ b/testdata/styles/Scopes/Code.yml @@ -0,0 +1,5 @@ +extends: existence +message: "'%s' should not be capitalized" +scope: python +raw: + - FIXME diff --git a/testdata/styles/OpenAPI/Titles.yml b/testdata/styles/Scopes/Titles.yml similarity index 100% rename from testdata/styles/OpenAPI/Titles.yml rename to testdata/styles/Scopes/Titles.yml diff --git a/testdata/styles/config/blueprints/Python.yml b/testdata/styles/config/blueprints/Python.yml index 6d817dfa..277b4e0f 100644 --- a/testdata/styles/config/blueprints/Python.yml +++ b/testdata/styles/config/blueprints/Python.yml @@ -1,3 +1,5 @@ engine: tree-sitter scopes: - expr: (comment)+ @comment + name: python + type: md From c58806ed505145f688a3e129824b6cd5274c0fec Mon Sep 17 00:00:00 2001 From: Joseph Kato Date: Thu, 9 Jan 2025 16:53:14 -0800 Subject: [PATCH 15/15] refactor: remove `allowedScopes` --- internal/check/scope.go | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/internal/check/scope.go b/internal/check/scope.go index 81e6606d..1c55f6fa 100644 --- a/internal/check/scope.go +++ b/internal/check/scope.go @@ -7,30 +7,6 @@ import ( "github.com/errata-ai/vale/v3/internal/nlp" ) -var allowedScopes = []string{ - "text", - "heading", - "heading.h1", - "heading.h2", - "heading.h3", - "heading.h4", - "heading.h5", - "heading.h6", - "table", - "table.header", - "table.cell", - "table.caption", - "figure.caption", - "list", - "paragraph", - "sentence", - "alt", - "title", - "blockquote", - "summary", - "raw", -} - // A Selector represents a named section of text. type Selector struct { Value []string // e.g., text.comment.line.py