Skip to content

Commit e35a9e8

Browse files
committed
better parsing and use TextDocumentContentProvider
Also extended Delve context (clipboard) request used for longer strings
1 parent b04ab94 commit e35a9e8

File tree

2 files changed

+98
-39
lines changed

2 files changed

+98
-39
lines changed

extension/package.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@
214214
"commands": [
215215
{
216216
"command": "go.debug.openVariableAsDoc",
217-
"title": "Go: Open in new Document"
217+
"title": "Open in new Document"
218218
},
219219
{
220220
"command": "go.gopath",
@@ -3486,6 +3486,10 @@
34863486
{
34873487
"command": "go.explorer.open",
34883488
"when": "false"
3489+
},
3490+
{
3491+
"command": "go.debug.openVariableAsDoc",
3492+
"when": "false"
34893493
}
34903494
],
34913495
"debug/callstack/context": [
@@ -3697,4 +3701,4 @@
36973701
}
36983702
]
36993703
}
3700-
}
3704+
}

extension/src/goDebugCommands.ts

Lines changed: 92 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,96 @@
11
import * as vscode from 'vscode';
22

3+
// Track sessions since vscode doesn't provide a list of them.
4+
const sessions = new Map<string, vscode.DebugSession>();
5+
vscode.debug.onDidStartDebugSession((s) => sessions.set(s.id, s));
6+
vscode.debug.onDidTerminateDebugSession((s) => sessions.delete(s.id));
7+
38
export function registerGoDebugCommands(ctx: vscode.ExtensionContext) {
4-
ctx.subscriptions.push(
5-
vscode.commands.registerCommand(
6-
'go.debug.openVariableAsDoc',
7-
async (args: any) => {
8-
const variable = args.variable;
9-
10-
let raw = variable.value.trim();
11-
if (
12-
(raw.startsWith('"') && raw.endsWith('"')) ||
13-
(raw.startsWith('`') && raw.endsWith('`'))
14-
) {
15-
raw = raw.slice(1, -1);
16-
}
17-
18-
let text: string;
19-
try {
20-
text = JSON.parse(`"${raw.replace(/"/g, '\\"')}"`);
21-
} catch {
22-
text = raw
23-
.replace(/\\r/g, '\r')
24-
.replace(/\\n/g, '\n')
25-
.replace(/\\t/g, '\t')
26-
.replace(/\\"/g, '"')
27-
.replace(/\\\\/g, '\\');
28-
}
29-
30-
const doc = await vscode.workspace.openTextDocument({
31-
language: 'plaintext',
32-
content: text
33-
});
34-
35-
const editor = await vscode.window.showTextDocument(doc);
36-
37-
await vscode.commands.executeCommand('workbench.action.editor.changeLanguageMode');
9+
class VariableContentProvider implements vscode.TextDocumentContentProvider {
10+
static uriForRef(ref: VariableRef) {
11+
return vscode.Uri.from({
12+
scheme: 'go-debug-variable',
13+
authority: `${ref.container.variablesReference}@${ref.sessionId}`,
14+
path: `/${ref.variable.name}`
15+
});
16+
}
17+
18+
async provideTextDocumentContent(uri: vscode.Uri): Promise<string> {
19+
const name = uri.path.replace(/^\//, '');
20+
const [container, sessionId] = uri.authority.split('@', 2);
21+
if (!container || !sessionId) {
22+
throw new Error('Invalid URI');
3823
}
39-
)
40-
)
41-
}
24+
25+
const session = sessions.get(sessionId);
26+
if (!session) return 'Debug session has been terminated';
27+
28+
const r: { variables: Variable[] } = await session.customRequest('variables', {
29+
variablesReference: parseInt(container, 10)
30+
});
31+
32+
const v = r.variables.find((v) => v.name === name);
33+
if (!v) return `Cannot resolve variable ${name}`;
34+
35+
const { result } = await session.customRequest('evaluate', {
36+
expression: v.evaluateName,
37+
context: 'clipboard'
38+
});
39+
40+
v.value = result ?? v.value;
41+
42+
return parseVariable(v);
43+
}
44+
}
45+
46+
ctx.subscriptions.push(
47+
vscode.workspace.registerTextDocumentContentProvider('go-debug-variable', new VariableContentProvider())
48+
);
49+
50+
ctx.subscriptions.push(
51+
vscode.commands.registerCommand('go.debug.openVariableAsDoc', async (ref: VariableRef) => {
52+
const uri = VariableContentProvider.uriForRef(ref);
53+
const doc = await vscode.workspace.openTextDocument(uri);
54+
await vscode.window.showTextDocument(doc);
55+
})
56+
);
57+
58+
interface VariableRef {
59+
sessionId: string;
60+
container: Container;
61+
variable: Variable;
62+
}
63+
64+
interface Container {
65+
name: string;
66+
variablesReference: number;
67+
expensive: boolean;
68+
}
69+
70+
interface Variable {
71+
name: string;
72+
value: string;
73+
evaluateName: string;
74+
variablesReference: number;
75+
}
76+
77+
const escapeCodes: Record<string, string> = {
78+
r: '\r',
79+
n: '\n',
80+
t: '\t'
81+
};
82+
83+
function parseVariable(variable: Variable) {
84+
let raw = variable.value.trim();
85+
try {
86+
// Attempt to parse as JSON
87+
return JSON.parse(raw);
88+
} catch (_) {
89+
// Fall back to manual unescaping
90+
raw = raw.slice(1, -1);
91+
}
92+
93+
// Manually unescape
94+
return raw.replace(/\\[nrt\\"'`]/, (_, s) => (s in escapeCodes ? escapeCodes[s] : s));
95+
}
96+
}

0 commit comments

Comments
 (0)