Skip to content

Commit 765e0d8

Browse files
Zhiwen Daidull-bird
authored andcommitted
feat(debugmate): Add "Add to Group" functionality and enhance variable categorization
- Introduce a new command to add unpaired variables to existing groups, improving user interaction with variable management. - Implement a new CVCategoryGroup class to categorize variables into distinct groups (Images, Plot, Points) for better organization. - Update CVVariablesProvider to support the new category structure and enhance the retrieval of child elements. - Refactor variable pairing logic to improve the user experience when selecting variables for pairing and grouping. - Enhance the panel management logic to ensure accurate state tracking and refresh behavior across different contexts.
1 parent 4197820 commit 765e0d8

5 files changed

Lines changed: 267 additions & 54 deletions

File tree

package.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,11 @@
5858
"command": "cv-debugmate.unpairVariable",
5959
"title": "Unpair",
6060
"icon": "$(link-external)"
61+
},
62+
{
63+
"command": "cv-debugmate.addToGroup",
64+
"title": "Add to Group",
65+
"icon": "$(plus)"
6166
}
6267
],
6368
"views": {
@@ -108,6 +113,11 @@
108113
"command": "cv-debugmate.unpairVariable",
109114
"when": "view == cv-debugmate-variables && (viewItem =~ /^cvVariablePaired:(mat|pointcloud)/)",
110115
"group": "1_pairing"
116+
},
117+
{
118+
"command": "cv-debugmate.addToGroup",
119+
"when": "view == cv-debugmate-variables && viewItem == 'cvGroup:paired'",
120+
"group": "inline@1"
111121
}
112122
]
113123
}

src/cvVariablesProvider.ts

Lines changed: 107 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,27 @@ export class CVVariable extends vscode.TreeItem {
118118
}
119119
}
120120

