gopls: add long forms for all the 1 rune flags

Also modify the printing so that it groups flags that work on the same
value into the same flag usage line.

For #41860

Change-Id: I97fdd3e64f24b22c15780b636789f512eb46ed2c
Reviewed-on: https://go-review.googlesource.com/c/tools/+/367838
Trust: Ian Cottrell <iancottrell@google.com>
Run-TryBot: Ian Cottrell <iancottrell@google.com>
gopls-CI: kokoro <noreply+kokoro@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
This commit is contained in:
Ian Cottrell 2021-11-24 17:09:03 -05:00
parent 217a9fc29a
commit 5ad7054788
13 changed files with 102 additions and 51 deletions

View File

@ -16,6 +16,7 @@ import (
"log"
"os"
"reflect"
"sort"
"strings"
"sync"
"text/tabwriter"
@ -63,10 +64,10 @@ type Application struct {
Remote string `flag:"remote" help:"forward all commands to a remote lsp specified by this flag. With no special prefix, this is assumed to be a TCP address. If prefixed by 'unix;', the subsequent address is assumed to be a unix domain socket. If 'auto', or prefixed by 'auto;', the remote address is automatically resolved based on the executing environment."`
// Verbose enables verbose logging.
Verbose bool `flag:"v" help:"verbose output"`
Verbose bool `flag:"v,verbose" help:"verbose output"`
// VeryVerbose enables a higher level of verbosity in logging output.
VeryVerbose bool `flag:"vv" help:"very verbose output"`
VeryVerbose bool `flag:"vv,veryverbose" help:"very verbose output"`
// Control ocagent export of telemetry
OCAgent string `flag:"ocagent" help:"the address of the ocagent (e.g. http://localhost:55678), or off"`
@ -138,9 +139,32 @@ command:
// this is a slightly modified version of flag.PrintDefaults to give us control
func printFlagDefaults(s *flag.FlagSet) {
var flags [][]*flag.Flag
seen := map[flag.Value]int{}
s.VisitAll(func(f *flag.Flag) {
if i, ok := seen[f.Value]; !ok {
seen[f.Value] = len(flags)
flags = append(flags, []*flag.Flag{f})
} else {
flags[i] = append(flags[i], f)
}
})
for _, entry := range flags {
sort.SliceStable(entry, func(i, j int) bool {
return len(entry[i].Name) < len(entry[j].Name)
})
var b strings.Builder
fmt.Fprintf(&b, " -%s", f.Name) // Two spaces before -; see next two comments.
for i, f := range entry {
switch i {
case 0:
b.WriteString(" -")
default:
b.WriteString(",-")
}
b.WriteString(f.Name)
}
f := entry[0]
name, usage := flag.UnquoteUsage(f)
if len(name) > 0 {
b.WriteString(" ")
@ -164,7 +188,7 @@ func printFlagDefaults(s *flag.FlagSet) {
}
}
fmt.Fprint(s.Output(), b.String(), "\n")
})
}
}
// isZeroValue is copied from the flags package

View File

