Skip to content

Commit

Permalink
Improve help output and quiet mode
Browse files Browse the repository at this point in the history
- A short help message is now shown if F2 is run without arguments
- Quiet mode no longer suppresses error messages
  • Loading branch information
ayoisaiah committed Aug 5, 2021
1 parent 1967f15 commit b04fe6c
Show file tree
Hide file tree
Showing 6 changed files with 138 additions and 79 deletions.
4 changes: 4 additions & 0 deletions cmd/f2/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package main
import (
"os"

"github.com/pterm/pterm"

f2 "github.com/ayoisaiah/f2/src"
)

Expand All @@ -13,6 +15,8 @@ func run(args []string) error {
func main() {
err := run(os.Args)
if err != nil {
pterm.EnableOutput()
pterm.Error.Println(err)
os.Exit(1)
}
}
96 changes: 37 additions & 59 deletions src/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,32 +12,7 @@ import (

func init() {
// Override the default help template
cli.AppHelpTemplate = `DESCRIPTION:
{{.Usage}}
USAGE:
{{.HelpName}} {{if .UsageText}}{{ .UsageText }}{{end}}
{{if len .Authors}}
AUTHOR:
{{range .Authors}}{{ . }}{{end}}{{end}}
{{if .Version}}
VERSION:
{{.Version}}{{end}}
{{if .VisibleFlags}}
FLAGS:{{range .VisibleFlags}}{{ if (eq .Name "find" "undo" "replace") }}
{{if .Aliases}}-{{range $element := .Aliases}}{{$element}},{{end}}{{end}} --{{.Name}} {{.DefaultText}}
{{.Usage}}
{{end}}{{end}}
OPTIONS:{{range .VisibleFlags}}{{ if not (eq .Name "find" "replace" "undo") }}
{{if .Aliases}}-{{range $element := .Aliases}}{{$element}},{{end}}{{end}} --{{.Name}} {{ .DefaultText }}
{{.Usage}}
{{end}}{{end}}{{end}}
DOCUMENTATION:
https://github.com/ayoisaiah/f2/wiki
WEBSITE:
https://github.com/ayoisaiah/f2
`
cli.AppHelpTemplate = helpText()

// Override the default version printer
oldVersionPrinter := cli.VersionPrinter
Expand Down Expand Up @@ -126,68 +101,68 @@ func GetApp() *cli.App {
Email: "[email protected]",
},
},
Usage: "F2 is a command-line tool for batch renaming multiple files and directories quickly and safely",
UsageText: "FLAGS [OPTIONS] [PATHS...]",
Version: "v1.7.0",
Usage: "F2 is a command-line tool for batch renaming multiple files and directories quickly and safely.",
UsageText: "FLAGS [OPTIONS] [PATHS TO FILES OR DIRECTORIES...]",
Version: "v1.7.1",
EnableBashCompletion: true,
Flags: []cli.Flag{
&cli.StringSliceFlag{
Name: "find",
Aliases: []string{"f"},
Usage: "Search pattern. Treated as a regular expression by default unless --string-mode is also used. If omitted, it defaults to the entire file name (including the extension).",
Usage: "Search pattern. Treated as a regular expression by default unless --string-mode is also used.\n\t\t\t\tDefaults to the entire file name if omitted.",
DefaultText: "<pattern>",
},
&cli.StringSliceFlag{
Name: "replace",
Aliases: []string{"r"},
Usage: "Replacement string. If omitted, defaults to an empty string. Supports built-in and regex capture variables. Learn more about variable support here: https://github.com/ayoisaiah/f2/wiki/Built-in-variables",
Usage: "Replacement string. If omitted, defaults to an empty string. Supports several kinds of variables.\n\t\t\t\tLearn more: https://github.com/ayoisaiah/f2/wiki/Built-in-variables.",
DefaultText: "<string>",
},
&cli.BoolFlag{
Name: "undo",
Aliases: []string{"u"},
Usage: "Undo the last operation performed in the current working directory if possible.\n\t\t\t\tLearn more: https://github.com/ayoisaiah/f2/wiki/Undoing-a-renaming-operation.",
},
&cli.StringFlag{
Name: "csv",
Usage: "Load a CSV file, and rename according to its contents. File names will be matched according to the content in the first column",
Usage: "Load a CSV file, and rename according to its contents.\n\t\t\t\tLearn more: https://github.com/ayoisaiah/f2/wiki/Renaming-from-a-CSV-file.",
DefaultText: "<csv file>",
},
&cli.IntFlag{
Name: "replace-limit",
Aliases: []string{"l"},
Usage: "Limit the number of replacements to be made on the file name (replaces all matches if set to 0). Can be set to a negative integer to start replacing from the end of the file name.",
Usage: "Limit the number of replacements to be made on each matched file (replaces all matches if set to 0).\n\t\t\t\tCan be set to a negative integer to start replacing from the end of the file name.",
Value: 0,
DefaultText: "<integer>",
},
&cli.BoolFlag{
Name: "string-mode",
Aliases: []string{"s"},
Usage: "Opt into string literal mode. The presence of this flag causes the search pattern to be treated as a non-regex string.",
Usage: "Treats the search pattern as a non-regex string.",
},
&cli.StringSliceFlag{
Name: "exclude",
Aliases: []string{"E"},
Usage: "Exclude files/directories that match the given search pattern. Treated as a regular expression. Multiple exclude patterns can be specified.",
Usage: "Exclude files/directories that match the given search pattern. Treated as a regular expression.\n\t\t\t\tMultiple exclude patterns can be specified by repeating this option.",
DefaultText: "<pattern>",
},
&cli.BoolFlag{
Name: "exec",
Aliases: []string{"x"},
Usage: "Execute the batch renaming operation. This will commit the changes to your filesystem.",
Usage: "Commit the renaming operation to the filesystem.",
},
&cli.BoolFlag{
Name: "recursive",
Aliases: []string{"R"},
Usage: "Recursively traverse all directories when searching for matches. Use the --max-depth flag to control the maximum allowed depth (no limit by default).",
Usage: "Recursively traverse directories when searching for matches.",
},
&cli.UintFlag{
Name: "max-depth",
Aliases: []string{"m"},
Usage: "Positive integer indicating the maximum depth for a recursive search (set to 0 for no limit).",
Usage: "Indicates the maximum depth for a recursive search (set to 0 by default for no limit).",
Value: 0,
DefaultText: "<integer>",
},
&cli.BoolFlag{
Name: "undo",
Aliases: []string{"u"},
Usage: "Undo the last operation performed in the current working directory if possible. Learn more: https://github.com/ayoisaiah/f2/wiki/Undoing-a-renaming-operation",
},
&cli.StringFlag{
Name: "sort",
Usage: `Sort the matches according to the provided '<sort>'.
Expand All @@ -208,12 +183,12 @@ func GetApp() *cli.App {
&cli.BoolFlag{
Name: "ignore-case",
Aliases: []string{"i"},
Usage: "When this flag is provided, the given pattern will be searched case insensitively.",
Usage: "Search for matches case insensitively.",
},
&cli.BoolFlag{
Name: "quiet",
Aliases: []string{"q"},
Usage: "Activate silent mode which doesn't print out any information including errors",
Usage: "Don't print out any information (except errors).",
},
&cli.BoolFlag{
Name: "ignore-ext",
Expand All @@ -223,55 +198,58 @@ func GetApp() *cli.App {
&cli.BoolFlag{
Name: "include-dir",
Aliases: []string{"d"},
Usage: "Include directories when searching for matches as they are exempted by default.",
Usage: "Include directories (they are exempted by default).",
},
&cli.BoolFlag{
Name: "only-dir",
Aliases: []string{"D"},
Usage: "Rename only directories, not files (implies --include-dir)",
Usage: "Rename only directories, not files (implies --include-dir).",
},
&cli.BoolFlag{
Name: "hidden",
Aliases: []string{"H"},
Usage: "Include hidden directories and files in the matches (they are skipped by default). A hidden file or directory is one whose name starts with a period (all operating systems) or one whose hidden attribute is set to true (Windows only)",
Usage: "Include hidden files (they are skipped by default).",
},
&cli.BoolFlag{
Name: "verbose",
Aliases: []string{"V"},
Usage: "Enable verbose output. Each renaming operation will be printed out if this flag is provided.",
Usage: "Enable verbose output.",
},
&cli.BoolFlag{
Name: "no-color",
Usage: "Disable coloured output",
Usage: "Disable coloured output.",
},
&cli.BoolFlag{
Name: "fix-conflicts",
Aliases: []string{"F"},
Usage: "Automatically fix conflicts based on predefined rules. Learn more: https://github.com/ayoisaiah/f2/wiki/Validation-and-conflict-detection",
Usage: "Automatically fix conflicts based on predefined rules.\n\t\t\t\tLearn more: https://github.com/ayoisaiah/f2/wiki/Validation-and-conflict-detection.",
},
&cli.BoolFlag{
Name: "allow-overwrites",
Usage: "Allow the overwriting of existing files",
Usage: "Allow the overwriting of existing files.",
},
},
UseShortOptionHandling: true,
Action: func(c *cli.Context) error {
if c.NumFlags() == 0 {
pterm.Println(shortHelp(c.App))
os.Exit(1)
}

if c.Bool("no-color") {
disableStyling()
}

op, err := newOperation(c)
if err != nil {
printError(false, err)
return err
if c.Bool("quiet") {
pterm.DisableOutput()
}

err = op.run()
op, err := newOperation(c)
if err != nil {
printError(op.quiet, err)
return err
}

return err
return op.run()
},
}
}
80 changes: 80 additions & 0 deletions src/help.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package f2

import (
"fmt"

"github.com/pterm/pterm"
"github.com/urfave/cli/v2"
)

func helpText() string {
description := fmt.Sprintf(
"%s\n\t\t{{.Usage}}\n\n",
pterm.Yellow("DESCRIPTION"),
)
usage := fmt.Sprintf(
"%s\n\t\t{{.HelpName}} {{if .UsageText}}{{ .UsageText }}{{end}}\n\n",
pterm.Yellow("USAGE"),
)
author := fmt.Sprintf(
"{{if len .Authors}}%s\n\t\t{{range .Authors}}{{ . }}{{end}}{{end}}\n\n",
pterm.Yellow("AUTHOR"),
)

version := fmt.Sprintf(
"{{if .Version}}%s\n\t\t{{.Version}}{{end}}\n\n",
pterm.Yellow("VERSION"),
)
flags := fmt.Sprintf(
"{{if .VisibleFlags}}%s\n{{range .VisibleFlags}}{{ if (eq .Name `find` `undo` `replace` `csv`) }}\t\t{{if .Aliases}}-{{range $element := .Aliases}}%s,{{end}}{{end}} %s\n\t\t\t\t{{.Usage}}\n\n{{end}}{{end}}",
pterm.Yellow("FLAGS"),
pterm.Green("{{$element}}"),
pterm.Green("--{{.Name}} {{.DefaultText}}"),
)
options := fmt.Sprintf(
"%s\n{{range .VisibleFlags}}{{ if not (eq .Name `find` `undo` `replace` `csv`) }}\t\t{{if .Aliases}}-{{range $element := .Aliases}}%s,{{end}}{{end}} %s\n\t\t\t\t{{.Usage}}\n\n{{end}}{{end}}{{end}}",
pterm.Yellow("OPTIONS"),
pterm.Green("{{$element}}"),
pterm.Green("--{{.Name}} {{.DefaultText}}"),
)

docs := fmt.Sprintf(
"%s\n\t\t%s\n\n",
pterm.Yellow("DOCUMENTATION"),
"https://github.com/ayoisaiah/f2/wiki",
)
website := fmt.Sprintf(
"%s\n\t\thttps://github.com/ayoisaiah/f2\n",
pterm.Yellow("WEBSITE"),
)

return description + usage + author + version + flags + options + docs + website
}

func shortHelp(app *cli.App) string {
heading := fmt.Sprintf(
"F2 — Command-line bulk renaming tool [version %s]\n\n",
app.Version,
)

usage := fmt.Sprintf("Usage: %s\n", app.UsageText)

description := `
F2 helps you organise your filesystem through batch renaming.
The simplest usage is to do a basic find and replace:
$ f2 -f 'Screenshot' -r 'Image'
+--------------------+---------------+--------+
| INPUT | OUTPUT | STATUS |
+--------------------+---------------+--------+
| Screenshot (1).png | Image (1).png | ok |
| Screenshot (2).png | Image (2).png | ok |
| Screenshot (3).png | Image (3).png | ok |
+--------------------+---------------+--------+
For more usage examples, see: https://github.com/ayoisaiah/f2/wiki
Use f2 --help to see the full list of options.`

return heading + usage + description
}
27 changes: 14 additions & 13 deletions src/operation.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,14 +83,14 @@ type Operation struct {
maxDepth int
sort string
reverseSort bool
quiet bool
errors []renameError
revert bool
numberOffset []int
replaceLimit int
allowOverwrites bool
verbose bool
csvFilename string
quiet bool
}

type backupFile struct {
Expand Down Expand Up @@ -366,9 +366,7 @@ func (op *Operation) noMatches() {
msg = "No operations to undo"
}

if !op.quiet {
pterm.Info.Println(msg)
}
pterm.Info.Println(msg)
}

// execute applies the renaming operation to the filesystem.
Expand All @@ -389,7 +387,7 @@ func (op *Operation) execute() error {
return op.backup()
}

if !op.quiet && !op.revert {
if !op.revert {
pterm.Info.Println("No files were renamed")
}

Expand All @@ -400,10 +398,11 @@ func (op *Operation) execute() error {
func (op *Operation) dryRun() {
if !op.quiet {
op.printChanges()
pterm.Info.Printfln(
"Use the -x or --exec flag to apply the above changes",
)
}

pterm.Info.Printfln(
"Use the -x or --exec flag to apply the above changes",
)
}

// apply prints the changes to be made in dry-run mode
Expand All @@ -419,9 +418,7 @@ func (op *Operation) apply() error {
op.detectConflicts()

if len(op.conflicts) > 0 && !op.fixConflicts {
if !op.quiet {
op.reportConflicts()
}
op.reportConflicts()

return errConflictDetected
}
Expand Down Expand Up @@ -762,7 +759,11 @@ func (op *Operation) handleCSV(paths map[string][]fs.DirEntry) error {
}

if !found && op.verbose {
pterm.Warning.Printfln("Source file '%s' was not found, so row '%d' was skipped", source, i+1)
pterm.Warning.Printfln(
"Source file '%s' was not found, so row '%d' was skipped",
source,
i+1,
)
}

loop:
Expand Down Expand Up @@ -822,12 +823,12 @@ func setOptions(op *Operation, c *cli.Context) error {
op.stringLiteralMode = c.Bool("string-mode")
op.excludeFilter = c.StringSlice("exclude")
op.maxDepth = int(c.Uint("max-depth"))
op.quiet = c.Bool("quiet")
op.revert = c.Bool("undo")
op.verbose = c.Bool("verbose")
op.allowOverwrites = c.Bool("allow-overwrites")
op.replaceLimit = c.Int("replace-limit")
op.csvFilename = c.String("csv")
op.quiet = c.Bool("quiet")

// Sorting
if c.String("sort") != "" {
Expand Down
Loading

0 comments on commit b04fe6c

Please sign in to comment.