Merge pull request #52 from JanetKuo/shorthand-deprecator

Add shorthand deprecator
diff --git a/README.md b/README.md
index deee931..247ceca 100644
--- a/README.md
+++ b/README.md
@@ -216,6 +216,25 @@
 myFlagSet.SetNormalizeFunc(aliasNormalizeFunc)
 ```
 
+## Deprecating a flag or its shorthand 
+It is possible to deprecate a flag, or just its shorthand. Deprecating a flag/shorthand hides it from help text and prints a usage message when the deprecated flag/shorthand is used. 
+
+**Example #1**: You want to deprecate a flag named "badflag" as well as inform the users what flag they should use instead. 
+```go
+// deprecate a flag by specifying its name and a usage message 
+flags.MarkDeprecated("badflag", "please use --good-flag instead")
+```
+This hides "badflag" from help text, and prints `Flag --badflag has been deprecated, please use --good-flag instead` when "badflag" is used. 
+
+**Example #2**: You want to keep a flag name "noshorthandflag" but deprecate its shortname "n". 
+```go
+// deprecate a flag shorthand by specifying its flag name and a usage message 
+flags.MarkShorthandDeprecated("noshorthandflag", "please use --noshorthandflag only")
+```
+This hides the shortname "n" from help text, and prints `Flag shorthand -n has been deprecated, please use --noshorthandflag only` when the shorthand "n" is used. 
+
+Note that usage message is essential here, and it should not be empty. 
+
 ## More info
 
 You can see the full reference documentation of the pflag package
diff --git a/flag.go b/flag.go
index 603c81a..73f202f 100644
--- a/flag.go
+++ b/flag.go
@@ -149,15 +149,16 @@
 
 // A Flag represents the state of a flag.
 type Flag struct {
-	Name        string              // name as it appears on command line
-	Shorthand   string              // one-letter abbreviated flag
-	Usage       string              // help message
-	Value       Value               // value as set
-	DefValue    string              // default value (as text); for usage message
-	Changed     bool                // If the user set the value (or if left to default)
-	NoOptDefVal string              //default value (as text); if the flag is on the command line without any options
-	Deprecated  string              // If this flag is deprecated, this string is the new or now thing to use
-	Annotations map[string][]string // used by cobra.Command  bash autocomple code
+	Name                string              // name as it appears on command line
+	Shorthand           string              // one-letter abbreviated flag
+	Usage               string              // help message
+	Value               Value               // value as set
+	DefValue            string              // default value (as text); for usage message
+	Changed             bool                // If the user set the value (or if left to default)
+	NoOptDefVal         string              //default value (as text); if the flag is on the command line without any options
+	Deprecated          string              // If this flag is deprecated, this string is the new or now thing to use
+	ShorthandDeprecated string              // If the shorthand of this flag is deprecated, this string is the new or now thing to use
+	Annotations         map[string][]string // used by cobra.Command  bash autocomple code
 }
 
 // Value is the interface to the dynamic value stored in a flag.
@@ -298,10 +299,28 @@
 	if flag == nil {
 		return fmt.Errorf("flag %q does not exist", name)
 	}
+	if len(usageMessage) == 0 {
+		return fmt.Errorf("deprecated message for flag %q must be set", name)
+	}
 	flag.Deprecated = usageMessage
 	return nil
 }
 
+// Mark the shorthand of a flag deprecated in your program. It will
+// continue to function but will not show up in help or usage messages. Using
+// this flag will also print the given usageMessage.
+func (f *FlagSet) MarkShorthandDeprecated(name string, usageMessage string) error {
+	flag := f.Lookup(name)
+	if flag == nil {
+		return fmt.Errorf("flag %q does not exist", name)
+	}
+	if len(usageMessage) == 0 {
+		return fmt.Errorf("deprecated message for flag %q must be set", name)
+	}
+	flag.ShorthandDeprecated = usageMessage
+	return nil
+}
+
 // Lookup returns the Flag structure of the named command-line flag,
 // returning nil if none exists.
 func Lookup(name string) *Flag {
@@ -379,7 +398,7 @@
 			return
 		}
 		format := ""
-		if len(flag.Shorthand) > 0 {
+		if len(flag.Shorthand) > 0 && len(flag.ShorthandDeprecated) == 0 {
 			format = "  -%s, --%s"
 		} else {
 			format = "   %s   --%s"
@@ -397,7 +416,11 @@
 			format = format + "]"
 		}
 		format = format + ": %s\n"
-		fmt.Fprintf(x, format, flag.Shorthand, flag.Name, flag.DefValue, flag.Usage)
+		shorthand := flag.Shorthand
+		if len(flag.ShorthandDeprecated) > 0 {
+			shorthand = ""
+		}
+		fmt.Fprintf(x, format, shorthand, flag.Name, flag.DefValue, flag.Usage)
 	})
 
 	return x.String()
@@ -586,9 +609,21 @@
 	if len(flag.Deprecated) > 0 {
 		fmt.Fprintf(os.Stderr, "Flag --%s has been deprecated, %s\n", flag.Name, flag.Deprecated)
 	}
+	if len(flag.ShorthandDeprecated) > 0 && containsShorthand(origArg, flag.Shorthand) {
+		fmt.Fprintf(os.Stderr, "Flag shorthand -%s has been deprecated, %s\n", flag.Shorthand, flag.ShorthandDeprecated)
+	}
 	return nil
 }
 
+func containsShorthand(arg, shorthand string) bool {
+	// filter out flags --<flag_name>
+	if strings.HasPrefix(arg, "-") {
+		return false
+	}
+	arg = strings.SplitN(arg, "=", 2)[0]
+	return strings.Contains(arg, shorthand)
+}
+
 func (f *FlagSet) parseLongArg(s string, args []string) (a []string, err error) {
 	a = args
 	name := s[2:]
diff --git a/flag_test.go b/flag_test.go
index 5114f28..165f689 100644
--- a/flag_test.go
+++ b/flag_test.go
@@ -729,6 +729,21 @@
 	}
 }
 
+func TestDeprecatedFlagShorthandInDocs(t *testing.T) {
+	f := NewFlagSet("bob", ContinueOnError)
+	name := "noshorthandflag"
+	f.BoolP(name, "n", true, "always true")
+	f.MarkShorthandDeprecated("noshorthandflag", fmt.Sprintf("use --%s instead", name))
+
+	out := new(bytes.Buffer)
+	f.SetOutput(out)
+	f.PrintDefaults()
+
+	if strings.Contains(out.String(), "-n,") {
+		t.Errorf("found deprecated flag shorthand in usage!")
+	}
+}
+
 func parseReturnStderr(t *testing.T, f *FlagSet, args []string) (string, error) {
 	oldStderr := os.Stderr
 	r, w, _ := os.Pipe()
@@ -768,6 +783,24 @@
 	}
 }
 
+func TestDeprecatedFlagShorthandUsage(t *testing.T) {
+	f := NewFlagSet("bob", ContinueOnError)
+	name := "noshorthandflag"
+	f.BoolP(name, "n", true, "always true")
+	usageMsg := fmt.Sprintf("use --%s instead", name)
+	f.MarkShorthandDeprecated(name, usageMsg)
+
+	args := []string{"-n"}
+	out, err := parseReturnStderr(t, f, args)
+	if err != nil {
+		t.Fatal("expected no error; got ", err)
+	}
+
+	if !strings.Contains(out, usageMsg) {
+		t.Errorf("usageMsg not printed when using a deprecated flag!")
+	}
+}
+
 func TestDeprecatedFlagUsageNormalized(t *testing.T) {
 	f := NewFlagSet("bob", ContinueOnError)
 	f.Bool("bad-double_flag", true, "always true")