diff --git a/helper/didyoumean/name_suggestion.go b/helper/didyoumean/name_suggestion.go new file mode 100644 index 000000000..54899bc65 --- /dev/null +++ b/helper/didyoumean/name_suggestion.go @@ -0,0 +1,24 @@ +package didyoumean + +import ( + "github.com/agext/levenshtein" +) + +// NameSuggestion tries to find a name from the given slice of suggested names +// that is close to the given name and returns it if found. If no suggestion +// is close enough, returns the empty string. +// +// The suggestions are tried in order, so earlier suggestions take precedence +// if the given string is similar to two or more suggestions. +// +// This function is intended to be used with a relatively-small number of +// suggestions. It's not optimized for hundreds or thousands of them. +func NameSuggestion(given string, suggestions []string) string { + for _, suggestion := range suggestions { + dist := levenshtein.Distance(given, suggestion, nil) + if dist < 3 { // threshold determined experimentally + return suggestion + } + } + return "" +} diff --git a/helper/didyoumean/name_suggestion_test.go b/helper/didyoumean/name_suggestion_test.go new file mode 100644 index 000000000..756cb4b69 --- /dev/null +++ b/helper/didyoumean/name_suggestion_test.go @@ -0,0 +1,53 @@ +package didyoumean + +import ( + "testing" +) + +func TestNameSuggestion(t *testing.T) { + var keywords = []string{"false", "true", "null"} + + tests := []struct { + Input, Want string + }{ + {"true", "true"}, + {"false", "false"}, + {"null", "null"}, + {"bananas", ""}, + {"NaN", ""}, + {"Inf", ""}, + {"Infinity", ""}, + {"void", ""}, + {"undefined", ""}, + + {"ture", "true"}, + {"tru", "true"}, + {"tre", "true"}, + {"treu", "true"}, + {"rtue", "true"}, + + {"flase", "false"}, + {"fales", "false"}, + {"flse", "false"}, + {"fasle", "false"}, + {"fasel", "false"}, + {"flue", "false"}, + + {"nil", "null"}, + {"nul", "null"}, + {"unll", "null"}, + {"nll", "null"}, + } + + for _, test := range tests { + t.Run(test.Input, func(t *testing.T) { + got := NameSuggestion(test.Input, keywords) + if got != test.Want { + t.Errorf( + "wrong result\ninput: %q\ngot: %q\nwant: %q", + test.Input, got, test.Want, + ) + } + }) + } +}