From 1625574f8061b5687cd0b7bd402348b3b3f056e4 Mon Sep 17 00:00:00 2001 From: Haneef Mohammed Date: Thu, 9 Mar 2023 14:12:05 -0800 Subject: [PATCH] Edit, move up/down. Add to Live Watch from variables window --- CHANGELOG.md | 11 ++++ package.json | 72 ++++++++++++++++++++-- src/backend/symbols.ts | 4 +- src/frontend/extension.ts | 57 ++++++++++++++--- src/frontend/views/live-watch.ts | 102 ++++++++++++++++++++++++++++++- src/live-watch-monitor.ts | 4 +- 6 files changed, 229 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a5103da0..7d7d7111 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,17 @@ ChangeLog ========= +# V1.9.2 - Major release only available as a Pre-release. +* Many features to Live Watch added + * You can now edit the expression + * You can move up/down the expression (they rotate if at edges) + * You can enable the global hex mode or use the hex mode on one expression at a time just like you can do it in the `WATCH` window + * To enable hex mode on a single expression, simply add a `,x` at the end of the expression. You can also do octal by using `,o`. However, this only works for the scalar variables and if it is a pinter or an array, it is not inherited by its children +* There is now a button on the `VARIABLES`, `WATCH` and `CORTEX LIVE WATCH` panels title bar to enable/disable Hex display mode globally. You can still override on individual expressions. +* Increased the number of threads (256) and frames (4K) you can have. Such limits should not even exist but it is an artifact of how VSCode works + +# V1.9.1 - Major release only available as a Pre-release. +* Oops. The context menu in the Variables window for enable/disable Hex had disappeared. # V1.9.1 - Major release only available as a Pre-release. * The Registers Panel is now removed. We had deprecated this window a year ago as it would not give you accurate register values based on the Program/Thread/Frame. The accurate version of the Registers have been available in the VARIABLES panel for almost a year now. * Fixed some issue when the Live Watch window was empty or variables added while there wasn't a debug session sometimes would not show diff --git a/package.json b/package.json index 7a0c7b65..ec85d519 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "version": "1.9.1", + "version": "1.9.2", "preview": false, "activationEvents": [ "onDebugResolve:cortex-debug", @@ -420,6 +420,26 @@ "title": "Remove expression", "icon": "$(close)" }, + { + "command": "cortex-debug.liveWatch.editExpr", + "title": "Edit expression", + "icon": "$(edit)" + }, + { + "command": "cortex-debug.liveWatch.moveUp", + "title": "Move expression up", + "icon": "$(arrow-up)" + }, + { + "command": "cortex-debug.liveWatch.moveDown", + "title": "Move expression down", + "icon": "$(arrow-down)" + }, + { + "command": "cortex-debug.liveWatch.addToLiveWatch", + "title": "Add to Live Watch", + "icon": "$(plus)" + }, { "category": "Cortex-Debug", "command": "cortex-debug.examineMemory", @@ -2889,6 +2909,21 @@ } ], "view/item/context": [ + { + "command": "cortex-debug.liveWatch.editExpr", + "when": "view == cortex-debug.liveWatch && viewItem == expression", + "group": "inline" + }, + { + "command": "cortex-debug.liveWatch.moveUp", + "when": "view == cortex-debug.liveWatch && viewItem == expression", + "group": "inline" + }, + { + "command": "cortex-debug.liveWatch.moveDown", + "when": "view == cortex-debug.liveWatch && viewItem == expression", + "group": "inline" + }, { "command": "cortex-debug.liveWatch.removeExpr", "when": "view == cortex-debug.liveWatch && viewItem == expression", @@ -2900,17 +2935,42 @@ "command": "cortex-debug.liveWatch.addExpr", "when": "view == cortex-debug.liveWatch", "group": "navigation" - } - ], - "debug/variables/title": [ + }, + { + "command": "cortex-debug.varHexModeTurnOn", + "when": "view == workbench.debug.variablesView && inDebugMode && debugType == 'cortex-debug' && debugState == stopped && cortex-debug:variableUseNaturalFormat", + "group": "navigation" + }, + { + "command": "cortex-debug.varHexModeTurnOff", + "when": "view == workbench.debug.variablesView && inDebugMode && debugType == 'cortex-debug' && debugState == stopped && !cortex-debug:variableUseNaturalFormat", + "group": "navigation" + }, { "command": "cortex-debug.varHexModeTurnOn", - "when": "inDebugMode && debugType == 'cortex-debug' && debugState == stopped && cortex-debug:variableUseNaturalFormat", + "when": "view == workbench.debug.watchExpressionsView && inDebugMode && debugType == 'cortex-debug' && debugState == stopped && cortex-debug:variableUseNaturalFormat", + "group": "navigation" + }, + { + "command": "cortex-debug.varHexModeTurnOff", + "when": "view == workbench.debug.watchExpressionsView && inDebugMode && debugType == 'cortex-debug' && debugState == stopped && !cortex-debug:variableUseNaturalFormat", "group": "navigation" }, + { + "command": "cortex-debug.varHexModeTurnOn", + "when": "view == cortex-debug.liveWatch && inDebugMode && debugType == 'cortex-debug' && debugState == stopped && cortex-debug:variableUseNaturalFormat", + "group": "navigation" + }, { "command": "cortex-debug.varHexModeTurnOff", - "when": "inDebugMode && debugType == 'cortex-debug' && debugState == stopped && !cortex-debug:variableUseNaturalFormat", + "when": "view == cortex-debug.liveWatch && inDebugMode && debugType == 'cortex-debug' && debugState == stopped && !cortex-debug:variableUseNaturalFormat", + "group": "navigation" + } + ], + "debug/variables/context": [ + { + "command": "cortex-debug.liveWatch.addToLiveWatch", + "when": "inDebugMode && debugType == 'cortex-debug' && debugState == stopped", "group": "navigation" } ] diff --git a/src/backend/symbols.ts b/src/backend/symbols.ts index 15dedbc7..787fa3f2 100644 --- a/src/backend/symbols.ts +++ b/src/backend/symbols.ts @@ -443,9 +443,9 @@ export class SymbolTable { const secName = match[9].trim(); const size = parseInt(match[10], 16); - if ((size === 0) && ((secName === '*ABS*') || (secName === '*UND*'))) { + if ((secName === '*ABS*') || (secName === '*UND*')) { // These are not true symbols, AFAIK and can be safely ignored as there can be hundreds of these - // junk symbols + // junk symbols. We already handled file names above return true; } diff --git a/src/frontend/extension.ts b/src/frontend/extension.ts index f4f4075a..04b5054f 100644 --- a/src/frontend/extension.ts +++ b/src/frontend/extension.ts @@ -64,7 +64,7 @@ export class CortexDebugExtension { Reporting.activate(context); - this.initLiveWatcher(); + this.liveWatchProvider = new LiveWatchTreeProvider(this.context); this.liveWatchTreeView = vscode.window.createTreeView('cortex-debug.liveWatch', { treeDataProvider: this.liveWatchProvider }); @@ -90,6 +90,10 @@ export class CortexDebugExtension { vscode.commands.registerCommand('cortex-debug.liveWatch.addExpr', this.addLiveWatchExpr.bind(this)), vscode.commands.registerCommand('cortex-debug.liveWatch.removeExpr', this.removeLiveWatchExpr.bind(this)), + vscode.commands.registerCommand('cortex-debug.liveWatch.editExpr', this.editLiveWatchExpr.bind(this)), + vscode.commands.registerCommand('cortex-debug.liveWatch.addToLiveWatch', this.addToLiveWatch.bind(this)), + vscode.commands.registerCommand('cortex-debug.liveWatch.moveUp', this.moveUpLiveWatchExpr.bind(this)), + vscode.commands.registerCommand('cortex-debug.liveWatch.moveDown', this.moveDownLiveWatchExpr.bind(this)), vscode.workspace.onDidChangeConfiguration(this.settingsChanged.bind(this)), vscode.debug.onDidReceiveDebugSessionCustomEvent(this.receivedCustomEvent.bind(this)), @@ -200,7 +204,11 @@ export class CortexDebugExtension { try { // Session may not have actually started according to VSCode but we know of it if (this.isDebugging(s.session)) { - s.session.customRequest('set-var-format', { hex: isHex }); + s.session.customRequest('set-var-format', { hex: isHex }).then(() => { + if (s.status === 'stopped') { + this.liveWatchProvider?.refresh(s.session); + } + }); if (s.status === 'stopped') { foundStopped = true; } @@ -979,26 +987,57 @@ export class CortexDebugExtension { private addLiveWatchExpr() { vscode.window.showInputBox({ - placeHolder: 'Enter a valid C/gdb expression. Must be a global variable type expression', + placeHolder: 'Enter a valid C/gdb expression. Must be a global variable expression', ignoreFocusOut: true, prompt: 'Enter Live Watch Expression' }).then((v) => { if (v) { - this.initLiveWatcher(); this.liveWatchProvider.addWatchExpr(v, vscode.debug.activeDebugSession); } }); } + private addToLiveWatch(arg: any) { + if (!arg || !arg.sessionId) { + return; + } + const mySession = CDebugSession.FindSessionById(arg.sessionId); + if (!mySession) { + vscode.window.showErrorMessage(`addToLiveWatch: Unknown debug session id ${arg.sessionId}`); + return; + } + console.log(arg); + const parent = arg.container; + const expr = arg.variable?.evaluateName; + if (parent && expr) { + const varRef = parent.variablesReference; + mySession.session.customRequest('is-global-or-static', {varRef: varRef}).then((result) => { + console.log(expr, varRef, result); + if (!result.success) { + vscode.window.showErrorMessage(`Cannot add ${expr} to Live Watch. Must be a global or static variable`); + } else { + this.liveWatchProvider.addWatchExpr(expr, vscode.debug.activeDebugSession); + } + }, (e) => { + console.log(e); + }); + } + } + private removeLiveWatchExpr(node: any) { - this.initLiveWatcher(); this.liveWatchProvider.removeWatchExpr(node); } - private initLiveWatcher() { - if (!this.liveWatchProvider) { - this.liveWatchProvider = new LiveWatchTreeProvider(this.context); - } + private editLiveWatchExpr(node: any) { + this.liveWatchProvider.editNode(node); + } + + private moveUpLiveWatchExpr(node: any) { + this.liveWatchProvider.moveUpNode(node); + } + + private moveDownLiveWatchExpr(node: any) { + this.liveWatchProvider.moveDownNode(node); } } diff --git a/src/frontend/views/live-watch.ts b/src/frontend/views/live-watch.ts index 0633730e..ff388385 100644 --- a/src/frontend/views/live-watch.ts +++ b/src/frontend/views/live-watch.ts @@ -49,6 +49,25 @@ export class LiveVariableNode extends BaseNode { return node && (node.getParent() === undefined); } + public rename(nm: string) { + if (this.isRootChild()) { + this.name = this.expr = nm; + } + } + + public getName() { + return this.name; + } + + public findName(str: string): LiveVariableNode | undefined { + for (const child of this.children || []) { + if (child.name === str) { + return child; + } + } + return undefined; + } + public getTreeItem(): TreeItem | Promise { const state = this.variablesReference || (this.children?.length > 0) ? (this.children?.length > 0 ? TreeItemCollapsibleState.Expanded : TreeItemCollapsibleState.Collapsed) : TreeItemCollapsibleState.None; @@ -80,8 +99,8 @@ export class LiveVariableNode extends BaseNode { return child; } - public removeChild(obj: any) { - const node = obj as LiveVariableNode; + public removeChild(node: LiveVariableNode): boolean { + if (!node || !node.isRootChild()) { return false; } let ix = 0; for (const child of this.children || []) { if (child.name === node.name) { @@ -93,6 +112,47 @@ export class LiveVariableNode extends BaseNode { return false; } + public moveUpChild(node: LiveVariableNode): boolean { + if (!node || !node.isRootChild()) { return false; } + let ix = 0; + for (const child of this.children || []) { + if (child.name === node.name) { + if (ix > 0) { + const prev = this.children[ix - 1]; + this.children[ix] = prev; + this.children[ix - 1] = child; + } else { + const first = this.children.shift(); + this.children.push(first); + } + return true; + } + ix++; + } + return false; + } + + public moveDownChild(node: LiveVariableNode): boolean { + if (!node || !node.isRootChild()) { return false; } + let ix = 0; + const last = this.children ? this.children.length - 1 : -1; + for (const child of this.children || []) { + if (child.name === node.name) { + if (ix !== last) { + const next = this.children[ix + 1]; + this.children[ix] = next; + this.children[ix + 1] = child; + } else { + const last = this.children.pop(); + this.children.unshift(last); + } + return true; + } + ix++; + } + return false; + } + public reset(valuesToo = true) { this.session = undefined; if (valuesToo) { @@ -516,6 +576,44 @@ export class LiveWatchTreeProvider implements TreeDataProvider } } + public editNode(node: LiveVariableNode) { + if (!node.isRootChild()) { + return; // Should never happen + } + const opts: vscode.InputBoxOptions = { + placeHolder: 'Enter a valid C/gdb expression. Must be a global variable expression', + ignoreFocusOut: true, + value: node.getName(), + prompt: 'Enter Live Watch Expression' + }; + vscode.window.showInputBox(opts).then((result) => { + result = result ? result.trim() : result; + if (result && (result !== node.getName())) { + if (this.variables.findName(result)) { + vscode.window.showInformationMessage(`Live Watch: Expression ${result} is already being watched`); + } else { + node.rename(result); + this.saveState(); + this.refresh(LiveWatchTreeProvider.session); + } + } + }); + } + + public moveUpNode(node: LiveVariableNode) { + const parent = node?.getParent() as LiveVariableNode; + if (parent && parent.moveUpChild(node)) { + this.fire(); + } + } + + public moveDownNode(node: LiveVariableNode) { + const parent = node?.getParent() as LiveVariableNode; + if (parent && parent.moveDownChild(node)) { + this.fire(); + } + } + public expandChildren(element: LiveVariableNode) { if (element) { element.expandChildren().then(() => { diff --git a/src/live-watch-monitor.ts b/src/live-watch-monitor.ts index 09098d1e..f57d9f55 100644 --- a/src/live-watch-monitor.ts +++ b/src/live-watch-monitor.ts @@ -1,7 +1,7 @@ import { DebugProtocol } from '@vscode/debugprotocol'; import { Handles } from '@vscode/debugadapter'; import { MI2 } from './backend/mi2/mi2'; -import { ExtendedVariable, GDBDebugSession, RequestQueue } from './gdb'; +import { decodeReference, ExtendedVariable, GDBDebugSession, RequestQueue } from './gdb'; import { MIError, VariableObject } from './backend/backend'; import * as crypto from 'crypto'; import { MINode } from './backend/mi_parse'; @@ -109,7 +109,7 @@ export class VariablesHandler { threadId = frameId = -1; args.frameId = undefined; } else if (args.frameId !== undefined) { - [threadId, frameId] = GDBDebugSession.decodeReference(args.frameId); + [threadId, frameId] = decodeReference(args.frameId); } if (args.context !== 'repl') {