Skip to content

Commit 695d259

Browse files
Show inline values (#384)
* Add inline provider * add inlineProvider * Add inlie provider * Add tests * fix import * fix lint * Update inline function to remove strins values * Add unit test for class type * fix merge
1 parent 481ccdb commit 695d259

File tree

6 files changed

+421
-0
lines changed

6 files changed

+421
-0
lines changed

src/extension/common/vscodeapi.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,3 +128,7 @@ export function startDebugging(
128128
) {
129129
debug.startDebugging(folder, nameOrConfiguration, parentSession);
130130
}
131+
132+
export function customRequest(command: string, args?: any): any {
133+
return debug.activeDebugSession?.customRequest(command, args);
134+
}
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT license.
3+
4+
import {
5+
InlineValue,
6+
InlineValueContext,
7+
InlineValuesProvider,
8+
Range,
9+
TextDocument,
10+
InlineValueVariableLookup,
11+
InlineValueEvaluatableExpression,
12+
} from 'vscode';
13+
import { customRequest } from '../../common/vscodeapi';
14+
15+
export class PythonInlineValueProvider implements InlineValuesProvider {
16+
public async provideInlineValues(
17+
document: TextDocument,
18+
viewPort: Range,
19+
context: InlineValueContext,
20+
): Promise<InlineValue[]> {
21+
let scopesRequest = await customRequest('scopes', { frameId: context.frameId });
22+
let variablesRequest = await customRequest('variables', {
23+
variablesReference: scopesRequest.scopes[0].variablesReference,
24+
});
25+
26+
//https://docs.python.org/3/reference/lexical_analysis.html#keywords
27+
const pythonKeywords = [
28+
'False',
29+
'await',
30+
'else',
31+
'import ',
32+
'pass',
33+
'None',
34+
'break',
35+
'except',
36+
'in',
37+
'raise',
38+
'True',
39+
'class',
40+
'finally',
41+
'is',
42+
'return',
43+
'and',
44+
'continue',
45+
'for',
46+
'lambda',
47+
'try',
48+
'as',
49+
'def',
50+
'from',
51+
'nonlocal',
52+
'while',
53+
'assert',
54+
'del',
55+
'global',
56+
'not',
57+
'with',
58+
'async',
59+
'elif',
60+
'if',
61+
'or',
62+
'yield',
63+
'self',
64+
];
65+
66+
const pythonVariables: any[] = variablesRequest.variables
67+
.filter((variable: any) => variable.type)
68+
.map((variable: any) => variable.name);
69+
70+
let variableRegex = new RegExp(
71+
'(?:self.)?' + //match self. if present
72+
'[a-zA-Z_][a-zA-Z0-9_]*', //math variable name
73+
'g',
74+
);
75+
76+
const allValues: InlineValue[] = [];
77+
for (let l = viewPort.start.line; l <= viewPort.end.line; l++) {
78+
const line = document.lineAt(l);
79+
// Skip comments
80+
if (line.text.trimStart().startsWith('#')) {
81+
continue;
82+
}
83+
84+
let code = removeCharsOutsideBraces(line.text);
85+
86+
for (let match = variableRegex.exec(code); match; match = variableRegex.exec(code)) {
87+
let varName = match[0];
88+
// Skip python keywords
89+
if (pythonKeywords.includes(varName)) {
90+
continue;
91+
}
92+
if (pythonVariables.includes(varName.split('.')[0])) {
93+
if (varName.includes('self')) {
94+
const rng = new Range(l, match.index, l, match.index + varName.length);
95+
allValues.push(new InlineValueEvaluatableExpression(rng, varName));
96+
} else {
97+
const rng = new Range(l, match.index, l, match.index + varName.length);
98+
allValues.push(new InlineValueVariableLookup(rng, varName, false));
99+
}
100+
}
101+
}
102+
}
103+
return allValues;
104+
}
105+
}
106+
107+
function removeCharsOutsideBraces(code: string): string {
108+
// Regular expression to find Python strings
109+
const stringRegex = /(["'])(?:(?=(\\?))\2.)*?\1/g;
110+
111+
//Regular expression to match values inside {}
112+
const insideBracesRegex = /{[^{}]*}/g;
113+
114+
return code.replace(stringRegex, (match) => {
115+
const content = match.slice(1, -1);
116+
117+
let result = '';
118+
let tempMatch;
119+
120+
while ((tempMatch = insideBracesRegex.exec(content)) !== null) {
121+
result += tempMatch[0];
122+
}
123+
const processedContent = result || content;
124+
125+
return match[0] + processedContent + match[0];
126+
});
127+
}

src/extension/extensionInit.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ import { openReportIssue } from './common/application/commands/reportIssueComman
4949
import { buildApi } from './api';
5050
import { IExtensionApi } from './apiTypes';
5151
import { registerHexDebugVisualizationTreeProvider } from './debugger/visualizers/inlineHexDecoder';
52+
import { PythonInlineValueProvider } from './debugger/inlineValue/pythonInlineValueProvider';
5253

5354
export async function registerDebugger(context: IExtensionContext): Promise<IExtensionApi> {
5455
const childProcessAttachService = new ChildProcessAttachService();
@@ -136,6 +137,7 @@ export async function registerDebugger(context: IExtensionContext): Promise<IExt
136137
context.subscriptions.push(
137138
debug.registerDebugAdapterDescriptorFactory(DebuggerTypeName, debugAdapterDescriptorFactory),
138139
);
140+
139141
context.subscriptions.push(
140142
debug.onDidStartDebugSession((debugSession) => {
141143
const shouldTerminalFocusOnStart = getConfiguration('python', debugSession.workspaceFolder?.uri)?.terminal
@@ -195,6 +197,10 @@ export async function registerDebugger(context: IExtensionContext): Promise<IExt
195197
>('inlineHexDecoder', registerHexDebugVisualizationTreeProvider()),
196198
);
197199

200+
context.subscriptions.push(
201+
languages.registerInlineValuesProvider({ language: 'python' }, new PythonInlineValueProvider()),
202+
);
203+
198204
context.subscriptions.push(
199205
debug.registerDebugVisualizationProvider('inlineHexDecoder', {
200206
provideDebugVisualization(_context, _token) {
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
class Person:
2+
def __init__(self, name, age):
3+
self.name = name
4+
self.age = age
5+
6+
def greet(self):
7+
return f"Hello, my name is {self.name} and I a {self.age} years old."
8+
9+
person1 = Person("John Doe", 30)
10+
person1.greet()

src/test/pythonFiles/testVarTypes.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
var1 = 5
2+
var2 = 7
3+
var3 = "hola"
4+
var4 = {"a": 1, "b": 2}
5+
var5 = [1, 2, 3]
6+
var6 =var1 + var2

0 commit comments

Comments
 (0)