Skip to content

Commit 231f385

Browse files
committed
MINOR: add identifier_scope configuration option
Introduce the `identifier_scope` setting to control where code identifiers are collected from to exclude as allowed words. The available scopes are: - `diff`: only from the diff content (added lines) - `files`: from the full content of changed files (new default) - `all`: from every file in the repository Previously, identifiers were only collected from the diff content. By defaulting to the full content of changed files, this improves spell checking accuracy by ensuring valid code identifiers are properly ignored even if they are not directly modified in the diff.
1 parent 432f65c commit 231f385

File tree

5 files changed

+80
-12
lines changed

5 files changed

+80
-12
lines changed

.aspell.yml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,15 @@ mode: all
66
## Minimum word length to spell-check (words shorter than this are skipped, default: 3)
77
# min_length: 3
88

9-
## If true, code identifiers found in the diff will NOT be excluded from spell check
9+
## If true, code identifiers will NOT be excluded from spell check
1010
# no_ignore_identifiers: false
1111

12+
## Where to collect code identifiers from to exclude as allowed words (default: files)
13+
## diff - only from the diff content (added lines)
14+
## files - from the full content of changed files
15+
## all - from every file in the repository
16+
# identifier_scope: diff | files | all
17+
1218
## File patterns to skip when mode is "all" (glob syntax)
1319
ignore_files:
1420
- '*test.go'

aspell/aspell.go

Lines changed: 55 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ import (
44
"bytes"
55
"fmt"
66
"log"
7+
"os"
78
"os/exec"
9+
"path/filepath"
810
"regexp"
911
"slices"
1012
"strings"
@@ -25,9 +27,10 @@ type RemoteFile struct {
2527

2628
type Aspell struct {
2729
RemoteFile RemoteFile `yaml:"remote_file"`
28-
Dictionaries DictionaryConfig `yaml:"dictionaries"`
2930
Mode mode `yaml:"mode"`
3031
HelpText string `yaml:"-"`
32+
IdentifierScope identifierScope `yaml:"identifier_scope"`
33+
Dictionaries DictionaryConfig `yaml:"dictionaries"`
3134
IgnoreFiles []string `yaml:"ignore_files"`
3235
AllowedWords []string `yaml:"allowed"`
3336
ExtraDicts []string `yaml:"-"` // paths to downloaded .rws files for aspell --extra-dicts
@@ -169,20 +172,63 @@ func (a Aspell) collectIdentifiers(content []map[string]string) []string {
169172
if a.NoIgnoreIdentifiers {
170173
return nil
171174
}
172-
var identifierWords []string
173175
seen := map[string]struct{}{}
174-
for _, file := range content {
175-
for name, v := range file {
176-
for _, word := range match.GetIdentifiersFromContent(name, v) {
177-
if _, ok := seen[word]; !ok {
178-
seen[word] = struct{}{}
179-
identifierWords = append(identifierWords, word)
176+
addWords := func(name, data string) {
177+
for _, word := range match.GetIdentifiersFromContent(name, data) {
178+
if _, ok := seen[word]; !ok {
179+
seen[word] = struct{}{}
180+
}
181+
}
182+
}
183+
184+
switch a.IdentifierScope {
185+
case identifierScopeDiff:
186+
for _, file := range content {
187+
for name, v := range file {
188+
addWords(name, v)
189+
}
190+
}
191+
case identifierScopeFiles, "":
192+
// Read full file content for each changed file
193+
for _, file := range content {
194+
for name := range file {
195+
data, err := os.ReadFile(name)
196+
if err != nil {
197+
log.Printf("aspell: could not read file %s for identifiers, using diff: %v", name, err)
198+
addWords(name, file[name])
199+
continue
180200
}
201+
addWords(name, string(data))
181202
}
182203
}
204+
case identifierScopeAll:
205+
// Collect from all files in the repo
206+
_ = filepath.WalkDir(".", func(path string, d os.DirEntry, err error) error {
207+
if err != nil {
208+
return nil
209+
}
210+
if d.IsDir() {
211+
base := d.Name()
212+
if base == ".git" || base == "vendor" || base == "node_modules" {
213+
return filepath.SkipDir
214+
}
215+
return nil
216+
}
217+
data, err := os.ReadFile(path)
218+
if err != nil {
219+
return nil
220+
}
221+
addWords(path, string(data))
222+
return nil
223+
})
224+
}
225+
226+
identifierWords := make([]string, 0, len(seen))
227+
for w := range seen {
228+
identifierWords = append(identifierWords, w)
183229
}
184230
if len(identifierWords) > 0 {
185-
log.Printf("collected %d identifiers from diff content for spell check filtering", len(identifierWords))
231+
log.Printf("collected %d identifiers (scope: %s) for spell check filtering", len(identifierWords), a.IdentifierScope)
186232
}
187233
return identifierWords
188234
}

aspell/fetch_dictionaries.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -285,10 +285,10 @@ func fetchSingleFile(url, name, tokenEnv string) (fetchedDictionaries, error) {
285285
return result, fmt.Errorf("creating temp file for %s: %w", name, err)
286286
}
287287
if _, err := tmpFile.Write(data); err != nil {
288-
tmpFile.Close()
288+
_ = tmpFile.Close()
289289
return result, fmt.Errorf("writing temp file for %s: %w", name, err)
290290
}
291-
tmpFile.Close()
291+
_ = tmpFile.Close()
292292
result.rwsFiles = append(result.rwsFiles, tmpFile.Name())
293293
log.Printf("aspell dictionaries: saved .rws dictionary %s to %s", name, tmpFile.Name())
294294
case strings.HasSuffix(lower, ".txt"):

aspell/mode.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,11 @@ const (
88
modeCommit mode = "commit"
99
modeAll mode = "all"
1010
)
11+
12+
type identifierScope string
13+
14+
const (
15+
identifierScopeDiff identifierScope = "diff"
16+
identifierScopeFiles identifierScope = "files"
17+
identifierScopeAll identifierScope = "all"
18+
)

aspell/new.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,14 @@ func New(filename string) (Aspell, error) {
6666
return Aspell{}, fmt.Errorf("invalid mode: %s", aspell.Mode)
6767
}
6868

69+
switch aspell.IdentifierScope {
70+
case identifierScopeDiff, identifierScopeFiles, identifierScopeAll:
71+
case "":
72+
aspell.IdentifierScope = identifierScopeFiles
73+
default:
74+
return Aspell{}, fmt.Errorf("invalid identifier_scope: %s", aspell.IdentifierScope)
75+
}
76+
6977
log.Printf("aspell mode set to %s", aspell.Mode)
7078
if fileExists {
7179
aspell.HelpText = `aspell can be configured with .aspell.yml file.

0 commit comments

Comments
 (0)