121+
export class CVCategoryGroup extends vscode.TreeItem {
122+
constructor(
123+
public readonly label: string,
124+
public readonly collapsibleState: vscode.TreeItemCollapsibleState,
125+
public readonly kind: 'mat' | 'plot' | 'pointcloud',
126+
public readonly children: (CVGroup | CVVariable)[]
127+
) {
128+
super(label, collapsibleState);
129+
this.contextValue = `cvCategory:${kind}`;
130+
131+
// Set icon based on category type
132+
if (kind === 'mat') {
133+
this.iconPath = new vscode.ThemeIcon('file-media');
134+
} else if (kind === 'plot') {
135+
this.iconPath = new vscode.ThemeIcon('graph');
136+
} else if (kind === 'pointcloud') {
137+
this.iconPath = new vscode.ThemeIcon('layers');
138+
}
139+
}
140+
}
141+
121142
export class CVGroup extends vscode.TreeItem {
122143
constructor(
123144
public readonly label: string,
@@ -126,7 +147,8 @@ export class CVGroup extends vscode.TreeItem {
126147
public readonly groupIndex?: number
127148
) {
128149
super(label, collapsibleState);
129-
this.contextValue = 'cvGroup';
150+
// Only paired groups (with groupIndex) can have add button
151+
this.contextValue = groupIndex !== undefined ? 'cvGroup:paired' : 'cvGroup';
130152

131153
if (groupIndex !== undefined) {
132154
const color = COLORS[groupIndex % COLORS.length];
@@ -137,9 +159,9 @@ export class CVGroup extends vscode.TreeItem {
137159
}
138160
}
139161

140-
export class CVVariablesProvider implements vscode.TreeDataProvider<CVVariable | CVGroup> {
141-
private _onDidChangeTreeData: vscode.EventEmitter<CVVariable | CVGroup | undefined | void> = new vscode.EventEmitter<CVVariable | CVGroup | undefined | void>();
142-
readonly onDidChangeTreeData: vscode.Event<CVVariable | CVGroup | undefined | void> = this._onDidChangeTreeData.event;
162+
export class CVVariablesProvider implements vscode.TreeDataProvider<CVVariable | CVGroup | CVCategoryGroup> {
163+
private _onDidChangeTreeData: vscode.EventEmitter<CVVariable | CVGroup | CVCategoryGroup | undefined | void> = new vscode.EventEmitter<CVVariable | CVGroup | CVCategoryGroup | undefined | void>();
164+
readonly onDidChangeTreeData: vscode.Event<CVVariable | CVGroup | CVCategoryGroup | undefined | void> = this._onDidChangeTreeData.event;
143165

144166
private variables: CVVariable[] = [];
145167
private groups: CVGroup[] = [];
@@ -166,19 +188,38 @@ export class CVVariablesProvider implements vscode.TreeDataProvider<CVVariable |
166188
return this.variables;
167189
}
168190

169-
getTreeItem(element: CVVariable | CVGroup): vscode.TreeItem {
191+
getTreeItem(element: CVVariable | CVGroup | CVCategoryGroup): vscode.TreeItem {
170192
return element;
171193
}
172194

173-
async getChildren(element?: CVVariable | CVGroup): Promise<(CVVariable | CVGroup)[]> {
195+
async getChildren(element?: CVVariable | CVGroup | CVCategoryGroup): Promise<(CVVariable | CVGroup | CVCategoryGroup)[]> {
196+
// Level 3: Variable children (no children)
197+
if (element instanceof CVVariable) {
198+
return [];
199+
}
200+
201+
// Level 2: Group children (return variables in the group)
174202
if (element instanceof CVGroup) {
175203
return element.variables;
176204
}
177205

178-
if (element instanceof CVVariable) {
179-
return [];
206+
// Level 1: Category children (return groups + unpaired variables)
207+
if (element instanceof CVCategoryGroup) {
208+
const result: (CVGroup | CVVariable)[] = [];
209+
210+
// Add paired groups
211+
const pairedGroups = element.children.filter(c => c instanceof CVGroup && (c as CVGroup).groupIndex !== undefined) as CVGroup[];
212+
pairedGroups.sort((a, b) => (a.groupIndex || 0) - (b.groupIndex || 0));
213+
result.push(...pairedGroups);
214+
215+
// Add unpaired variables
216+
const unpairedVars = element.children.filter(c => c instanceof CVVariable && !(c as CVVariable).isPaired) as CVVariable[];
217+
result.push(...unpairedVars);
218+
219+
return result;
180220
}
181-
221+
222+
// Level 0: Root (return category groups)
182223
const debugSession = vscode.debug.activeDebugSession;
183224
if (!debugSession) {
184225
this.variables = [];
@@ -611,39 +652,73 @@ export class CVVariablesProvider implements vscode.TreeDataProvider<CVVariable |
611652

612653
this.variables = visualizableVariables;
613654

614-
const groupMap = new Map<number | undefined, CVVariable[]>();
615-
for (const v of visualizableVariables) {
616-
const idx = v.groupIndex;
617-
if (!groupMap.has(idx)) {
618-
groupMap.set(idx, []);
655+
// Separate variables by type
656+
const matVars = visualizableVariables.filter(v => v.kind === 'mat');
657+
const plotVars = visualizableVariables.filter(v => v.kind === 'plot');
658+
const pointcloudVars = visualizableVariables.filter(v => v.kind === 'pointcloud');
659+
660+
const resultCategories: CVCategoryGroup[] = [];
661+
662+
// 1. Images (2D) category
663+
if (matVars.length > 0) {
664+
const matChildren: (CVGroup | CVVariable)[] = [];
665+
666+
// Group paired mat variables
667+
const groupMap = new Map<number, CVVariable[]>();
668+
for (const v of matVars) {
669+
if (v.isPaired && v.groupIndex !== undefined) {
670+
if (!groupMap.has(v.groupIndex)) {
671+
groupMap.set(v.groupIndex, []);
672+
}
673+
groupMap.get(v.groupIndex)!.push(v);
674+
}
619675
}
620-
groupMap.get(idx)!.push(v);
676+
677+
// Create paired groups
678+
const sortedIndices = Array.from(groupMap.keys()).sort((a, b) => a - b);
679+
for (const idx of sortedIndices) {
680+
const vars = groupMap.get(idx)!;
681+
matChildren.push(new CVGroup(
682+
`Group ${idx + 1}`,
683+
vscode.TreeItemCollapsibleState.Expanded,
684+
vars,
685+
idx
686+
));
687+
}
688+
689+
// Add unpaired mat variables
690+
const unpairedMatVars = matVars.filter(v => !v.isPaired);
691+
matChildren.push(...unpairedMatVars);
692+
693+
resultCategories.push(new CVCategoryGroup(
694+
'Images (2D)',
695+
vscode.TreeItemCollapsibleState.Expanded,
696+
'mat',
697+
matChildren
698+
));
621699
}
622-
623-
const resultGroups: CVGroup[] = [];
624-
const sortedIndices = Array.from(groupMap.keys())
625-
.filter((idx): idx is number => idx !== undefined)
626-
.sort((a, b) => a - b);
627700

628-
for (const idx of sortedIndices) {
629-
resultGroups.push(new CVGroup(
630-
`Group ${idx + 1}`,
701+
// 2. Plot (1D) category
702+
if (plotVars.length > 0) {
703+
resultCategories.push(new CVCategoryGroup(
704+
'Plot (1D)',
631705
vscode.TreeItemCollapsibleState.Expanded,
632-
groupMap.get(idx)!,
633-
idx
706+
'plot',
707+
plotVars
634708
));
635709
}
636-
637-
const ungrouped = groupMap.get(undefined);
638-
if (ungrouped && ungrouped.length > 0) {
639-
resultGroups.push(new CVGroup(
640-
'(ungrouped)',
710+
711+
// 3. Points (3D) category
712+
if (pointcloudVars.length > 0) {
713+
resultCategories.push(new CVCategoryGroup(
714+
'Points (3D)',
641715
vscode.TreeItemCollapsibleState.Expanded,
642-
ungrouped
716+
'pointcloud',
717+
pointcloudVars
643718
));
644719
}
645720

646-
return resultGroups;
721+
return resultCategories;
647722
} catch (error) {
648723
console.error('Error fetching variables:', error);
649724
this.variables = [];

src/extension.ts

Lines changed: 103 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { getEvaluateContext, is2DStdArrayEnhanced, is2DCStyleArrayEnhanced, is1D
33
import { drawPointCloud, drawStdArrayPointCloud } from "./pointCloud/pointCloudProvider";
44
import { drawMatImage, drawMatxImage, draw2DStdArrayImage, draw3DArrayImage } from "./matImage/matProvider";
55
import { drawPlot, drawStdArrayPlot, drawCStyleArrayPlot } from "./plot/plotProvider";
6-
import { CVVariablesProvider, CVVariable } from "./cvVariablesProvider";
6+
import { CVVariablesProvider, CVVariable, CVGroup } from "./cvVariablesProvider";
77
import { PanelManager } from "./utils/panelManager";
88
import { SyncManager } from "./utils/syncManager";
99
import { isPoint3Vector, isMat, is1DVector, isLikely1DMat, is1DSet, isMatx, is2DStdArray, is1DStdArray, isPoint3StdArray, is2DCStyleArray, is1DCStyleArray, is3DCStyleArray, is3DStdArray, isUninitializedOrInvalid, isUninitializedMat, isUninitializedMatFromChildren, isUninitializedVector, isPointerType, getPointerEvaluateExpression } from "./utils/opencv";
@@ -146,25 +146,79 @@ export function activate(context: vscode.ExtensionContext) {
146146
context.subscriptions.push(
147147
vscode.commands.registerCommand("cv-debugmate.pairVariable", async (cvVar: CVVariable) => {
148148
const variables = cvVariablesProvider.getVariables();
149-
const options = variables
150-
.filter(v => v.name !== cvVar.name && v.kind === cvVar.kind)
151-
.map(v => ({
152-
label: v.name,
153-
description: v.sizeInfo ? `[${v.sizeInfo}]` : v.isEmpty ? '[empty]' : ''
154-
}));
155-
156-
if (options.length === 0) {
149+
const sameKindVars = variables.filter(v => v.name !== cvVar.name && v.kind === cvVar.kind);
150+
151+
if (sameKindVars.length === 0) {
157152
vscode.window.showInformationMessage(`No other ${cvVar.kind === 'mat' ? 'image' : 'point cloud'} variables found to pair with.`);
158153
return;
159154
}
155+
156+
// Organize variables by groups (matching the tree structure)
157+
const groupMap = new Map<number | undefined, CVVariable[]>();
158+
for (const v of sameKindVars) {
159+
const idx = v.groupIndex;
160+
if (!groupMap.has(idx)) {
161+
groupMap.set(idx, []);
162+
}
163+
groupMap.get(idx)!.push(v);
164+
}
165+
166+
const options: vscode.QuickPickItem[] = [];
167+
168+
// Add paired groups
169+
const pairedGroups = Array.from(groupMap.entries())
170+
.filter(([idx]) => idx !== undefined)
171+
.sort(([a], [b]) => (a as number) - (b as number));
172+
173+
for (const [idx, vars] of pairedGroups) {
174+
options.push({
175+
label: `Group ${(idx as number) + 1}`,
176+
kind: vscode.QuickPickItemKind.Separator
177+
});
178+
for (const v of vars) {
179+
const openIndicator = PanelManager.hasPanel(
180+
cvVar.kind === 'mat' ? 'MatImageViewer' : (cvVar.kind === 'pointcloud' ? 'PointCloudViewer' : 'PlotViewer'),
181+
vscode.debug.activeDebugSession?.id || '',
182+
v.name
183+
) ? '● ' : '';
184+
const sizeInfo = v.sizeInfo || (v.isEmpty ? '[empty]' : '');
185+
options.push({
186+
label: ` ${v.name}`,
187+
description: `${openIndicator}[${sizeInfo}]`
188+
});
189+
}
190+
}
191+
192+
// Add unpaired variables
193+
const unpairedVars = groupMap.get(undefined) || [];
194+
if (unpairedVars.length > 0) {
195+
options.push({
196+
label: 'Unpaired',
197+
kind: vscode.QuickPickItemKind.Separator
198+
});
199+
for (const v of unpairedVars) {
200+
const openIndicator = PanelManager.hasPanel(
201+
cvVar.kind === 'mat' ? 'MatImageViewer' : (cvVar.kind === 'pointcloud' ? 'PointCloudViewer' : 'PlotViewer'),
202+
vscode.debug.activeDebugSession?.id || '',
203+
v.name
204+
) ? '● ' : '';
205+
const sizeInfo = v.sizeInfo || (v.isEmpty ? '[empty]' : '');
206+
options.push({
207+
label: v.name,
208+
description: `${openIndicator}[${sizeInfo}]`
209+
});
210+
}
211+
}
160212

161213
const selected = await vscode.window.showQuickPick(options, {
162214
placeHolder: `Select a variable to pair with ${cvVar.name}`
163215
});
164216

165-
if (selected) {
166-
cvVariablesProvider.setPairing(cvVar.name, selected.label);
167-
vscode.window.showInformationMessage(`Paired ${cvVar.name} with ${selected.label}`);
217+
if (selected && selected.kind !== vscode.QuickPickItemKind.Separator) {
218+
// Extract variable name (remove leading spaces if from a group)
219+
const varName = selected.label.trim();
220+
cvVariablesProvider.setPairing(cvVar.name, varName);
221+
vscode.window.showInformationMessage(`Paired ${cvVar.name} with ${varName}`);
168222
}
169223
})
170224
);
@@ -180,6 +234,43 @@ export function activate(context: vscode.ExtensionContext) {
180234
})
181235
);
182236

237+
// Add variable to group command
238+
context.subscriptions.push(
239+
vscode.commands.registerCommand("cv-debugmate.addToGroup", async (group: CVGroup) => {
240+
if (group.groupIndex === undefined || group.variables.length === 0) {
241+
vscode.window.showErrorMessage("Cannot add to this group.");
242+
return;
243+
}
244+
245+
const firstVar = group.variables[0];
246+
const variables = cvVariablesProvider.getVariables();
247+
const options = variables
248+
.filter(v =>
249+
v.name !== firstVar.name &&
250+
v.kind === firstVar.kind &&
251+
!v.isPaired // Only show unpaired variables
252+
)
253+
.map(v => ({
254+
label: v.name,
255+
description: v.sizeInfo ? `[${v.sizeInfo}]` : v.isEmpty ? '[empty]' : ''
256+
}));
257+
258+
if (options.length === 0) {
259+
vscode.window.showInformationMessage(`No other unpaired ${firstVar.kind === 'mat' ? 'image' : 'point cloud'} variables found to add to this group.`);
260+
return;
261+
}
262+
263+
const selected = await vscode.window.showQuickPick(options, {
264+
placeHolder: `Select a variable to add to group`
265+
});
266+
267+
if (selected) {
268+
cvVariablesProvider.setPairing(firstVar.name, selected.label);
269+
vscode.window.showInformationMessage(`Added ${selected.label} to group`);
270+
}
271+
})
272+
);
273+
183274
async function visualizeVariable(variable: any, force: boolean = false, reveal: boolean = true) {
184275
const debugSession = vscode.debug.activeDebugSession;
185276

0 commit comments

Comments
 (0)