-
Notifications
You must be signed in to change notification settings - Fork 136
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Name allowed keys and/guess on missing map key (#547)
* Name allowed keys and/guess on missing map key * Attribute "Nearest" to Starlark authors * Adjust format to make more readable with 2+ errors Co-authored-by: John Ryan <[email protected]>
- Loading branch information
1 parent
0c8d4b9
commit c8ed5cd
Showing
5 changed files
with
169 additions
and
28 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
// Copyright 2021 VMware, Inc. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
// Package spell file defines a simple spelling checker for use in attribute errors | ||
// such as "no such field .foo; did you mean .food?". | ||
// (this source was copied from https://github.com/google/starlark-go/blob/b0039bd2cfe369fe8f2bdba0614bafd1f9402dbb/internal/spell/spell.go) | ||
package spell | ||
|
||
import ( | ||
"strings" | ||
"unicode" | ||
) | ||
|
||
// Nearest returns the element of candidates | ||
// nearest to x using the Levenshtein metric, | ||
// or "" if none were promising. | ||
func Nearest(x string, candidates []string) string { | ||
// Ignore underscores and case when matching. | ||
fold := func(s string) string { | ||
return strings.Map(func(r rune) rune { | ||
if r == '_' { | ||
return -1 | ||
} | ||
return unicode.ToLower(r) | ||
}, s) | ||
} | ||
|
||
x = fold(x) | ||
|
||
var best string | ||
bestD := (len(x) + 1) / 2 // allow up to 50% typos | ||
for _, c := range candidates { | ||
d := levenshtein(x, fold(c), bestD) | ||
if d < bestD { | ||
bestD = d | ||
best = c | ||
} | ||
} | ||
return best | ||
} | ||
|
||
// levenshtein returns the non-negative Levenshtein edit distance | ||
// between the byte strings x and y. | ||
// | ||
// If the computed distance exceeds max, | ||
// the function may return early with an approximate value > max. | ||
func levenshtein(x, y string, max int) int { | ||
// This implementation is derived from one by Laurent Le Brun in | ||
// Bazel that uses the single-row space efficiency trick | ||
// described at bitbucket.org/clearer/iosifovich. | ||
|
||
// Let x be the shorter string. | ||
if len(x) > len(y) { | ||
x, y = y, x | ||
} | ||
|
||
// Remove common prefix. | ||
for i := 0; i < len(x); i++ { | ||
if x[i] != y[i] { | ||
x = x[i:] | ||
y = y[i:] | ||
break | ||
} | ||
} | ||
if x == "" { | ||
return len(y) | ||
} | ||
|
||
if d := abs(len(x) - len(y)); d > max { | ||
return d // excessive length divergence | ||
} | ||
|
||
row := make([]int, len(y)+1) | ||
for i := range row { | ||
row[i] = i | ||
} | ||
|
||
for i := 1; i <= len(x); i++ { | ||
row[0] = i | ||
best := i | ||
prev := i - 1 | ||
for j := 1; j <= len(y); j++ { | ||
a := prev + b2i(x[i-1] != y[j-1]) // substitution | ||
b := 1 + row[j-1] // deletion | ||
c := 1 + row[j] // insertion | ||
k := min(a, min(b, c)) | ||
prev, row[j] = row[j], k | ||
best = min(best, k) | ||
} | ||
if best > max { | ||
return best | ||
} | ||
} | ||
return row[len(y)] | ||
} | ||
|
||
func b2i(b bool) int { | ||
if b { | ||
return 1 | ||
} | ||
return 0 | ||
} | ||
|
||
func min(x, y int) int { | ||
if x < y { | ||
return x | ||
} | ||
return y | ||
} | ||
|
||
func abs(x int) int { | ||
if x >= 0 { | ||
return x | ||
} | ||
return -x | ||
} |