Skip to content

Commit

Permalink
Shorten type names in variable values
Browse files Browse the repository at this point in the history
The variables view in VS Code is a lot easier to read if long type names are
shortened in much the same way as we shorten them for functions in the call
stack view.

We only shorten them in the value strings; the Type field of dap.Variable is
kept as is. Since this only appears in a tooltip, it isn't a problem to have the
full type visible there.
  • Loading branch information
stefanhaller committed Oct 20, 2023
1 parent e039584 commit 62e94bb
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 47 deletions.
99 changes: 57 additions & 42 deletions service/api/prettyprint.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,33 +21,48 @@ const (
// SinglelineString returns a representation of v on a single line.
func (v *Variable) SinglelineString() string {
var buf bytes.Buffer
v.writeTo(&buf, true, false, true, "", "")
v.writeTo(&buf, true, false, true, false, "", "")
return buf.String()
}

// SinglelineString returns a representation of v on a single line, with types shortened.
func (v *Variable) SinglelineStringWithShortTypes() string {
var buf bytes.Buffer
v.writeTo(&buf, true, false, true, true, "", "")
return buf.String()
}

// SinglelineStringFormatted returns a representation of v on a single line, using the format specified by fmtstr.
func (v *Variable) SinglelineStringFormatted(fmtstr string) string {
var buf bytes.Buffer
v.writeTo(&buf, true, false, true, "", fmtstr)
v.writeTo(&buf, true, false, true, false, "", fmtstr)
return buf.String()
}

// MultilineString returns a representation of v on multiple lines.
func (v *Variable) MultilineString(indent, fmtstr string) string {
var buf bytes.Buffer
v.writeTo(&buf, true, true, true, indent, fmtstr)
v.writeTo(&buf, true, true, true, false, indent, fmtstr)
return buf.String()
}

func (v *Variable) writeTo(buf io.Writer, top, newlines, includeType bool, indent, fmtstr string) {
func (v *Variable) typeStr(shortenType bool) string {
if shortenType {
return ShortenType(v.Type)
}

return v.Type
}

func (v *Variable) writeTo(buf io.Writer, top, newlines, includeType, shortenType bool, indent, fmtstr string) {
if v.Unreadable != "" {
fmt.Fprintf(buf, "(unreadable %s)", v.Unreadable)
return
}

if !top && v.Addr == 0 && v.Value == "" {
if includeType && v.Type != "void" {
fmt.Fprintf(buf, "%s nil", v.Type)
fmt.Fprintf(buf, "%s nil", v.typeStr(shortenType))
} else {
fmt.Fprint(buf, "nil")
}
Expand All @@ -56,21 +71,21 @@ func (v *Variable) writeTo(buf io.Writer, top, newlines, includeType bool, inden

switch v.Kind {
case reflect.Slice:
v.writeSliceTo(buf, newlines, includeType, indent, fmtstr)
v.writeSliceTo(buf, newlines, includeType, shortenType, indent, fmtstr)
case reflect.Array:
v.writeArrayTo(buf, newlines, includeType, indent, fmtstr)
v.writeArrayTo(buf, newlines, includeType, shortenType, indent, fmtstr)
case reflect.Ptr:
if v.Type == "" || len(v.Children) == 0 {
fmt.Fprint(buf, "nil")
} else if v.Children[0].OnlyAddr && v.Children[0].Addr != 0 {
v.writePointerTo(buf)
v.writePointerTo(buf, shortenType)
} else {
if top && newlines && v.Children[0].Addr != 0 {
v.writePointerTo(buf)
v.writePointerTo(buf, shortenType)
fmt.Fprint(buf, "\n")
}
fmt.Fprint(buf, "*")
v.Children[0].writeTo(buf, false, newlines, includeType, indent, fmtstr)
v.Children[0].writeTo(buf, false, newlines, includeType, shortenType, indent, fmtstr)
}
case reflect.UnsafePointer:
if len(v.Children) == 0 {
Expand All @@ -80,20 +95,20 @@ func (v *Variable) writeTo(buf io.Writer, top, newlines, includeType bool, inden
}
case reflect.Chan:
if newlines {
v.writeStructTo(buf, newlines, includeType, indent, fmtstr)
v.writeStructTo(buf, newlines, includeType, shortenType, indent, fmtstr)
} else {
if len(v.Children) == 0 {
fmt.Fprintf(buf, "%s nil", v.Type)
fmt.Fprintf(buf, "%s nil", v.typeStr(shortenType))
} else {
fmt.Fprintf(buf, "%s %s/%s", v.Type, v.Children[0].Value, v.Children[1].Value)
fmt.Fprintf(buf, "%s %s/%s", v.typeStr(shortenType), v.Children[0].Value, v.Children[1].Value)
}
}
case reflect.Struct:
if v.Value != "" {
fmt.Fprintf(buf, "%s(%s)", v.Type, v.Value)
fmt.Fprintf(buf, "%s(%s)", v.typeStr(shortenType), v.Value)
includeType = false
}
v.writeStructTo(buf, newlines, includeType, indent, fmtstr)
v.writeStructTo(buf, newlines, includeType, shortenType, indent, fmtstr)
case reflect.Interface:
if v.Addr == 0 {
// an escaped interface variable that points to nil, this shouldn't
Expand All @@ -103,13 +118,13 @@ func (v *Variable) writeTo(buf io.Writer, top, newlines, includeType bool, inden
}
if includeType {
if v.Children[0].Kind == reflect.Invalid {
fmt.Fprintf(buf, "%s ", v.Type)
fmt.Fprintf(buf, "%s ", v.typeStr(shortenType))
if v.Children[0].Addr == 0 {
fmt.Fprint(buf, "nil")
return
}
} else {
fmt.Fprintf(buf, "%s(%s) ", v.Type, v.Children[0].Type)
fmt.Fprintf(buf, "%s(%s) ", v.typeStr(shortenType), v.Children[0].Type)
}
}
data := v.Children[0]
Expand All @@ -121,19 +136,19 @@ func (v *Variable) writeTo(buf io.Writer, top, newlines, includeType bool, inden
} else if data.Children[0].OnlyAddr {
fmt.Fprintf(buf, "0x%x", v.Children[0].Addr)
} else {
v.Children[0].writeTo(buf, false, newlines, !includeType, indent, fmtstr)
v.Children[0].writeTo(buf, false, newlines, !includeType, shortenType, indent, fmtstr)
}
} else if data.OnlyAddr {
if strings.Contains(v.Type, "/") {
fmt.Fprintf(buf, "*(*%q)(%#x)", v.Type, v.Addr)
fmt.Fprintf(buf, "*(*%q)(%#x)", v.typeStr(shortenType), v.Addr)
} else {
fmt.Fprintf(buf, "*(*%s)(%#x)", v.Type, v.Addr)
fmt.Fprintf(buf, "*(*%s)(%#x)", v.typeStr(shortenType), v.Addr)
}
} else {
v.Children[0].writeTo(buf, false, newlines, !includeType, indent, fmtstr)
v.Children[0].writeTo(buf, false, newlines, !includeType, shortenType, indent, fmtstr)
}
case reflect.Map:
v.writeMapTo(buf, newlines, includeType, indent, fmtstr)
v.writeMapTo(buf, newlines, includeType, shortenType, indent, fmtstr)
case reflect.Func:
if v.Value == "" {
fmt.Fprint(buf, "nil")
Expand All @@ -145,11 +160,11 @@ func (v *Variable) writeTo(buf io.Writer, top, newlines, includeType bool, inden
}
}

func (v *Variable) writePointerTo(buf io.Writer) {
func (v *Variable) writePointerTo(buf io.Writer, shortenType bool) {
if strings.Contains(v.Type, "/") {
fmt.Fprintf(buf, "(%q)(%#x)", v.Type, v.Children[0].Addr)
fmt.Fprintf(buf, "(%q)(%#x)", v.typeStr(shortenType), v.Children[0].Addr)
} else {
fmt.Fprintf(buf, "(%s)(%#x)", v.Type, v.Children[0].Addr)
fmt.Fprintf(buf, "(%s)(%#x)", v.typeStr(shortenType), v.Children[0].Addr)
}
}

Expand Down Expand Up @@ -226,36 +241,36 @@ func ExtractIntValue(s string) string {
return s[open+1 : len(s)-1]
}

func (v *Variable) writeSliceTo(buf io.Writer, newlines, includeType bool, indent, fmtstr string) {
func (v *Variable) writeSliceTo(buf io.Writer, newlines, includeType, shortenType bool, indent, fmtstr string) {
if includeType {
fmt.Fprintf(buf, "%s len: %d, cap: %d, ", v.Type, v.Len, v.Cap)
fmt.Fprintf(buf, "%s len: %d, cap: %d, ", v.typeStr(shortenType), v.Len, v.Cap)
}
if v.Base == 0 && len(v.Children) == 0 {
fmt.Fprintf(buf, "nil")
return
}
v.writeSliceOrArrayTo(buf, newlines, indent, fmtstr)
v.writeSliceOrArrayTo(buf, newlines, shortenType, indent, fmtstr)
}

func (v *Variable) writeArrayTo(buf io.Writer, newlines, includeType bool, indent, fmtstr string) {
func (v *Variable) writeArrayTo(buf io.Writer, newlines, includeType, shortenType bool, indent, fmtstr string) {
if includeType {
fmt.Fprintf(buf, "%s ", v.Type)
fmt.Fprintf(buf, "%s ", v.typeStr(shortenType))
}
v.writeSliceOrArrayTo(buf, newlines, indent, fmtstr)
v.writeSliceOrArrayTo(buf, newlines, shortenType, indent, fmtstr)
}

func (v *Variable) writeStructTo(buf io.Writer, newlines, includeType bool, indent, fmtstr string) {
func (v *Variable) writeStructTo(buf io.Writer, newlines, includeType, shortenType bool, indent, fmtstr string) {
if int(v.Len) != len(v.Children) && len(v.Children) == 0 {
if strings.Contains(v.Type, "/") {
fmt.Fprintf(buf, "(*%q)(%#x)", v.Type, v.Addr)
fmt.Fprintf(buf, "(*%q)(%#x)", v.typeStr(shortenType), v.Addr)
} else {
fmt.Fprintf(buf, "(*%s)(%#x)", v.Type, v.Addr)
fmt.Fprintf(buf, "(*%s)(%#x)", v.typeStr(shortenType), v.Addr)
}
return
}

if includeType {
fmt.Fprintf(buf, "%s ", v.Type)
fmt.Fprintf(buf, "%s ", v.typeStr(shortenType))
}

nl := v.shouldNewlineStruct(newlines)
Expand All @@ -267,7 +282,7 @@ func (v *Variable) writeStructTo(buf io.Writer, newlines, includeType bool, inde
fmt.Fprintf(buf, "\n%s%s", indent, indentString)
}
fmt.Fprintf(buf, "%s: ", v.Children[i].Name)
v.Children[i].writeTo(buf, false, nl, true, indent+indentString, fmtstr)
v.Children[i].writeTo(buf, false, nl, true, shortenType, indent+indentString, fmtstr)
if i != len(v.Children)-1 || nl {
fmt.Fprint(buf, ",")
if !nl {
Expand All @@ -288,9 +303,9 @@ func (v *Variable) writeStructTo(buf io.Writer, newlines, includeType bool, inde
fmt.Fprint(buf, "}")
}

func (v *Variable) writeMapTo(buf io.Writer, newlines, includeType bool, indent, fmtstr string) {
func (v *Variable) writeMapTo(buf io.Writer, newlines, includeType, shortenType bool, indent, fmtstr string) {
if includeType {
fmt.Fprintf(buf, "%s ", v.Type)
fmt.Fprintf(buf, "%s ", v.typeStr(shortenType))
}
if v.Base == 0 && len(v.Children) == 0 {
fmt.Fprintf(buf, "nil")
Expand All @@ -309,9 +324,9 @@ func (v *Variable) writeMapTo(buf io.Writer, newlines, includeType bool, indent,
fmt.Fprintf(buf, "\n%s%s", indent, indentString)
}

key.writeTo(buf, false, false, false, indent+indentString, fmtstr)
key.writeTo(buf, false, false, false, shortenType, indent+indentString, fmtstr)
fmt.Fprint(buf, ": ")
value.writeTo(buf, false, nl, false, indent+indentString, fmtstr)
value.writeTo(buf, false, nl, false, shortenType, indent+indentString, fmtstr)
if i != len(v.Children)-1 || nl {
fmt.Fprint(buf, ", ")
}
Expand Down Expand Up @@ -403,15 +418,15 @@ func (v *Variable) shouldNewlineStruct(newlines bool) bool {
return false
}

func (v *Variable) writeSliceOrArrayTo(buf io.Writer, newlines bool, indent, fmtstr string) {
func (v *Variable) writeSliceOrArrayTo(buf io.Writer, newlines, shortenType bool, indent, fmtstr string) {
nl := v.shouldNewlineArray(newlines)
fmt.Fprint(buf, "[")

for i := range v.Children {
if nl {
fmt.Fprintf(buf, "\n%s%s", indent, indentString)
}
v.Children[i].writeTo(buf, false, nl, false, indent+indentString, fmtstr)
v.Children[i].writeTo(buf, false, nl, false, shortenType, indent+indentString, fmtstr)
if i != len(v.Children)-1 || nl {
fmt.Fprint(buf, ",")
}
Expand Down
8 changes: 4 additions & 4 deletions service/dap/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -2579,7 +2579,7 @@ func (s *Session) convertVariableWithOpts(v *proc.Variable, qualifiedNameOrExpr
}
return s.variableHandles.create(&fullyQualifiedVariable{v, qualifiedNameOrExpr, false /*not a scope*/, 0})
}
value = api.ConvertVar(v).SinglelineString()
value = api.ConvertVar(v).SinglelineStringWithShortTypes()
if v.Unreadable != nil {
return value, 0
}
Expand All @@ -2593,7 +2593,7 @@ func (s *Session) convertVariableWithOpts(v *proc.Variable, qualifiedNameOrExpr
// TODO(polina): Get *proc.Variable object from debugger instead. Export a function to set v.loaded to false
// and call v.loadValue gain with a different load config. It's more efficient, and it's guaranteed to keep
// working with generics.
value = api.ConvertVar(v).SinglelineString()
value = api.ConvertVar(v).SinglelineStringWithShortTypes()
typeName := api.PrettyTypeName(v.DwarfType)
loadExpr := fmt.Sprintf("*(*%q)(%#x)", typeName, v.Addr)
s.config.log.Debugf("loading %s (type %s) with %s", qualifiedNameOrExpr, typeName, loadExpr)
Expand All @@ -2607,7 +2607,7 @@ func (s *Session) convertVariableWithOpts(v *proc.Variable, qualifiedNameOrExpr
} else {
v.Children = vLoaded.Children
v.Value = vLoaded.Value
value = api.ConvertVar(v).SinglelineString()
value = api.ConvertVar(v).SinglelineStringWithShortTypes()
}
return value
}
Expand Down Expand Up @@ -2639,7 +2639,7 @@ func (s *Session) convertVariableWithOpts(v *proc.Variable, qualifiedNameOrExpr
} else {
cLoaded.Name = v.Children[0].Name // otherwise, this will be the pointer expression
v.Children = []proc.Variable{*cLoaded}
value = api.ConvertVar(v).SinglelineString()
value = api.ConvertVar(v).SinglelineStringWithShortTypes()
}
} else {
value = reloadVariable(v, qualifiedNameOrExpr)
Expand Down
2 changes: 1 addition & 1 deletion service/dap/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2502,7 +2502,7 @@ func TestGlobalScopeAndVariables(t *testing.T) {
client.VariablesRequest(globalsScope)
globals := client.ExpectVariablesResponse(t)
checkChildren(t, globals, "Globals", 1)
ref := checkVarExact(t, globals, 0, "SomeVar", "github.com/go-delve/delve/_fixtures/internal/dir0/pkg.SomeVar", "github.com/go-delve/delve/_fixtures/internal/dir0/pkg.SomeType {X: 0}", "github.com/go-delve/delve/_fixtures/internal/dir0/pkg.SomeType", hasChildren)
ref := checkVarExact(t, globals, 0, "SomeVar", "github.com/go-delve/delve/_fixtures/internal/dir0/pkg.SomeVar", "pkg.SomeType {X: 0}", "github.com/go-delve/delve/_fixtures/internal/dir0/pkg.SomeType", hasChildren)

if ref > 0 {
client.VariablesRequest(ref)
Expand Down

0 comments on commit 62e94bb

Please sign in to comment.