Skip to content

Commit b71f123

Browse files
committed
internal/lsp/debug: show Options in gopls debug server
Provide both the current values and default values of Options. These are in the Sessions tabs. Display in the debug server, including the Options, needs work. Change-Id: I970fa65631702b79f6e5f8089a5b1cc5daa8334e Reviewed-on: https://go-review.googlesource.com/c/tools/+/275540 Trust: Peter Weinberger <[email protected]> Run-TryBot: Peter Weinberger <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Robert Findley <[email protected]>
1 parent a543418 commit b71f123

File tree

2 files changed

+103
-0
lines changed

2 files changed

+103
-0
lines changed

internal/lsp/debug/info.go

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

1620
type 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+
}

internal/lsp/debug/serve.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -680,6 +680,9 @@ Unknown page
680680
}
681681
return s
682682
},
683+
"options": func(s *cache.Session) []string {
684+
return showOptions(s.Options())
685+
},
683686
})
684687

685688
var mainTmpl = template.Must(template.Must(baseTemplate.Clone()).Parse(`
@@ -779,6 +782,8 @@ From: <b>{{template "cachelink" .Cache.ID}}</b><br>
779782
<ul>{{range .Views}}<li>{{.Name}} is {{template "viewlink" .ID}} in {{.Folder}}</li>{{end}}</ul>
780783
<h2>Overlays</h2>
781784
<ul>{{range .Overlays}}<li>{{template "filelink" .}}</li>{{end}}</ul>
785+
<h2>Options</h2>
786+
{{range options .}}<p>{{.}}{{end}}
782787
{{end}}
783788
`))
784789

0 commit comments

Comments
 (0)