in src/debugAdapter/goDebug.ts [1679:1765]
protected variablesRequest(
response: DebugProtocol.VariablesResponse,
args: DebugProtocol.VariablesArguments
): void {
log('VariablesRequest');
const vari = this.variableHandles.get(args.variablesReference);
let variablesPromise: Promise<DebugProtocol.Variable[]> | undefined;
const loadChildren = async (exp: string, v: DebugVariable) => {
// from https://github.com/go-delve/delve/blob/master/Documentation/api/ClientHowto.md#looking-into-variables
if (
(v.kind === GoReflectKind.Struct && v.len > v.children.length) ||
(v.kind === GoReflectKind.Interface && v.children.length > 0 && v.children[0].onlyAddr === true)
) {
await this.evaluateRequestImpl({ expression: exp }).then(
(result) => {
const variable = this.delve?.isApiV1 ? <DebugVariable>result : (<EvalOut>result).Variable;
v.children = variable.children;
},
(err) => this.logDelveError(err, 'Failed to evaluate expression')
);
}
};
// expressions passed to loadChildren defined per
// https://github.com/go-delve/delve/blob/master/Documentation/api/ClientHowto.md#loading-more-of-a-variable
if (vari.kind === GoReflectKind.Array || vari.kind === GoReflectKind.Slice) {
variablesPromise = Promise.all(
vari.children.map((v, i) => {
return loadChildren(`*(*"${v.type}")(${v.addr})`, v).then(
(): DebugProtocol.Variable => {
const { result, variablesReference } = this.convertDebugVariableToProtocolVariable(v);
return {
name: '[' + i + ']',
value: result,
evaluateName: vari.fullyQualifiedName + '[' + i + ']',
variablesReference
};
}
);
})
);
} else if (vari.kind === GoReflectKind.Map) {
variablesPromise = Promise.all(
vari.children
.map((_, i) => {
// even indices are map keys, odd indices are values
if (i % 2 === 0 && i + 1 < vari.children.length) {
const mapKey = this.convertDebugVariableToProtocolVariable(vari.children[i]);
return loadChildren(
`${vari.fullyQualifiedName}.${vari.name}[${mapKey.result}]`,
vari.children[i + 1]
).then(() => {
const mapValue = this.convertDebugVariableToProtocolVariable(vari.children[i + 1]);
return {
name: mapKey.result,
value: mapValue.result,
evaluateName: vari.fullyQualifiedName + '[' + mapKey.result + ']',
variablesReference: mapValue.variablesReference
} as DebugProtocol.Variable;
});
}
})
.filter((v): v is Promise<DebugProtocol.Variable> => !!v) // remove the null values created by combining keys and values
);
} else {
variablesPromise = Promise.all(
vari.children.map((v) => {
return loadChildren(`*(*"${v.type}")(${v.addr})`, v).then(
(): DebugProtocol.Variable => {
const { result, variablesReference } = this.convertDebugVariableToProtocolVariable(v);
return {
name: v.name,
value: result,
evaluateName: v.fullyQualifiedName,
variablesReference
};
}
);
})
);
}
variablesPromise.then((variables) => {
response.body = { variables };
this.sendResponse(response);
log('VariablesResponse', JSON.stringify(variables, null, ' '));
});
}