diff --git a/cmd/check.go b/cmd/check.go index d1dd12c55f..696e20a67c 100644 --- a/cmd/check.go +++ b/cmd/check.go @@ -112,6 +112,22 @@ func checkCmd(cmd *cobra.Command, args []string) error { continue } + // Check spelling in both the app and the manifest. + community.SilentSpelling = true + err = community.SpellCheck(cmd, []string{app}) + if err != nil { + foundIssue = true + failure(app, fmt.Errorf("app contains spelling errors: %w", err), fmt.Sprintf("try `pixlet community spell-check --fix %s`", app)) + continue + } + err = community.SpellCheck(cmd, []string{manifestFile}) + if err != nil { + foundIssue = true + failure(app, fmt.Errorf("manifest contains spelling errors: %w", err), fmt.Sprintf("try `pixlet community spell-check --fix %s`", manifestFile)) + continue + } + + // If we're here, the app and manifest are good to go! success(app) } diff --git a/cmd/community/community.go b/cmd/community/community.go index 1e93b240f5..a07834b788 100644 --- a/cmd/community/community.go +++ b/cmd/community/community.go @@ -7,6 +7,7 @@ import ( func init() { CommunityCmd.AddCommand(CreateManifestCmd) CommunityCmd.AddCommand(ListIconsCmd) + CommunityCmd.AddCommand(SpellCheckCmd) CommunityCmd.AddCommand(TargetDeterminatorCmd) CommunityCmd.AddCommand(ValidateIconsCmd) CommunityCmd.AddCommand(ValidateManifestCmd) diff --git a/cmd/community/spellcheck.go b/cmd/community/spellcheck.go new file mode 100644 index 0000000000..1aebcd0c5f --- /dev/null +++ b/cmd/community/spellcheck.go @@ -0,0 +1,98 @@ +package community + +import ( + "fmt" + "io" + "os" + + "github.com/client9/misspell" + "github.com/spf13/cobra" +) + +var ( + FixSpelling bool + SilentSpelling bool +) + +func init() { + SpellCheckCmd.Flags().BoolVarP(&FixSpelling, "fix", "f", false, "fixes spelling mistakes automatically") + SpellCheckCmd.Flags().BoolVarP(&SilentSpelling, "silent", "s", false, "silences spelling mistakes") +} + +var SpellCheckCmd = &cobra.Command{ + Use: "spell-check ", + Short: "Spell check for a file", + Example: ` pixlet community spell-check manifest.yaml + pixlet community spell-check app.star`, + Long: `This command checks the spelling of strings located in a file. This can be used +both for a manifest and Tidbyt app.`, + Args: cobra.ExactArgs(1), + RunE: SpellCheck, +} + +func SpellCheck(cmd *cobra.Command, args []string) error { + // Load file for checking. + f, err := os.OpenFile(args[0], os.O_RDWR, 0644) + if err != nil { + return fmt.Errorf("could not open file: %w", err) + } + defer f.Close() + + b, err := io.ReadAll(f) + if err != nil { + return fmt.Errorf("could not read file: %w", err) + } + + // Create replacer. + r := misspell.Replacer{ + Replacements: misspell.DictMain, + } + + // Tidbyt is primarily in US markets. We only ship a US power plug, and all + // materials are in the US locale. In the future, we will need to consider + // how we manage spell check as we look to support more markets. + r.AddRuleList(misspell.DictAmerican) + r.Compile() + + // Run replacer. + updated, diffs := r.Replace(string(b)) + + // If FixSpelling is true, we only want to fix spelling and return + if FixSpelling { + // Updating a file in line gets a bit tricky. The file would first have + // to be cleared of the file contents, which feels dangerous. So + // instead, create a temp file, write the contents, and then replace + // the original file with the new file. + temp := args[0] + ".temp" + t, err := os.Create(temp) + if err != nil { + return fmt.Errorf("could not create file: %w", err) + } + defer t.Close() + + _, err = t.WriteString(updated) + if err != nil { + return fmt.Errorf("could not update file: %w", err) + } + + err = os.Rename(temp, args[0]) + if err != nil { + return fmt.Errorf("could not replace file: %w", err) + } + + return nil + } + + if !SilentSpelling { + for _, diff := range diffs { + fmt.Printf("`%s` is a misspelling of `%s` at line: %d\n", diff.Original, diff.Corrected, diff.Line) + } + } + + // Return error if there are any diffs. + if len(diffs) > 0 { + return fmt.Errorf("%s contains spelling errors", args[0]) + } + + return nil +} diff --git a/go.mod b/go.mod index 4d9272d9c2..ce88d2c3a3 100644 --- a/go.mod +++ b/go.mod @@ -39,6 +39,7 @@ require ( require ( github.com/chzyer/readline v1.5.0 // indirect + github.com/client9/misspell v0.3.4 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.17 // indirect ) diff --git a/go.sum b/go.sum index c9354fbf52..82a71bc1a2 100644 --- a/go.sum +++ b/go.sum @@ -90,6 +90,7 @@ github.com/chzyer/readline v1.5.0 h1:lSwwFrbNviGePhkewF1az4oLmcwqCZijQ2/Wi3BGHAI github.com/chzyer/readline v1.5.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86cAH8qUic= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cloudflare/circl v1.1.0 h1:bZgT/A+cikZnKIwn7xL2OBj012Bmvho/o6RpRvv3GKY= github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I=