Skip to content

Commit 802f045

Browse files
committed
fix(ini): expand paths during load
Expand INI values known to be paths during initial loading. This prevents having to guess where paths came from in later processing. Signed-off-by: Randolph Sapp <rs@ti.com>
1 parent 831ded9 commit 802f045

1 file changed

Lines changed: 78 additions & 30 deletions

File tree

internal/core/ini.go

Lines changed: 78 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"errors"
55
"fmt"
66
"io/fs"
7+
"os"
78
"path/filepath"
89
"strings"
910

@@ -13,6 +14,10 @@ import (
1314
"github.com/errata-ai/vale/v3/internal/system"
1415
)
1516

17+
var pathKeys = []string{
18+
"StylesPath",
19+
}
20+
1621
var coreError = "'%s' is a core option; it should be defined above any syntax-specific options (`[...]`)."
1722

1823
func mergeValues(shadows []string) []string {
@@ -161,40 +166,15 @@ var globalOpts = map[string]func(*ini.Section, *Config){
161166

162167
var coreOpts = map[string]func(*ini.Section, *Config) error{
163168
"StylesPath": func(sec *ini.Section, cfg *Config) error {
164-
// NOTE: The order of these paths is important. They represent the load
165-
// order of the configuration files -- not `cfg.Paths`.
166169
paths := sec.Key("StylesPath").ValueWithShadows()
167-
files := cfg.ConfigFiles
168-
if cfg.Flags.Local && len(files) == 2 {
169-
// This represents the case where we have a default `.vale.ini`
170-
// file and a local `.vale.ini` file.
171-
//
172-
// In such a case, there are three options: (1) both files define a
173-
// `StylesPath`, (2) only one file defines a `StylesPath`, or (3)
174-
// neither file defines a `StylesPath`.
175-
basePath := system.DeterminePath(files[0], filepath.FromSlash(paths[0]))
176-
mockPath := system.DeterminePath(files[1], filepath.FromSlash(paths[0]))
177-
// ^ This case handles the situation where both configs define the
178-
// same StylesPath (e.g., `StylesPath = styles`).
179-
if len(paths) == 2 {
180-
basePath = system.DeterminePath(files[0], filepath.FromSlash(paths[0]))
181-
mockPath = system.DeterminePath(files[1], filepath.FromSlash(paths[1]))
182-
}
183-
cfg.AddStylesPath(basePath)
184-
cfg.AddStylesPath(mockPath)
185-
} else if len(paths) > 0 {
186-
// In this case, we have a local configuration file (no default)
187-
// that defines a `StylesPath`.
188-
candidate := filepath.FromSlash(paths[len(paths)-1])
189-
path := system.DeterminePath(cfg.ConfigFile(), candidate)
190-
191-
cfg.AddStylesPath(path)
170+
for _, path := range paths {
192171
if !system.FileExists(path) {
193172
return NewE201FromTarget(
194173
fmt.Sprintf("The path '%s' does not exist.", path),
195-
candidate,
174+
path,
196175
cfg.Flags.Path)
197176
}
177+
cfg.AddStylesPath(path)
198178
}
199179
return nil
200180
},
@@ -255,10 +235,78 @@ var coreOpts = map[string]func(*ini.Section, *Config) error{
255235
},
256236
}
257237

238+
func expandPaths(file *ini.File, source interface{}) {
239+
var path string
240+
241+
switch s := source.(type) {
242+
case string:
243+
abs, _ := filepath.Abs(s)
244+
path = filepath.Dir(abs)
245+
default:
246+
path, _ = os.Getwd()
247+
}
248+
249+
for _, section := range file.Sections() {
250+
for _, key := range section.Keys() {
251+
if StringInSlice(key.Name(), pathKeys) {
252+
value := key.Value()
253+
if !filepath.IsAbs(value) {
254+
key.SetValue(filepath.Join(path, value))
255+
}
256+
}
257+
}
258+
}
259+
}
260+
261+
func shadowMerge(primary *ini.File, secondary *ini.File) {
262+
for _, secondarySection := range secondary.Sections() {
263+
sectionName := secondarySection.Name()
264+
265+
primarySection, _ := primary.GetSection(sectionName)
266+
if primarySection == nil {
267+
primarySection, _ = primary.NewSection(sectionName)
268+
}
269+
270+
for _, secondaryKey := range secondarySection.Keys() {
271+
keyName := secondaryKey.Name()
272+
keyValue := secondaryKey.Value()
273+
274+
primaryKey, _ := primarySection.GetKey(keyName)
275+
if primaryKey == nil {
276+
primarySection.NewKey(keyName, keyValue)
277+
} else {
278+
primaryKey.AddShadow(keyValue)
279+
}
280+
}
281+
}
282+
}
283+
258284
func shadowLoad(source interface{}, others ...interface{}) (*ini.File, error) {
259-
return ini.LoadSources(ini.LoadOptions{
285+
options := ini.LoadOptions{
260286
AllowShadows: true,
261-
SpaceBeforeInlineComment: true}, source, others...)
287+
Loose: true,
288+
SpaceBeforeInlineComment: true,
289+
}
290+
291+
primary, err := ini.LoadSources(options, source)
292+
if err != nil {
293+
return nil, err
294+
}
295+
expandPaths(primary, source)
296+
297+
for _, other := range others {
298+
var shadow *ini.File
299+
300+
shadow, err = ini.LoadSources(options, other)
301+
if err != nil {
302+
return nil, err
303+
}
304+
305+
expandPaths(shadow, other)
306+
shadowMerge(primary, shadow)
307+
}
308+
309+
return primary, nil
262310
}
263311

264312
func processSources(cfg *Config, sources []string) (*ini.File, error) {

0 commit comments

Comments
 (0)