debug: better copying of eval expression results (#187951)

For #187784. Needs DAP methods for a complete solution.
This commit is contained in:
Connor Peet
2023-07-14 20:41:23 +02:00
committed by GitHub
parent 6c94c8e088
commit 5eb82ae6c4
4 changed files with 38 additions and 17 deletions
+28 -7
View File
@@ -19,13 +19,14 @@ import { HistoryNavigator } from 'vs/base/common/history';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import { removeAnsiEscapeCodes } from 'vs/base/common/strings';
import { ThemeIcon } from 'vs/base/common/themables';
import { URI as uri } from 'vs/base/common/uri';
import 'vs/css!./media/repl';
import { ICodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser';
import { EditorAction, registerEditorAction } from 'vs/editor/browser/editorExtensions';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget';
import { EditorOption, EDITOR_FONT_DEFAULTS } from 'vs/editor/common/config/editorOptions';
import { EDITOR_FONT_DEFAULTS, EditorOption } from 'vs/editor/common/config/editorOptions';
import { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
import { IDecorationOptions } from 'vs/editor/common/editorCommon';
@@ -55,7 +56,6 @@ import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storag
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { editorForeground, resolveColorValue } from 'vs/platform/theme/common/colorRegistry';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { ThemeIcon } from 'vs/base/common/themables';
import { FilterViewPane, IViewPaneOptions, ViewAction } from 'vs/workbench/browser/parts/views/viewPane';
import { IViewDescriptorService, IViewsService } from 'vs/workbench/common/views';
import { getSimpleCodeEditorWidgetOptions, getSimpleEditorOptions } from 'vs/workbench/contrib/codeEditor/browser/simpleEditorOptions';
@@ -63,10 +63,10 @@ import { FocusSessionActionViewItem } from 'vs/workbench/contrib/debug/browser/d
import { debugConsoleClearAll, debugConsoleEvaluationPrompt } from 'vs/workbench/contrib/debug/browser/debugIcons';
import { LinkDetector } from 'vs/workbench/contrib/debug/browser/linkDetector';
import { ReplFilter } from 'vs/workbench/contrib/debug/browser/replFilter';
import { ReplAccessibilityProvider, ReplDataSource, ReplDelegate, ReplEvaluationInputsRenderer, ReplEvaluationResultsRenderer, ReplGroupRenderer, ReplRawObjectsRenderer, ReplOutputElementRenderer, ReplVariablesRenderer } from 'vs/workbench/contrib/debug/browser/replViewer';
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';
import { ReplAccessibilityProvider, ReplDataSource, ReplDelegate, ReplEvaluationInputsRenderer, ReplEvaluationResultsRenderer, ReplGroupRenderer, ReplOutputElementRenderer, ReplRawObjectsRenderer, ReplVariablesRenderer } from 'vs/workbench/contrib/debug/browser/replViewer';
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';
import { Variable } from 'vs/workbench/contrib/debug/common/debugModel';
import { ReplGroup } from 'vs/workbench/contrib/debug/common/replModel';
import { ReplEvaluationResult, ReplGroup } from 'vs/workbench/contrib/debug/common/replModel';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
const $ = dom.$;
@@ -1045,12 +1045,33 @@ registerAction2(class extends Action2 {
async run(accessor: ServicesAccessor, element: IReplElement): Promise<void> {
const clipboardService = accessor.get(IClipboardService);
const debugService = accessor.get(IDebugService);
const nativeSelection = window.getSelection();
const selectedText = nativeSelection?.toString();
if (selectedText && selectedText.length > 0) {
await clipboardService.writeText(selectedText);
return clipboardService.writeText(selectedText);
} else if (element) {
await clipboardService.writeText(element.toString());
return clipboardService.writeText(await this.tryEvaluateAndCopy(debugService, element) || element.toString());
}
}
private async tryEvaluateAndCopy(debugService: IDebugService, element: IReplElement): Promise<string | undefined> {
// todo: we should expand DAP to allow copying more types here (#187784)
if (!(element instanceof ReplEvaluationResult)) {
return;
}
const stackFrame = debugService.getViewModel().focusedStackFrame;
const session = debugService.getViewModel().focusedSession;
if (!stackFrame || !session || !session.capabilities.supportsClipboardContext) {
return;
}
try {
const evaluation = await session.evaluate(element.originalExpression, stackFrame.frameId, 'clipboard');
return evaluation?.body.result;
} catch (e) {
return;
}
}
});
@@ -239,14 +239,14 @@ export class ReplVariablesRenderer extends AbstractExpressionsRenderer<IExpressi
public renderElement(node: ITreeNode<IExpression | ReplVariableElement, FuzzyScore>, _index: number, data: IExpressionTemplateData): void {
const element = node.element;
super.renderExpressionElement(element instanceof ReplVariableElement ? element.expr : element, node, data);
super.renderExpressionElement(element instanceof ReplVariableElement ? element.expression : element, node, data);
}
protected renderExpression(expression: IExpression | ReplVariableElement, data: IExpressionTemplateData, highlights: IHighlight[]): void {
const isReplVariable = expression instanceof ReplVariableElement;
if (isReplVariable || !expression.name) {
data.label.set('');
renderExpressionValue(isReplVariable ? expression.expr : expression, data.value, { showHover: false, colorize: true, linkDetector: this.linkDetector });
renderExpressionValue(isReplVariable ? expression.expression : expression, data.value, { showHover: false, colorize: true, linkDetector: this.linkDetector });
data.expression.classList.remove('nested-variable');
} else {
renderVariable(expression as Variable, data, true, highlights, this.linkDetector);
@@ -76,19 +76,19 @@ export class ReplVariableElement implements INestingReplElement {
private readonly id = generateUuid();
constructor(
public readonly expr: IExpression,
public readonly expression: IExpression,
public readonly severity: severity,
public readonly sourceData?: IReplElementSource,
) {
this.hasChildren = expr.hasChildren;
this.hasChildren = expression.hasChildren;
}
getChildren(): IReplElement[] | Promise<IReplElement[]> {
return this.expr.getChildren();
return this.expression.getChildren();
}
toString(): string {
return this.expr.toString();
return this.expression.toString();
}
getId(): string {
@@ -169,7 +169,7 @@ export class ReplEvaluationResult extends ExpressionContainer implements IReplEl
return this._available;
}
constructor() {
constructor(public readonly originalExpression: string) {
super(undefined, undefined, 0, generateUuid());
}
@@ -271,7 +271,7 @@ export class ReplModel {
async addReplExpression(session: IDebugSession, stackFrame: IStackFrame | undefined, name: string): Promise<void> {
this.addReplElement(new ReplEvaluationInput(name));
const result = new ReplEvaluationResult();
const result = new ReplEvaluationResult(name);
await result.evaluateExpression(name, session, stackFrame, 'repl');
this.addReplElement(result);
}
@@ -51,8 +51,8 @@ suite('Debug - REPL', () => {
const keyValueObject = { 'key1': 2, 'key2': 'value' };
repl.appendToRepl(session, { output: '', expression: new RawObjectReplElement('fakeid', 'fake', keyValueObject), sev: severity.Info });
const element = <ReplVariableElement>repl.getReplElements()[3];
assert.strictEqual(element.expr.value, 'Object');
assert.deepStrictEqual((element.expr as RawObjectReplElement).valueObj, keyValueObject);
assert.strictEqual(element.expression.value, 'Object');
assert.deepStrictEqual((element.expression as RawObjectReplElement).valueObj, keyValueObject);
repl.removeReplExpressions();
assert.strictEqual(repl.getReplElements().length, 0);