Skip to content

Commit

Permalink
refactor: use actual path for assigned formats
Browse files Browse the repository at this point in the history
  • Loading branch information
jdkato committed Mar 1, 2024
1 parent 92d50d4 commit cbc6e2d
Show file tree
Hide file tree
Showing 16 changed files with 146 additions and 46 deletions.
7 changes: 6 additions & 1 deletion cmd/vale/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,12 @@ func transform(args []string, flags *core.CLIFlags) error {
return err
}

out, err := linter.Transform(args[0])
f, err := core.NewFile(args[0], cfg)
if err != nil {
return err
}

out, err := linter.Transform(f)
if err != nil {
return err
}
Expand Down
49 changes: 34 additions & 15 deletions internal/core/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,25 +62,44 @@ func NewFile(src string, config *Config) (*File, error) {
src = "stdin" + config.Flags.InExt
lookup = true
}
filepaths := []string{src}

fp := src
old := filepath.Ext(fp)
if normed, found := config.Formats[strings.Trim(old, ".")]; found {
fp = fp[0:len(fp)-len(old)] + "." + normed
normed := ReplaceExt(src, config.Formats)
if normed != src {
// NOTE: In retrospect, this was a mistake: we should NOT normalize
// the extension with respect to the `.vale.ini` file.
//
// The `.vale.ini` file should reflect the actual file extensions (as
// they appear on disk). Unfortunately, changing this behavior entirely
// would break backwards compatibility with many configurations.
//
// So, as a workaround, we check both cases. This means that there are
// two cases:
//
// - No assigned format: No change (no normed path).
//
// - Assigned format: We can reference the file using the normed path
// (old behavior) or the actual path (desired behavior).
//
// See also `Linter.skip`.
filepaths = append(filepaths, normed)
}

baseStyles := config.GBaseStyles
for _, sec := range config.StyleKeys {
if pat, found := config.SecToPat[sec]; found && pat.Match(fp) {
baseStyles = config.SBaseStyles[sec]
checks := make(map[string]bool)

for _, fp := range filepaths {
for _, sec := range config.StyleKeys {
if pat, found := config.SecToPat[sec]; found && pat.Match(fp) {
baseStyles = config.SBaseStyles[sec]
}
}
}

checks := make(map[string]bool)
for _, sec := range config.RuleKeys {
if pat, found := config.SecToPat[sec]; found && pat.Match(fp) {
for k, v := range config.SChecks[sec] {
checks[k] = v
for _, sec := range config.RuleKeys {
if pat, found := config.SecToPat[sec]; found && pat.Match(fp) {
for k, v := range config.SChecks[sec] {
checks[k] = v
}
}
}
}
Expand All @@ -90,7 +109,7 @@ func NewFile(src string, config *Config) (*File, error) {
sec, err := glob.Compile(syntax)
if err != nil {
return &File{}, err
} else if sec.Match(fp) {
} else if sec.Match(src) {
lang = code
break
}
Expand Down Expand Up @@ -121,7 +140,7 @@ func NewFile(src string, config *Config) (*File, error) {
simple: config.Flags.Simple, Transform: transform,
limits: make(map[string]int), Path: src, Metrics: make(map[string]int),
NLP: nlp.Info{Endpoint: config.NLPEndpoint, Lang: lang},
Lookup: lookup, NormedPath: ReplaceExt(src, config.Formats),
Lookup: lookup, NormedPath: normed,
}

return &file, nil
Expand Down
2 changes: 1 addition & 1 deletion internal/core/format.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ func FormatFromExt(path string, mapping map[string]string) (string, string) {
if format, found := mapping[base]; found {
if kind == "code" && getFormat("."+format) == "markup" {
// NOTE: This is a special case of embedded markup within code.
return format, "fragment"
return "." + format, "fragment"
}
base = format
}
Expand Down
11 changes: 11 additions & 0 deletions internal/glob/glob.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,17 @@ func (g Glob) Match(query string) bool {
return p.Match(q) != g.Negated
}

// MatchAny returns whether or not the Glob g matches any of the strings in
// query.
func (g Glob) MatchAny(query []string) bool {
for _, q := range query {
if g.Match(q) {
return true
}
}
return false
}

// NewGlob creates a Glob from the string pat.
func NewGlob(pat string) (Glob, error) {
negate := false
Expand Down
2 changes: 1 addition & 1 deletion internal/lint/adoc.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ func (l *Linter) lintADoc(f *core.File) error {
return core.NewE100("lintAdoc", errors.New("asciidoctor not found"))
}

s, err := l.applyPatterns(f.Content, "\n----\n$1\n----\n", "`$1`", f.NormedPath)
s, err := l.Transform(f)
if err != nil {
return err
}
Expand Down
11 changes: 8 additions & 3 deletions internal/lint/fragment.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package lint

import (
"bytes"
"fmt"

"github.com/errata-ai/vale/v3/internal/core"
)
Expand Down Expand Up @@ -59,12 +60,16 @@ func (l *Linter) lintFragments(f *core.File) error {
f.SetText(comment.Text)

switch f.NormedExt {
case "md":
case ".md":
err = l.lintMarkdown(f)
case "rst":
case ".rst":
err = l.lintRST(f)
case "adoc":
case ".adoc":
err = l.lintADoc(f)
case ".org":
err = l.lintOrg(f)
default:
return fmt.Errorf("unsupported markup format '%s'", f.NormedExt)
}

size := len(f.Alerts)
Expand Down
12 changes: 7 additions & 5 deletions internal/lint/html.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,16 @@ func (l *Linter) lintHTML(f *core.File) error {
return l.lintHTMLTokens(f, []byte(f.Content), 0)
}

func (l *Linter) applyPatterns(content, block, inline, ext string) (string, error) {
s := reFrontMatter.ReplaceAllString(content, block)
func (l *Linter) applyPatterns(f *core.File, block, inline string) (string, error) {
// TODO: Should we assume this?
s := reFrontMatter.ReplaceAllString(f.Content, block)

exts := []string{f.NormedExt, f.RealExt}
for syntax, regexes := range l.Manager.Config.BlockIgnores {
sec, err := glob.Compile(syntax)
if err != nil {
return s, err
} else if sec.Match(ext) {
} else if sec.MatchAny(exts) {
for _, r := range regexes {
pat, errc := regexp2.CompileStd(r)
if errc != nil { //nolint:gocritic
Expand All @@ -47,7 +49,7 @@ func (l *Linter) applyPatterns(content, block, inline, ext string) (string, erro
r,
l.Manager.Config.Flags.Path,
)
} else if strings.HasSuffix(ext, ".rst") {
} else if strings.HasSuffix(f.NormedExt, ".rst") {
// HACK: We need to add padding for the literal block.
for _, c := range pat.FindAllStringSubmatch(s, -1) {
sec := fmt.Sprintf(block, core.Indent(c[0], " "))
Expand All @@ -71,7 +73,7 @@ func (l *Linter) applyPatterns(content, block, inline, ext string) (string, erro
sec, err := glob.Compile(syntax)
if err != nil {
return s, err
} else if sec.Match(ext) {
} else if sec.MatchAny(exts) {
for _, r := range regexes {
pat, errc := regexp2.CompileStd(r)
if errc != nil {
Expand Down
23 changes: 9 additions & 14 deletions internal/lint/lint.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,21 +55,16 @@ func NewLinter(cfg *core.Config) (*Linter, error) {
//
// Transformations include block and token ignores, as well as some built-in
// replacements.
func (l *Linter) Transform(path string) (string, error) {
f, err := core.NewFile(path, l.Manager.Config)
if err != nil {
return "", err
}

func (l *Linter) Transform(f *core.File) (string, error) {
switch f.NormedExt {
case ".adoc":
return l.applyPatterns(f.Content, "\n----\n$1\n----\n", "`$1`", f.NormedPath)
return l.applyPatterns(f, "\n----\n$1\n----\n", "`$1`")
case ".md":
return l.applyPatterns(f.Content, "\n```\n$1\n```\n", "`$1`", f.NormedPath)
return l.applyPatterns(f, "\n```\n$1\n```\n", "`$1`")
case ".rst":
return l.applyPatterns(f.Content, "\n::\n\n%s\n", "``$1``", f.NormedPath)
return l.applyPatterns(f, "\n::\n\n%s\n", "``$1``")
case ".org":
return l.applyPatterns(f.Content, orgExample, "=$1=", f.NormedPath)
return l.applyPatterns(f, orgExample, "=$1=")
default:
return f.Content, fmt.Errorf("ignore patterns are not supported in '%s' files", f.NormedExt)
}
Expand Down Expand Up @@ -375,14 +370,14 @@ func (l *Linter) match(s string) bool {
return l.glob.Match(s)
}

func (l *Linter) skip(fp string) bool {
fp = filepath.ToSlash(core.ReplaceExt(fp, l.Manager.Config.Formats))
func (l *Linter) skip(old string) bool {
ref := filepath.ToSlash(core.ReplaceExt(old, l.Manager.Config.Formats))

if !l.match(fp) {
if !l.match(old) && !l.match(ref) {
return true
} else if l.nonGlobal {
for _, pat := range l.Manager.Config.SecToPat {
if pat.Match(fp) {
if pat.Match(old) || pat.Match(ref) {
return false
}
}
Expand Down
2 changes: 1 addition & 1 deletion internal/lint/md.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ var reLinkDef = regexp.MustCompile(`\[(?:[^]]+)\]:`)
func (l Linter) lintMarkdown(f *core.File) error {
var buf bytes.Buffer

s, err := l.applyPatterns(f.Content, "\n```\n$1\n```\n", "`$1`", f.NormedPath)
s, err := l.Transform(f)
if err != nil {
return err
}
Expand Down
6 changes: 5 additions & 1 deletion internal/lint/org.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,17 @@ func (l Linter) lintOrg(f *core.File) error {
extendedWriter := &ExtendedHTMLWriter{orgWriter}
orgWriter.ExtendingWriter = extendedWriter

old := f.Content

s := reOrgAttribute.ReplaceAllString(f.Content, "\n=$1=\n")
s = reOrgProps.ReplaceAllString(s, orgExample)

s, err := l.applyPatterns(s, orgExample, "=$1=", f.NormedPath)
f.Content = s
s, err := l.Transform(f)
if err != nil {
return err
}
f.Content = old

// We don't want to find matches in `begin_src` lines.
body := reOrgSrc.ReplaceAllStringFunc(f.Content, func(m string) string {
Expand Down
2 changes: 1 addition & 1 deletion internal/lint/rst.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ func (l *Linter) lintRST(f *core.File) error {
return core.NewE100("lintRST", errors.New("rst2html not found"))
}

s, err := l.applyPatterns(f.Content, "\n::\n\n%s\n", "``$1``", f.NormedPath)
s, err := l.Transform(f)
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion testdata/features/config.feature
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@ Feature: Config
"""

Scenario: Test another negated glob
When I test glob "!**/*.{md,py}"
When I test glob "!**/*.{md,py,mdx}"
Then the output should not contain "md"
And the output should not contain "py"

Expand Down
5 changes: 4 additions & 1 deletion testdata/features/fragments.feature
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ Feature: Fragments
When I test "fragments"
Then the output should contain exactly:
"""
test.cc:6:10:Vale.Spelling:Did you really mean 'objectmodel'?
test.cc:14:13:Vale.Repetition:'the' is repeated!
test.cc:47:12:Vale.Repetition:'the' is repeated!
test.go:2:14:Vale.Spelling:Did you really mean 'implments'?
test.go:10:4:Vale.Spelling:Did you really mean 'Println'?
test.go:12:4:Vale.Spelling:Did you really mean 'Println'?
Expand Down Expand Up @@ -40,4 +43,4 @@ Feature: Fragments
test2.py:252:36:Vale.Spelling:Did you really mean 'vaiable'?
test2.rs:47:42:Vale.Spelling:Did you really mean 'vrsion'?
test2.rs:2860:34:Vale.Spelling:Did you really mean 'conlicts'?
"""
"""
6 changes: 5 additions & 1 deletion testdata/fixtures/fragments/.vale.ini
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ py = md
rs = md
go = md
rb = md
cc = md

[*.md]
[*.{py,rs,go,rb,cc}]
BasedOnStyles = Vale

[*.cc]
TokenIgnores = (\\c \w+)
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ docstring
env
pdoc(?:'s)?
ripgrep(?:'s)?
QObject

51 changes: 51 additions & 0 deletions testdata/fixtures/fragments/test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@

/*!
\class QObject
\brief The QObject class is the base class of all Qt objects.
\ingroup objectmodel
\reentrant
QObject is the heart of the Qt \l{Object Model}. The
central feature in this model is a very powerful mechanism
for seamless object communication called \l{signals and
slots}. You can connect a signal to a slot with connect()
and destroy the the connection with disconnect(). To avoid
never ending notification loops you can temporarily block
signals with blockSignals(). The protected functions
connectNotify() and disconnectNotify() make it possible to
track connections.
QObjects organize themselves in \l {Object Trees &
Ownership} {object trees}. When you create a QObject with
another object as parent, the object will automatically
add itself to the parent's \c children() list. The parent
takes ownership of the object. It will automatically
delete its children in its destructor. You can look for an
object by name and optionally type using findChild() or
findChildren().
Every object has an objectName() and its class name can be
found via the corresponding metaObject() (see
QMetaObject::className()). You can determine whether the
object's class inherits another class in the QObject
inheritance hierarchy by using the \c inherits() function.
*/

#include <fstream>
#include <iostream>
using namespace std;

int main() {
// Create and open a text file
ofstream MyFile("filename.txt");

// Write to the file
MyFile << "Files can be tricky, but it is fun enough!";

// Close the the file
MyFile.close();

// Call \c func to start the process.
}

0 comments on commit cbc6e2d

Please sign in to comment.