@@ -9,8 +9,12 @@ import (
99 "context"
1010 "fmt"
1111 "io"
12+ "reflect"
1213 "runtime/debug"
14+ "sort"
1315 "strings"
16+
17+ "golang.org/x/tools/internal/lsp/source"
1418)
1519
1620type PrintMode int
@@ -156,3 +160,97 @@ func printModuleInfo(w io.Writer, m *Module, mode PrintMode) {
156160 }
157161 fmt .Fprintf (w , "\n " )
158162}
163+
164+ type field struct {
165+ index []int
166+ }
167+
168+ var fields []field
169+
170+ // find all the options. The presumption is that the Options are nested structs
171+ // and that pointers don't need to be dereferenced
172+ func swalk (t reflect.Type , ix []int , indent string ) {
173+ switch t .Kind () {
174+ case reflect .Struct :
175+ for i := 0 ; i < t .NumField (); i ++ {
176+ fld := t .Field (i )
177+ ixx := append (append ([]int {}, ix ... ), i )
178+ swalk (fld .Type , ixx , indent + ". " )
179+ }
180+ default :
181+ // everything is either a struct or a field (that's an assumption about Options)
182+ fields = append (fields , field {ix })
183+ }
184+ }
185+
186+ func showOptions (o * source.Options ) []string {
187+ // non-breaking spaces for indenting current and defaults when they are on a separate line
188+ const indent = "\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 "
189+ var ans strings.Builder
190+ t := reflect .TypeOf (* o )
191+ swalk (t , []int {}, "" )
192+ v := reflect .ValueOf (* o )
193+ do := reflect .ValueOf (* source .DefaultOptions ())
194+ for _ , f := range fields {
195+ val := v .FieldByIndex (f .index )
196+ def := do .FieldByIndex (f .index )
197+ tx := t .FieldByIndex (f .index )
198+ prefix := fmt .Sprintf ("%s (type is %s): " , tx .Name , tx .Type )
199+ is := strVal (val )
200+ was := strVal (def )
201+ if len (is ) < 30 && len (was ) < 30 {
202+ fmt .Fprintf (& ans , "%s current:%s, default:%s\n " , prefix , is , was )
203+ } else {
204+ fmt .Fprintf (& ans , "%s\n %scurrent:%s\n %sdefault:%s\n " , prefix , indent , is , indent , was )
205+ }
206+ }
207+ return strings .Split (ans .String (), "\n " )
208+ }
209+ func strVal (val reflect.Value ) string {
210+ switch val .Kind () {
211+ case reflect .Bool :
212+ return fmt .Sprintf ("%v" , val .Interface ())
213+ case reflect .Int , reflect .Int8 , reflect .Int16 , reflect .Int32 , reflect .Int64 :
214+ return fmt .Sprintf ("%v" , val .Interface ())
215+ case reflect .Uint , reflect .Uint8 , reflect .Uint16 , reflect .Uint32 , reflect .Uint64 :
216+ return fmt .Sprintf ("%v" , val .Interface ())
217+ case reflect .Uintptr , reflect .UnsafePointer :
218+ return fmt .Sprintf ("0x%x" , val .Pointer ())
219+ case reflect .Complex64 , reflect .Complex128 :
220+ return fmt .Sprintf ("%v" , val .Complex ())
221+ case reflect .Array , reflect .Slice :
222+ ans := []string {}
223+ for i := 0 ; i < val .Len (); i ++ {
224+ ans = append (ans , strVal (val .Index (i )))
225+ }
226+ sort .Strings (ans )
227+ return fmt .Sprintf ("%v" , ans )
228+ case reflect .Chan , reflect .Func , reflect .Ptr :
229+ return val .Kind ().String ()
230+ case reflect .Struct :
231+ var x source.Analyzer
232+ if val .Type () != reflect .TypeOf (x ) {
233+ return val .Kind ().String ()
234+ }
235+ // this is sort of ugly, but usable
236+ str := val .FieldByName ("Analyzer" ).Elem ().FieldByName ("Doc" ).String ()
237+ ix := strings .Index (str , "\n " )
238+ if ix == - 1 {
239+ ix = len (str )
240+ }
241+ return str [:ix ]
242+ case reflect .String :
243+ return fmt .Sprintf ("%q" , val .Interface ())
244+ case reflect .Map :
245+ ans := []string {}
246+ iter := val .MapRange ()
247+ for iter .Next () {
248+ k := iter .Key ()
249+ v := iter .Value ()
250+ ans = append (ans , fmt .Sprintf ("%s:%s, " , strVal (k ), strVal (v )))
251+ }
252+ sort .Strings (ans )
253+ return fmt .Sprintf ("%v" , ans )
254+ }
255+ return fmt .Sprintf ("??%s??" , val .Type ())
256+ }
0 commit comments