@ -19,9 +19,9 @@ import (
// format implements the format verb for gopls.
type format struct {
Diff bool `flag:"d" help:"display diffs instead of rewriting files"`
Write bool `flag:"w" help:"write result to (source) file instead of stdout"`
List bool `flag:"l" help:"list files whose formatting differs from gofmt's"`
Diff bool `flag:"d,diff" help:"display diffs instead of rewriting files"`
Write bool `flag:"w,write" help:"write result to (source) file instead of stdout"`
List bool `flag:"l,list" help:"list files whose formatting differs from gofmt's"`
app *Application
}

View File

@ -20,8 +20,8 @@ import (
// imports implements the import verb for gopls.
type imports struct {
Diff bool `flag:"d" help:"display diffs instead of rewriting files"`
Write bool `flag:"w" help:"write result to (source) file instead of stdout"`
Diff bool `flag:"d,diff" help:"display diffs instead of rewriting files"`
Write bool `flag:"w,write" help:"write result to (source) file instead of stdout"`
app *Application
}

View File

@ -17,7 +17,7 @@ import (
// references implements the references verb for gopls
type references struct {
IncludeDeclaration bool `flag:"d" help:"include the declaration of the specified identifier in the results"`
IncludeDeclaration bool `flag:"d,declaration" help:"include the declaration of the specified identifier in the results"`
app *Application
}

View File

@ -23,8 +23,8 @@ import (
// rename implements the rename verb for gopls.
type rename struct {
Diff bool `flag:"d" help:"display diffs instead of rewriting files"`
Write bool `flag:"w" help:"write result to (source) file instead of stdout"`
Diff bool `flag:"d,diff" help:"display diffs instead of rewriting files"`
Write bool `flag:"w,write" help:"write result to (source) file instead of stdout"`
Preserve bool `flag:"preserve" help:"preserve original files"`
app *Application

View File

@ -20,9 +20,9 @@ import (
// suggestedFix implements the fix verb for gopls.
type suggestedFix struct {
Diff bool `flag:"d" help:"display diffs instead of rewriting files"`
Write bool `flag:"w" help:"write result to (source) file instead of stdout"`
All bool `flag:"a" help:"apply all fixes, not just preferred fixes"`
Diff bool `flag:"d,diff" help:"display diffs instead of rewriting files"`
Write bool `flag:"w,write" help:"write result to (source) file instead of stdout"`
All bool `flag:"a,all" help:"apply all fixes, not just preferred fixes"`
app *Application
}

View File

@ -7,6 +7,9 @@ Example: apply suggested fixes for this file
$ gopls fix -w internal/lsp/cmd/check.go
fix-flags:
-a apply all fixes, not just preferred fixes
-d display diffs instead of rewriting files
-w write result to (source) file instead of stdout
-a,-all
apply all fixes, not just preferred fixes
-d,-diff
display diffs instead of rewriting files
-w,-write
write result to (source) file instead of stdout

View File

@ -10,6 +10,9 @@ Example: reformat this file:
$ gopls format -w internal/lsp/cmd/check.go
format-flags:
-d display diffs instead of rewriting files
-l list files whose formatting differs from gofmt's
-w write result to (source) file instead of stdout
-d,-diff
display diffs instead of rewriting files
-l,-list
list files whose formatting differs from gofmt's
-w,-write
write result to (source) file instead of stdout

View File

@ -8,5 +8,7 @@ Example: update imports statements in a file:
$ gopls imports -w internal/lsp/cmd/check.go
imports-flags:
-d display diffs instead of rewriting files
-w write result to (source) file instead of stdout
-d,-diff
display diffs instead of rewriting files
-w,-write
write result to (source) file instead of stdout

View File

@ -10,4 +10,5 @@ Example:
$ gopls references helper/helper.go:#53
references-flags:
-d include the declaration of the specified identifier in the results
-d,-declaration
include the declaration of the specified identifier in the results

View File

@ -10,7 +10,9 @@ Example:
$ gopls rename helper/helper.go:#53 Foo
rename-flags:
-d display diffs instead of rewriting files
-d,-diff
display diffs instead of rewriting files
-preserve
preserve original files
-w write result to (source) file instead of stdout
-w,-write
write result to (source) file instead of stdout

View File

@ -69,6 +69,7 @@ flags:
when used with -remote=auto, the -logfile value used to start the daemon
-rpc.trace
print the full rpc trace in lsp inspector format
-v verbose output
-vv
-v,-verbose
verbose output
-vv,-veryverbose
very verbose output

View File

@ -15,6 +15,7 @@ import (
"runtime"
"runtime/pprof"
"runtime/trace"
"strings"
"time"
)
@ -28,8 +29,9 @@ import (
// (&Application{}).Main("myapp", "non-flag-command-line-arg-help", os.Args[1:])
// }
// It recursively scans the application object for fields with a tag containing
// `flag:"flagname" help:"short help text"``
// uses all those fields to build command line flags.
// `flag:"flagnames" help:"short help text"``
// uses all those fields to build command line flags. It will split flagnames on
// commas and add a flag per name.
// It expects the Application type to have a method
// Run(context.Context, args...string) error
// which it invokes only after all command line flag processing has been finished.
@ -168,30 +170,44 @@ func addFlags(f *flag.FlagSet, field reflect.StructField, value reflect.Value) *
return nil
}
// now see if is actually a flag
flagName, isFlag := field.Tag.Lookup("flag")
flagNames, isFlag := field.Tag.Lookup("flag")
help := field.Tag.Get("help")
if !isFlag {
// not a flag, but it might be a struct with flags in it
value = resolve(value.Elem())
if value.Kind() != reflect.Struct {
return nil
}
p, _ := value.Addr().Interface().(*Profile)
// go through all the fields of the struct
for i := 0; i < value.Type().NumField(); i++ {
child := value.Type().Field(i)
v := value.Field(i)
// make sure we have a pointer
if v.Kind() != reflect.Ptr {
v = v.Addr()
}
// check if that field is a flag or contains flags
if fp := addFlags(f, child, v); fp != nil {
p = fp
if isFlag {
nameList := strings.Split(flagNames, ",")
// add the main flag
addFlag(f, value, nameList[0], help)
if len(nameList) > 1 {
// and now add any aliases using the same flag value
fv := f.Lookup(nameList[0]).Value
for _, flagName := range nameList[1:] {
f.Var(fv, flagName, help)
}
}
return p
return nil
}
// not a flag, but it might be a struct with flags in it
value = resolve(value.Elem())
if value.Kind() != reflect.Struct {
return nil
}
p, _ := value.Addr().Interface().(*Profile)
// go through all the fields of the struct
for i := 0; i < value.Type().NumField(); i++ {
child := value.Type().Field(i)
v := value.Field(i)
// make sure we have a pointer
if v.Kind() != reflect.Ptr {
v = v.Addr()
}
// check if that field is a flag or contains flags
if fp := addFlags(f, child, v); fp != nil {
p = fp
}
}
return p
}
func addFlag(f *flag.FlagSet, value reflect.Value, flagName string, help string) {
switch v := value.Interface().(type) {
case flag.Value:
f.Var(v, flagName, help)
@ -214,7 +230,6 @@ func addFlags(f *flag.FlagSet, field reflect.StructField, value reflect.Value) *
default:
log.Fatalf("Cannot understand flag of type %T", v)
}
return nil
}
func resolve(v reflect.Value) reflect.Value {