Maxim KernFrom CI: gopls settings section in package.json needs update. To update the settings, run "go run tools/generate.go -w -gopls".
Hongxiang JiangI runed the generator, now there are many more changes. Is this ok? I don't fully understand its purpose and results.
Probably not. Let me try to figure this out. Normally it's the gopls version wrong running in your workstation.
I will also review the change. Thanks for the contribution.
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
Maxim KernFrom CI: gopls settings section in package.json needs update. To update the settings, run "go run tools/generate.go -w -gopls".
Hongxiang JiangI runed the generator, now there are many more changes. Is this ok? I don't fully understand its purpose and results.
Probably not. Let me try to figure this out. Normally it's the gopls version wrong running in your workstation.
I will also review the change. Thanks for the contribution.
Try
```
go install golang.org/x/tools/go...@v0.20.0
// verify the go version is 0.20.0
gopls version
// run generate
cd extension
go run tools/generate.go -w -gopls
```
The generator use the gopls local version, so maybe the gopls version in your env is 0.19 or earlier resulting in this big change in package.json.
extension: add debug menu for var show in doccan you also add this change to.
`CHANGELOG.md`
"command": "go.debug.openVariableAsDoc",
"title": "Open in new Document"
},could you create a description for this command?
also follow the pattern of title `Go: ...`
the prefix `Go:` allow user to clearly identify this command is for Go debug session not for others.
import * as vscode from 'vscode';Could you add the copyright header
```
/*---------------------------------------------------------
* Copyright 2025 The Go Authors. All rights reserved.
* Licensed under the MIT License. See LICENSE in the project root for license information.
*--------------------------------------------------------*/
```
interface VariableRef {
sessionId: string;
container: Container;
variable: Variable;
}
interface Container {
name: string;
variablesReference: number;
expensive: boolean;
}
interface Variable {
name: string;
value: string;
evaluateName: string;
variablesReference: number;
}
It's also new to me recently that vscode try passing the context information to the extension based on where the command is being called.
There isnt a pre-defined type we can just use in the input parameter (not I can find from Google or LLM). So can we document it here saying this is the type of information vscode will be passing to the call back function with pointer to
https://code.visualstudio.com/api/references/contribution-points#contributes.menus
return JSON.parse(raw);In my local experiment, the yaml and a random string both succeed and returns.
raw = raw.slice(1, -1);I'm not very sure whether this is intentional, but if I have a variable that is type `*main.config` and it's value is nil, I will end up with opening a file with
```
main.config ni
```
the `*` and the `l` is taken away.
same if I have a variable of type "error".
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
Maxim KernFrom CI: gopls settings section in package.json needs update. To update the settings, run "go run tools/generate.go -w -gopls".
Hongxiang JiangI runed the generator, now there are many more changes. Is this ok? I don't fully understand its purpose and results.
Hongxiang JiangProbably not. Let me try to figure this out. Normally it's the gopls version wrong running in your workstation.
I will also review the change. Thanks for the contribution.
Try
```
go install golang.org/x/tools/go...@v0.20.0// verify the go version is 0.20.0
gopls version// run generate
cd extension
go run tools/generate.go -w -gopls
```The generator use the gopls local version, so maybe the gopls version in your env is 0.19 or earlier resulting in this big change in package.json.
It helped! Thanks!
can you also add this change to.
`CHANGELOG.md`
Done
"command": "go.debug.openVariableAsDoc",
"title": "Open in new Document"
},could you create a description for this command?
also follow the pattern of title `Go: ...`
the prefix `Go:` allow user to clearly identify this command is for Go debug session not for others.
Set the prefix, but it was changed intentionally from https://go-review.googlesource.com/c/vscode-go/+/690675/comment/985f8e7b_d0af72d1/
Could you add the copyright header
```
/*---------------------------------------------------------
* Copyright 2025 The Go Authors. All rights reserved.
* Licensed under the MIT License. See LICENSE in the project root for license information.
*--------------------------------------------------------*/
```
Done
interface VariableRef {
sessionId: string;
container: Container;
variable: Variable;
}
interface Container {
name: string;
variablesReference: number;
expensive: boolean;
}
interface Variable {
name: string;
value: string;
evaluateName: string;
variablesReference: number;
}
It's also new to me recently that vscode try passing the context information to the extension based on where the command is being called.
There isnt a pre-defined type we can just use in the input parameter (not I can find from Google or LLM). So can we document it here saying this is the type of information vscode will be passing to the call back function with pointer to
https://code.visualstudio.com/api/references/contribution-points#contributes.menus
Done
In my local experiment, the yaml and a random string both succeed and returns.
I simplified the logic a bit and now it parses json just in case, and if it failed I go through the replace. Also i did some research about how it works underhood the Delve and used read memory DAP protocol part that requires some additional work (PR in Delve is ready). So i propose to wait for it. What do you think? Current code is for new features of Delve, but we can read big strings after it.
I'm not very sure whether this is intentional, but if I have a variable that is type `*main.config` and it's value is nil, I will end up with opening a file with
```
main.config ni
```the `*` and the `l` is taken away.
same if I have a variable of type "error".
Done
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
extension: add debug menu for var show in doc
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c8d70ae..9b183fe 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,8 @@
## Unreleased
+Added menu in a debugger that will show variable in a new document with respect to special chars like `\r\n\t`
+
## v0.49.0 (prerelease)
Date: 2025-07-07
diff --git a/docs/commands.md b/docs/commands.md
index c13f5ab..271faa2 100644
--- a/docs/commands.md
+++ b/docs/commands.md
@@ -23,6 +23,10 @@
<!-- Everything below this line is generated. DO NOT EDIT. -->
+### `Go: Open in new Document`
+
+Open selected variable in a new document.
+
### `Go: Current GOPATH`
See the currently set GOPATH.
diff --git a/extension/package.json b/extension/package.json
index f93e9ea..b090b95 100644
--- a/extension/package.json
+++ b/extension/package.json
@@ -213,6 +213,11 @@
},
"commands": [
{
+ "command": "go.debug.openVariableAsDoc",
+ "title": "Go: Open in new Document",
+ "description": "Open selected variable in a new document."
+ },
+ {
"command": "go.gopath",
"title": "Go: Current GOPATH",
"description": "See the currently set GOPATH."
@@ -3487,6 +3492,10 @@
{
"command": "go.explorer.open",
"when": "false"
+ },
+ {
+ "command": "go.debug.openVariableAsDoc",
+ "when": "false"
}
],
"debug/callstack/context": [
@@ -3495,6 +3504,13 @@
"when": "debugType == 'go' && callStackItemType == 'stackFrame' || (callStackItemType == 'thread' && callStackItemStopped)"
}
],
+ "debug/variables/context": [
+ {
+ "command": "go.debug.openVariableAsDoc",
+ "when": "debugType=='go'",
+ "group": "navigation"
+ }
+ ],
"editor/context": [
{
"when": "editorTextFocus && config.go.editorContextMenuCommands.toggleTestFile && resourceLangId == go",
diff --git a/extension/src/goDebugCommands.ts b/extension/src/goDebugCommands.ts
new file mode 100644
index 0000000..19b7221
--- /dev/null
+++ b/extension/src/goDebugCommands.ts
@@ -0,0 +1,139 @@
+/*---------------------------------------------------------
+ * Copyright (C) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See LICENSE in the project root for license information.
+ *--------------------------------------------------------*/
+
+import * as vscode from 'vscode';
+import { TextDecoder } from 'util';
+
+// Track sessions since vscode doesn't provide a list of them.
+const sessions = new Map<string, vscode.DebugSession>();
+vscode.debug.onDidStartDebugSession((s) => sessions.set(s.id, s));
+vscode.debug.onDidTerminateDebugSession((s) => sessions.delete(s.id));
+
+/**
+ * Registers commands to improve the debugging experience for Go.
+ *
+ * Currently, it adds a command to open a variable in a new text document.
+ */
+export function registerGoDebugCommands(ctx: vscode.ExtensionContext) {
+ class VariableContentProvider implements vscode.TextDocumentContentProvider {
+ static uriForRef(ref: VariableRef) {
+ return vscode.Uri.from({
+ scheme: 'go-debug-variable',
+ authority: `${ref.container.variablesReference}@${ref.sessionId}`,
+ path: `/${ref.variable.name}`
+ });
+ }
+
+ async provideTextDocumentContent(uri: vscode.Uri): Promise<string> {
+ const name = uri.path.replace(/^\//, '');
+ const [container, sessionId] = uri.authority.split('@', 2);
+ if (!container || !sessionId) {
+ throw new Error('Invalid URI');
+ }
+
+ const session = sessions.get(sessionId);
+ if (!session) return 'Debug session has been terminated';
+
+ const { variables } = await session.customRequest('variables', {
+ variablesReference: parseInt(container, 10)
+ }) as { variables: Variable[] };
+
+ const v = variables.find(v => v.name === name);
+ if (!v) return `Cannot resolve variable ${name}`;
+
+ if (!v.memoryReference) {
+ const { result } = await session.customRequest('evaluate', {
+ expression: v.evaluateName,
+ context: 'clipboard'
+ }) as { result: string };
+
+ v.value = result ?? v.value;
+
+ return parseVariable(v);
+ }
+
+ const chunk = 1 << 14;
+ let offset = 0;
+ let full: Uint8Array[] = [];
+
+ while (true) {
+ const resp = await session.customRequest('readMemory', {
+ memoryReference: v.memoryReference,
+ offset,
+ count: chunk
+ }) as { address: string; data: string; unreadableBytes: number };
+
+ if (!resp.data) break;
+ full.push(Buffer.from(resp.data, 'base64'));
+
+ if (resp.unreadableBytes === 0) break;
+ offset += chunk;
+ }
+
+ const allBytes = Buffer.concat(full);
+
+ return new TextDecoder('utf-8').decode(allBytes);
+ }
+ }
+
+ ctx.subscriptions.push(
+ vscode.workspace.registerTextDocumentContentProvider('go-debug-variable', new VariableContentProvider())
+ );
+
+ ctx.subscriptions.push(
+ vscode.commands.registerCommand('go.debug.openVariableAsDoc', async (ref: VariableRef) => {
+ const uri = VariableContentProvider.uriForRef(ref);
+ const doc = await vscode.workspace.openTextDocument(uri);
+ await vscode.window.showTextDocument(doc);
+ })
+ );
+
+ /**
+ * A reference to a variable, used to pass data between commands.
+ */
+ interface VariableRef {
+ sessionId: string;
+ container: Container;
+ variable: Variable;
+ }
+
+ /**
+ * A container for variables, used to pass data between commands.
+ */
+ interface Container {
+ name: string;
+ variablesReference: number;
+ expensive: boolean;
+ }
+
+ /**
+ * A variable, used to pass data between commands.
+ */
+ interface Variable {
+ name: string;
+ value: string;
+ evaluateName: string;
+ variablesReference: number;
+ memoryReference?: string;
+ }
+
+ const escapeCodes: Record<string, string> = {
+ r: '\r',
+ n: '\n',
+ t: '\t'
+ };
+
+ /**
+ * Parses a variable value, unescaping special characters.
+ */
+ function parseVariable(variable: Variable) {
+ let raw = variable.value.trim();
+ try {
+ return JSON.parse(raw);
+ } catch (_) {
+ return raw.replace(/\\[nrt\\"'`]/, (_, s) => (s in escapeCodes ? escapeCodes[s] : s));
+ }
+ }
+}
diff --git a/extension/src/goDebugConfiguration.ts b/extension/src/goDebugConfiguration.ts
index a1de509..f221984 100644
--- a/extension/src/goDebugConfiguration.ts
+++ b/extension/src/goDebugConfiguration.ts
@@ -34,6 +34,7 @@
import { createRegisterCommand } from './commands';
import { GoExtensionContext } from './context';
import { spawn } from 'child_process';
+import { registerGoDebugCommands } from './goDebugCommands';
let dlvDAPVersionChecked = false;
@@ -45,6 +46,7 @@
const registerCommand = createRegisterCommand(ctx, goCtx);
registerCommand('go.debug.pickProcess', () => pickProcess);
registerCommand('go.debug.pickGoProcess', () => pickGoProcess);
+ registerGoDebugCommands(ctx);
}
constructor(private defaultDebugAdapterType: string = 'go') {}
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
Maxim KernFrom CI: gopls settings section in package.json needs update. To update the settings, run "go run tools/generate.go -w -gopls".
Hongxiang JiangI runed the generator, now there are many more changes. Is this ok? I don't fully understand its purpose and results.
Hongxiang JiangProbably not. Let me try to figure this out. Normally it's the gopls version wrong running in your workstation.
I will also review the change. Thanks for the contribution.
Maxim KernTry
```
go install golang.org/x/tools/go...@v0.20.0// verify the go version is 0.20.0
gopls version// run generate
cd extension
go run tools/generate.go -w -gopls
```The generator use the gopls local version, so maybe the gopls version in your env is 0.19 or earlier resulting in this big change in package.json.
It helped! Thanks!
There are still unexpected changes in package.json.
I think you just need a rebase to make it correct. Try to sync your commit to the head of master branch.
Rebase and re-run the steps above.
I tried it. https://go-review.git.corp.google.com/c/vscode-go/+/695396
There are some style error showing up in my editor, please also resolve those. I think the presubmit will fail. I can trigger a test run.
Like
```
'full' is never reassigned. Use 'const' instead
Unexpected constant condition.
'raw' is never reassigned. Use 'const' instead.eslintprefer-const
```
return JSON.parse(raw);Maxim KernIn my local experiment, the yaml and a random string both succeed and returns.
I simplified the logic a bit and now it parses json just in case, and if it failed I go through the replace. Also i did some research about how it works underhood the Delve and used read memory DAP protocol part that requires some additional work (PR in Delve is ready). So i propose to wait for it. What do you think? Current code is for new features of Delve, but we can read big strings after it.
Sure. Could you link the PR to delv?
Most of the CL looks good to me. Need @ethan....@gmail.com to take another look at the recent change.
We might need to have version aware logic, or a capability aware logic to coordinate with the delv.
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
return JSON.parse(raw);Maxim KernIn my local experiment, the yaml and a random string both succeed and returns.
Hongxiang JiangI simplified the logic a bit and now it parses json just in case, and if it failed I go through the replace. Also i did some research about how it works underhood the Delve and used read memory DAP protocol part that requires some additional work (PR in Delve is ready). So i propose to wait for it. What do you think? Current code is for new features of Delve, but we can read big strings after it.
Sure. Could you link the PR to delv?
Most of the CL looks good to me. Need @ethan....@gmail.com to take another look at the recent change.
We might need to have version aware logic, or a capability aware logic to coordinate with the delv.
Of course, PR in Delve https://github.com/go-delve/delve/pull/4083 with small demo of what i'm trying to achieve.
Actually, it looks like PR in vscode-go in it's current state is compatible with current version of Delve, because of this https://github.com/golang/vscode-go/pull/3818/files#diff-ec4ea7b9d627c035b805356e9a923aa1d2045ce8892a1755891b7b76bdbaf1d6R46. And when Delve PR will be merged (and also google/go-dap https://github.com/google/go-dap/pull/94) we get full strings feature automatically
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
vscode.debug.onDidTerminateDebugSession((s) => sessions.delete(s.id));It's probably best to move these into `registerGoDebugCommands` and add them to the extension context's list of subscriptions so they can be cleaned up. When I wrote this I was thinking "they live for the life of the extension so it's ok if they're not cleaned up". But extensions get unloaded and reloaded so that's not actually true.
class VariableContentProvider implements vscode.TextDocumentContentProvider {This is a personal nit: I don't like code that is unnecessarily indented. This class does not need to be defined within the function and IMO it would be more readable if it wasn't.
return new TextDecoder('utf-8').decode(allBytes);Why not the follow, potentially avoiding an allocation?
```suggestion
return allBytes.toString('utf-8');
```
return JSON.parse(raw);Maxim KernIn my local experiment, the yaml and a random string both succeed and returns.
Hongxiang JiangI simplified the logic a bit and now it parses json just in case, and if it failed I go through the replace. Also i did some research about how it works underhood the Delve and used read memory DAP protocol part that requires some additional work (PR in Delve is ready). So i propose to wait for it. What do you think? Current code is for new features of Delve, but we can read big strings after it.
Maxim KernSure. Could you link the PR to delv?
Most of the CL looks good to me. Need @ethan....@gmail.com to take another look at the recent change.
We might need to have version aware logic, or a capability aware logic to coordinate with the delv.
Of course, PR in Delve https://github.com/go-delve/delve/pull/4083 with small demo of what i'm trying to achieve.
Actually, it looks like PR in vscode-go in it's current state is compatible with current version of Delve, because of this https://github.com/golang/vscode-go/pull/3818/files#diff-ec4ea7b9d627c035b805356e9a923aa1d2045ce8892a1755891b7b76bdbaf1d6R46. And when Delve PR will be merged (and also google/go-dap https://github.com/google/go-dap/pull/94) we get full strings feature automatically
I think it should work. If the version of delve doesn't support memory references, it will fall back to string parsing. I wasn't sure about the original string parsing - I assumed there was a reason for handling back ticks so I preserved that when I made my suggestion. If delve never produces backtick wrapped values, it's unnecessary.
interface VariableRef {Same as my nit with the class, I think it would be cleaner if these were global (for the file) instead of nested within the registration function.
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
Sorry for the delay, I wasn't checking gerrit.
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
vscode.debug.onDidTerminateDebugSession((s) => sessions.delete(s.id));It's probably best to move these into `registerGoDebugCommands` and add them to the extension context's list of subscriptions so they can be cleaned up. When I wrote this I was thinking "they live for the life of the extension so it's ok if they're not cleaned up". But extensions get unloaded and reloaded so that's not actually true.
I moved this calls into registerGoDebugCommands function and also used them in ctx.subscriptions.push, because it looks like it's better pattern for Disposable objects. Like we already did it for vscode.workspace.registerTextDocumentContentProvider and vscode.commands.registerCommand. What do you think?
There are also vscode.debug.onDidChangeActiveDebugSession callback and vscode.debug.activeDebugSession prop that we ignore. Is it safe or we must use it?
class VariableContentProvider implements vscode.TextDocumentContentProvider {This is a personal nit: I don't like code that is unnecessarily indented. This class does not need to be defined within the function and IMO it would be more readable if it wasn't.
Done
Why not the follow, potentially avoiding an allocation?
```suggestion
return allBytes.toString('utf-8');
```
Done
Same as my nit with the class, I think it would be cleaner if these were global (for the file) instead of nested within the registration function.
Done
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Code-Review | +1 |
| Commit-Queue | +1 |
vscode.debug.onDidTerminateDebugSession((s) => sessions.delete(s.id));Maxim KernIt's probably best to move these into `registerGoDebugCommands` and add them to the extension context's list of subscriptions so they can be cleaned up. When I wrote this I was thinking "they live for the life of the extension so it's ok if they're not cleaned up". But extensions get unloaded and reloaded so that's not actually true.
I moved this calls into registerGoDebugCommands function and also used them in ctx.subscriptions.push, because it looks like it's better pattern for Disposable objects. Like we already did it for vscode.workspace.registerTextDocumentContentProvider and vscode.commands.registerCommand. What do you think?
There are also vscode.debug.onDidChangeActiveDebugSession callback and vscode.debug.activeDebugSession prop that we ignore. Is it safe or we must use it?
Yeah, it all looks good.
There are also vscode.debug.onDidChangeActiveDebugSession callback and vscode.debug.activeDebugSession prop that we ignore
We don't care about which session is active, we just need to be able to map ID to debug session so that we can query delve. So maintaining a list of live sessions is sufficient.
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
From CI: gopls settings section in package.json needs update. To update the settings, run "go run tools/generate.go -w -gopls".
Hongxiang JiangI runed the generator, now there are many more changes. Is this ok? I don't fully understand its purpose and results.
Hongxiang JiangProbably not. Let me try to figure this out. Normally it's the gopls version wrong running in your workstation.
I will also review the change. Thanks for the contribution.
Maxim KernTry
```
go install golang.org/x/tools/go...@v0.20.0// verify the go version is 0.20.0
gopls version// run generate
cd extension
go run tools/generate.go -w -gopls
```The generator use the gopls local version, so maybe the gopls version in your env is 0.19 or earlier resulting in this big change in package.json.
Hongxiang JiangIt helped! Thanks!
There are still unexpected changes in package.json.
I think you just need a rebase to make it correct. Try to sync your commit to the head of master branch.
Rebase and re-run the steps above.
I tried it. https://go-review.git.corp.google.com/c/vscode-go/+/695396
I fix conflict in github interface (and that merged master into my branch). Rerun generator - nothing changed.
There are some style error showing up in my editor, please also resolve those. I think the presubmit will fail. I can trigger a test run.
Like
```
'full' is never reassigned. Use 'const' instead
Unexpected constant condition.
'raw' is never reassigned. Use 'const' instead.eslintprefer-const
```
Did it. I don't have such messages, probably because of the tooling installed. Am I missing anything else?
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
Hi Maxim, I have not been following the latest update on the delv side, is this CL ready for review? @ctaj...@gmail.com
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
Hi Maxim, I have not been following the latest update on the delv side, is this CL ready for review? @ctaj...@gmail.com
Hi @hxj...@golang.org, dlv 1.26 was released recently. Looks like it is ready to go. I've updated PR (fix conflict in CHANGELOG) and rebased to current master.
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |