@@ -9,8 +9,12 @@ import (
9
9
"context"
10
10
"fmt"
11
11
"io"
12
+ "reflect"
12
13
"runtime/debug"
14
+ "sort"
13
15
"strings"
16
+
17
+ "golang.org/x/tools/internal/lsp/source"
14
18
)
15
19
16
20
type PrintMode int
@@ -156,3 +160,97 @@ func printModuleInfo(w io.Writer, m *Module, mode PrintMode) {
156
160
}
157
161
fmt .Fprintf (w , "\n " )
158
162
}
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