Skip to content

Commit 21c9f1a

Browse files
Use InlineValuesProvider to provide inline debugging feature (#977)
* Use InlineValuesProvider to provide inline debugging feature * Add metrics to track the performance of inline values feature
1 parent abef713 commit 21c9f1a

File tree

7 files changed

+162
-7
lines changed

7 files changed

+162
-7
lines changed

package-lock.json

Lines changed: 39 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
"debugger"
1515
],
1616
"engines": {
17-
"vscode": "^1.54.0"
17+
"vscode": "^1.55.0"
1818
},
1919
"license": "SEE LICENSE IN LICENSE.txt",
2020
"repository": {
@@ -768,7 +768,7 @@
768768
"@types/mocha": "^5.2.7",
769769
"@types/node": "^14.14.10",
770770
"@types/uuid": "^8.3.0",
771-
"@types/vscode": "1.54.0",
771+
"@types/vscode": "1.55.0",
772772
"cross-env": "^5.2.0",
773773
"gulp": "^4.0.2",
774774
"gulp-tslint": "^8.1.4",
@@ -787,6 +787,8 @@
787787
"uuid": "^8.3.1",
788788
"vscode-extension-telemetry": "^0.1.6",
789789
"vscode-extension-telemetry-wrapper": "^0.9.0",
790+
"vscode-languageclient": "6.0.0-next.9",
791+
"vscode-languageserver-types": "3.16.0",
790792
"vscode-tas-client": "^0.1.22"
791793
}
792794
}

src/JavaInlineValueProvider.ts

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT license.
3+
4+
import * as compareVersions from "compare-versions";
5+
import { debug, InlineValue, InlineValueContext, InlineValueEvaluatableExpression, InlineValuesProvider, InlineValueText, InlineValueVariableLookup,
6+
Range, TextDocument, version } from "vscode";
7+
import { instrumentOperation, instrumentOperationStep, sendInfo } from "vscode-extension-telemetry-wrapper";
8+
import * as CodeConverter from "vscode-languageclient/lib/codeConverter";
9+
import * as ProtocolConverter from "vscode-languageclient/lib/protocolConverter";
10+
import { InlineKind, InlineVariable, resolveInlineVariables } from "./languageServerPlugin";
11+
12+
// In VS Code 1.55.0, viewport doesn't change while scrolling the editor and it's fixed in 1.56.0.
13+
// So dynamically enable viewport support based on the user's VS Code version.
14+
const isViewPortSupported = compareVersions(version.replace(/-insider$/i, ""), "1.56.0") >= 0;
15+
16+
const protoConverter: ProtocolConverter.Converter = ProtocolConverter.createConverter();
17+
const codeConverter: CodeConverter.Converter = CodeConverter.createConverter();
18+
19+
export class JavaInlineValuesProvider implements InlineValuesProvider {
20+
21+
public async provideInlineValues(document: TextDocument, viewPort: Range, context: InlineValueContext): Promise<InlineValue[]> {
22+
const provideInlineValuesOperation = instrumentOperation("provideInlineValues", async (operationId) => {
23+
const resolveInlineVariablesStep = instrumentOperationStep(operationId, "resolveInlineVariables", async () => {
24+
return <InlineVariable[]> (await resolveInlineVariables({
25+
uri: document.uri.toString(),
26+
viewPort: isViewPortSupported ? codeConverter.asRange(viewPort) : undefined,
27+
stoppedLocation: codeConverter.asRange(context.stoppedLocation),
28+
}));
29+
});
30+
const variables: InlineVariable[] = await resolveInlineVariablesStep();
31+
32+
const resolveInlineValuesStep = instrumentOperationStep(operationId, "resolveInlineValues", async () => {
33+
if (!variables || !variables.length) {
34+
sendInfo(operationId, {
35+
inlineVariableCount: 0,
36+
});
37+
return [];
38+
}
39+
40+
const unresolvedVariables: any[] = variables.filter((variable) => variable.kind === InlineKind.Evaluation).map((variable) => {
41+
return {
42+
expression: variable.expression || variable.name,
43+
declaringClass: variable.declaringClass,
44+
};
45+
});
46+
sendInfo(operationId, {
47+
inlineVariableCount: variables.length,
48+
inlineVariableLookupCount: variables.length - unresolvedVariables.length,
49+
inlineVariableEvaluationCount: unresolvedVariables.length,
50+
});
51+
52+
let resolvedVariables: any;
53+
if (unresolvedVariables.length && debug.activeDebugSession) {
54+
const response = await debug.activeDebugSession.customRequest("inlineValues", {
55+
frameId: context.frameId,
56+
variables: unresolvedVariables,
57+
});
58+
resolvedVariables = response?.variables;
59+
}
60+
61+
const result: InlineValue[] = [];
62+
let next = 0;
63+
for (const variable of variables) {
64+
if (variable.kind === InlineKind.VariableLookup) {
65+
result.push(new InlineValueVariableLookup(protoConverter.asRange(variable.range), variable.name, true));
66+
} else if (resolvedVariables && resolvedVariables.length > next) {
67+
const resolvedValue = resolvedVariables[next++];
68+
if (resolvedValue) {
69+
result.push(new InlineValueText(protoConverter.asRange(variable.range), `${variable.name} = ${resolvedValue.value}`));
70+
} else {
71+
result.push(new InlineValueEvaluatableExpression(protoConverter.asRange(variable.range), variable.name));
72+
}
73+
} else {
74+
result.push(new InlineValueEvaluatableExpression(protoConverter.asRange(variable.range), variable.name));
75+
}
76+
}
77+
78+
return result;
79+
});
80+
return resolveInlineValuesStep();
81+
});
82+
83+
return provideInlineValuesOperation();
84+
}
85+
86+
}

