Skip to content

Commit 5eb82ae

Browse files
authored
debug: better copying of eval expression results (microsoft#187951)
For microsoft#187784. Needs DAP methods for a complete solution.
1 parent 6c94c8e commit 5eb82ae

File tree

4 files changed

+38
-17
lines changed

4 files changed

+38
-17
lines changed

src/vs/workbench/contrib/debug/browser/repl.ts

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,14 @@ import { HistoryNavigator } from 'vs/base/common/history';
1919
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
2020
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
2121
import { removeAnsiEscapeCodes } from 'vs/base/common/strings';
22+
import { ThemeIcon } from 'vs/base/common/themables';
2223
import { URI as uri } from 'vs/base/common/uri';
2324
import 'vs/css!./media/repl';
2425
import { ICodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser';
2526
import { EditorAction, registerEditorAction } from 'vs/editor/browser/editorExtensions';
2627
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
2728
import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget';
28-
import { EditorOption, EDITOR_FONT_DEFAULTS } from 'vs/editor/common/config/editorOptions';
29+
import { EDITOR_FONT_DEFAULTS, EditorOption } from 'vs/editor/common/config/editorOptions';
2930
import { Position } from 'vs/editor/common/core/position';
3031
import { Range } from 'vs/editor/common/core/range';
3132
import { IDecorationOptions } from 'vs/editor/common/editorCommon';
@@ -55,18 +56,17 @@ import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storag
5556
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
5657
import { editorForeground, resolveColorValue } from 'vs/platform/theme/common/colorRegistry';
5758
import { IThemeService } from 'vs/platform/theme/common/themeService';
58-
import { ThemeIcon } from 'vs/base/common/themables';
5959
import { FilterViewPane, IViewPaneOptions, ViewAction } from 'vs/workbench/browser/parts/views/viewPane';
6060
import { IViewDescriptorService, IViewsService } from 'vs/workbench/common/views';
6161
import { getSimpleCodeEditorWidgetOptions, getSimpleEditorOptions } from 'vs/workbench/contrib/codeEditor/browser/simpleEditorOptions';
6262
import { FocusSessionActionViewItem } from 'vs/workbench/contrib/debug/browser/debugActionViewItems';
6363
import { debugConsoleClearAll, debugConsoleEvaluationPrompt } from 'vs/workbench/contrib/debug/browser/debugIcons';
6464
import { LinkDetector } from 'vs/workbench/contrib/debug/browser/linkDetector';
6565
import { ReplFilter } from 'vs/workbench/contrib/debug/browser/replFilter';
66-
import { ReplAccessibilityProvider, ReplDataSource, ReplDelegate, ReplEvaluationInputsRenderer, ReplEvaluationResultsRenderer, ReplGroupRenderer, ReplRawObjectsRenderer, ReplOutputElementRenderer, ReplVariablesRenderer } from 'vs/workbench/contrib/debug/browser/replViewer';
67-
import { CONTEXT_DEBUG_STATE, CONTEXT_IN_DEBUG_REPL, CONTEXT_MULTI_SESSION_REPL, DEBUG_SCHEME, getStateLabel, IDebugConfiguration, IDebugService, IDebugSession, IReplConfiguration, IReplElement, IReplOptions, REPL_VIEW_ID, State } from 'vs/workbench/contrib/debug/common/debug';
66+
import { ReplAccessibilityProvider, ReplDataSource, ReplDelegate, ReplEvaluationInputsRenderer, ReplEvaluationResultsRenderer, ReplGroupRenderer, ReplOutputElementRenderer, ReplRawObjectsRenderer, ReplVariablesRenderer } from 'vs/workbench/contrib/debug/browser/replViewer';
67+
import { CONTEXT_DEBUG_STATE, CONTEXT_IN_DEBUG_REPL, CONTEXT_MULTI_SESSION_REPL, DEBUG_SCHEME, IDebugConfiguration, IDebugService, IDebugSession, IReplConfiguration, IReplElement, IReplOptions, REPL_VIEW_ID, State, getStateLabel } from 'vs/workbench/contrib/debug/common/debug';
6868
import { Variable } from 'vs/workbench/contrib/debug/common/debugModel';
69-
import { ReplGroup } from 'vs/workbench/contrib/debug/common/replModel';
69+
import { ReplEvaluationResult, ReplGroup } from 'vs/workbench/contrib/debug/common/replModel';
7070
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
7171

7272
const $ = dom.$;
@@ -1045,12 +1045,33 @@ registerAction2(class extends Action2 {
10451045

10461046
async run(accessor: ServicesAccessor, element: IReplElement): Promise<void> {
10471047
const clipboardService = accessor.get(IClipboardService);
1048+
const debugService = accessor.get(IDebugService);
10481049
const nativeSelection = window.getSelection();
10491050
const selectedText = nativeSelection?.toString();
10501051
if (selectedText && selectedText.length > 0) {
1051-
await clipboardService.writeText(selectedText);
1052+
return clipboardService.writeText(selectedText);
10521053
} else if (element) {
1053-
await clipboardService.writeText(element.toString());
1054+
return clipboardService.writeText(await this.tryEvaluateAndCopy(debugService, element) || element.toString());
1055+
}
1056+
}
1057+
1058+
private async tryEvaluateAndCopy(debugService: IDebugService, element: IReplElement): Promise<string | undefined> {
1059+
// todo: we should expand DAP to allow copying more types here (#187784)
1060+
if (!(element instanceof ReplEvaluationResult)) {
1061+
return;
1062+
}
1063+
1064+
const stackFrame = debugService.getViewModel().focusedStackFrame;
1065+
const session = debugService.getViewModel().focusedSession;
1066+
if (!stackFrame || !session || !session.capabilities.supportsClipboardContext) {
1067+
return;
1068+
}
1069+
1070+
try {
1071+
const evaluation = await session.evaluate(element.originalExpression, stackFrame.frameId, 'clipboard');
1072+
return evaluation?.body.result;
1073+
} catch (e) {
1074+
return;
10541075
}
10551076
}
10561077
});

src/vs/workbench/contrib/debug/browser/replViewer.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -239,14 +239,14 @@ export class ReplVariablesRenderer extends AbstractExpressionsRenderer<IExpressi
239239

240240
public renderElement(node: ITreeNode<IExpression | ReplVariableElement, FuzzyScore>, _index: number, data: IExpressionTemplateData): void {
241241
const element = node.element;
242-
super.renderExpressionElement(element instanceof ReplVariableElement ? element.expr : element, node, data);
242+
super.renderExpressionElement(element instanceof ReplVariableElement ? element.expression : element, node, data);
243243
}
244244

245245
protected renderExpression(expression: IExpression | ReplVariableElement, data: IExpressionTemplateData, highlights: IHighlight[]): void {
246246
const isReplVariable = expression instanceof ReplVariableElement;
247247
if (isReplVariable || !expression.name) {
248248
data.label.set('');
249-
renderExpressionValue(isReplVariable ? expression.expr : expression, data.value, { showHover: false, colorize: true, linkDetector: this.linkDetector });
249+
renderExpressionValue(isReplVariable ? expression.expression : expression, data.value, { showHover: false, colorize: true, linkDetector: this.linkDetector });
250250
data.expression.classList.remove('nested-variable');
251251
} else {
252252
renderVariable(expression as Variable, data, true, highlights, this.linkDetector);

src/vs/workbench/contrib/debug/common/replModel.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -76,19 +76,19 @@ export class ReplVariableElement implements INestingReplElement {
7676
private readonly id = generateUuid();
7777

7878
constructor(
79-
public readonly expr: IExpression,
79+
public readonly expression: IExpression,
8080
public readonly severity: severity,
8181
public readonly sourceData?: IReplElementSource,
8282
) {
83-
this.hasChildren = expr.hasChildren;
83+
this.hasChildren = expression.hasChildren;
8484
}
8585

8686
getChildren(): IReplElement[] | Promise<IReplElement[]> {
87-
return this.expr.getChildren();
87+
return this.expression.getChildren();
8888
}
8989

9090
toString(): string {
91-
return this.expr.toString();
91+
return this.expression.toString();
9292
}
9393

9494
getId(): string {
@@ -169,7 +169,7 @@ export class ReplEvaluationResult extends ExpressionContainer implements IReplEl
169169
return this._available;
170170
}
171171

172-
constructor() {
172+
constructor(public readonly originalExpression: string) {
173173
super(undefined, undefined, 0, generateUuid());
174174
}
175175

@@ -271,7 +271,7 @@ export class ReplModel {
271271

272272
async addReplExpression(session: IDebugSession, stackFrame: IStackFrame | undefined, name: string): Promise<void> {
273273
this.addReplElement(new ReplEvaluationInput(name));
274-
const result = new ReplEvaluationResult();
274+
const result = new ReplEvaluationResult(name);
275275
await result.evaluateExpression(name, session, stackFrame, 'repl');
276276
this.addReplElement(result);
277277
}

src/vs/workbench/contrib/debug/test/browser/repl.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ suite('Debug - REPL', () => {
5151
const keyValueObject = { 'key1': 2, 'key2': 'value' };
5252
repl.appendToRepl(session, { output: '', expression: new RawObjectReplElement('fakeid', 'fake', keyValueObject), sev: severity.Info });
5353
const element = <ReplVariableElement>repl.getReplElements()[3];
54-
assert.strictEqual(element.expr.value, 'Object');
55-
assert.deepStrictEqual((element.expr as RawObjectReplElement).valueObj, keyValueObject);
54+
assert.strictEqual(element.expression.value, 'Object');
55+
assert.deepStrictEqual((element.expression as RawObjectReplElement).valueObj, keyValueObject);
5656

5757
repl.removeReplExpressions();
5858
assert.strictEqual(repl.getReplElements().length, 0);

0 commit comments

Comments
 (0)