src/commands.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ export const JAVA_RESOLVE_CLASSFILTERS = "vscode.java.resolveClassFilters";
4444

4545
export const JAVA_RESOLVE_SOURCE_URI = "vscode.java.resolveSourceUri";
4646

47+
export const JAVA_RESOLVE_INLINE_VARIABLES = "vscode.java.resolveInlineVariables";
48+
4749
export function executeJavaLanguageServerCommand(...rest: any[]) {
4850
return executeJavaExtensionCommand(JAVA_EXECUTE_WORKSPACE_COMMAND, ...rest);
4951
}

src/extension.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { initializeCodeLensProvider, startDebugging } from "./debugCodeLensProvi
1515
import { initExpService } from "./experimentationService";
1616
import { handleHotCodeReplaceCustomEvent, initializeHotCodeReplace, NO_BUTTON, YES_BUTTON } from "./hotCodeReplace";
1717
import { JavaDebugAdapterDescriptorFactory } from "./javaDebugAdapterDescriptorFactory";
18+
import { JavaInlineValuesProvider } from "./JavaInlineValueProvider";
1819
import { logJavaException, logJavaInfo } from "./javaLogger";
1920
import { IMainClassOption, IMainMethod, resolveMainMethod } from "./languageServerPlugin";
2021
import { logger, Type } from "./logger";
@@ -78,6 +79,7 @@ function initializeExtension(_operationId: string, context: vscode.ExtensionCont
7879
initializeCodeLensProvider(context);
7980
initializeThreadOperations(context);
8081

82+
context.subscriptions.push(vscode.languages.registerInlineValuesProvider("java", new JavaInlineValuesProvider()));
8183
return {
8284
progressProvider,
8385
};

src/languageServerPlugin.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the MIT license.
33

44
import * as vscode from "vscode";
5+
import { Range } from "vscode-languageserver-types";
56

67
import * as commands from "./commands";
78

@@ -116,3 +117,29 @@ export async function resolveClassFilters(patterns: string[]): Promise<string[]>
116117
export async function resolveSourceUri(line: string): Promise<string> {
117118
return <string> await commands.executeJavaLanguageServerCommand(commands.JAVA_RESOLVE_SOURCE_URI, line);
118119
}
120+
121+
export async function resolveInlineVariables(inlineParams: InlineParams): Promise<InlineVariable[]> {
122+
return <InlineVariable[]> await commands.executeJavaLanguageServerCommand(commands.JAVA_RESOLVE_INLINE_VARIABLES, JSON.stringify(inlineParams));
123+
}
124+
125+
// tslint:disable-next-line:interface-name
126+
export interface InlineParams {
127+
uri: string;
128+
viewPort?: Range;
129+
stoppedLocation: Range;
130+
}
131+
132+
// tslint:disable-next-line:interface-name
133+
export enum InlineKind {
134+
VariableLookup = 0,
135+
Evaluation = 1,
136+
}
137+
138+
// tslint:disable-next-line:interface-name
139+
export interface InlineVariable {
140+
range: Range;
141+
name: string;
142+
kind: InlineKind;
143+
expression: string;
144+
declaringClass: string;
145+
}

tslint.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
true,
3737
"log",
3838
"error"
39-
]
39+
],
40+
"no-submodule-imports": false
4041
}
4142
}

0 commit comments

Comments
 (0)