Skip to content

Commit 515af88

Browse files
authored
Fix solution-local pack handling
1 parent 12c45a0 commit 515af88

11 files changed

Lines changed: 142 additions & 94 deletions

src/views/manage-components-packs/components-packs-webview-main.test.ts

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ const cprojectItem = new COutlineItem('project');
4343
cprojectItem.setAttribute('label', 'project');
4444
cprojectItem.setAttribute('expandable', '0');
4545
cprojectItem.setAttribute('type', 'projectFile');
46-
cprojectItem.setAttribute('resourcePath', 'to/project/path/myproject.cproject.yaml');
46+
cprojectItem.setAttribute('resourcePath', 'to/project/path/myproject.cproject.yml');
4747

4848
describe('ComponentsPacksWebviewMain', () => {
4949
let solutionManager: MockSolutionManager;
@@ -143,7 +143,7 @@ describe('ComponentsPacksWebviewMain', () => {
143143
});
144144

145145
describe('handleWebviewCommand', () => {
146-
const projectPath = 'to/project/path/myproject.cproject.yaml';
146+
const projectPath = 'to/project/path/myproject.cproject.yml';
147147

148148
beforeEach(() => {
149149
// Replace openWebview with a spy for each test
@@ -1256,6 +1256,63 @@ describe('ComponentsPacksWebviewMain', () => {
12561256
expect(webviewManager.sendMessage).toHaveBeenCalledWith(expect.objectContaining({ type: 'SET_PACKS_INFO', packs: expect.any(Array) }));
12571257
});
12581258

1259+
it('maps pack row fields and dedupes references in SET_PACKS_INFO', async () => {
1260+
(componentsPacksWebviewMain as any).csolutionService.getPacksInfo = jest.fn().mockResolvedValue({
1261+
packs: [{
1262+
id: 'Arm::CMSIS@1.2.3',
1263+
description: 'CMSIS pack',
1264+
used: true,
1265+
doc: 'https://example.com/cmsis',
1266+
references: [
1267+
{ pack: 'myPack', origin: path.join('root', 'my.cproject.yml'), path: path.join('root', 'packs/mypack') },
1268+
{ pack: 'myPack', origin: path.join('root', 'my.cproject.yml'), path: path.join('root', 'packs/mypack') },
1269+
]
1270+
}]
1271+
});
1272+
1273+
const selectSpy = jest.spyOn((componentsPacksWebviewMain as any).manageComponentsActions, 'selectPackage').mockResolvedValue(undefined);
1274+
1275+
await (componentsPacksWebviewMain as any).selectPackage({ type: 'SELECT_PACKAGE', target: 'target', packId: 'Arm::CMSIS@1.2.3' });
1276+
1277+
expect(selectSpy).toHaveBeenCalledWith('ctx', 'target', 'Arm::CMSIS@1.2.3');
1278+
const packsMsg = webviewManager.sendMessage.mock.calls.map(c => c[0]).find(m => m.type === 'SET_PACKS_INFO');
1279+
expect(packsMsg).toBeDefined();
1280+
expect(packsMsg.packs).toHaveLength(1);
1281+
expect(packsMsg.packs[0]).toEqual(expect.objectContaining({
1282+
name: 'Arm::CMSIS',
1283+
packId: 'Arm::CMSIS@1.2.3',
1284+
versionUsed: '@1.2.3',
1285+
description: 'CMSIS pack',
1286+
used: true,
1287+
overviewLink: 'https://example.com/cmsis'
1288+
}));
1289+
expect(packsMsg.packs[0].references).toHaveLength(1);
1290+
expect(packsMsg.packs[0].references[0]).toEqual(expect.objectContaining({
1291+
pack: 'myPack',
1292+
origin: path.join('root', 'my.cproject.yml'),
1293+
relOrigin: 'my.cproject.yml',
1294+
relPath: 'packs/mypack'
1295+
}));
1296+
});
1297+
1298+
it('maps each input pack into SET_PACKS_INFO without pack-level dedupe', async () => {
1299+
(componentsPacksWebviewMain as any).csolutionService.getPacksInfo = jest.fn().mockResolvedValue({
1300+
packs: [
1301+
{ id: 'Arm::CMSIS@1.2.3', references: [] },
1302+
{ id: 'Arm::CMSIS@2.0.0', references: [] },
1303+
]
1304+
});
1305+
1306+
jest.spyOn((componentsPacksWebviewMain as any).manageComponentsActions, 'selectPackage').mockResolvedValue(undefined);
1307+
1308+
await (componentsPacksWebviewMain as any).selectPackage({ type: 'SELECT_PACKAGE', target: 'target', packId: 'Arm::CMSIS@2.0.0' });
1309+
1310+
const packsMsg = webviewManager.sendMessage.mock.calls.map(c => c[0]).find(m => m.type === 'SET_PACKS_INFO');
1311+
expect(packsMsg).toBeDefined();
1312+
expect(packsMsg.packs).toHaveLength(2);
1313+
expect(packsMsg.packs.map((p: { packId: string }) => p.packId)).toEqual(['Arm::CMSIS@1.2.3', 'Arm::CMSIS@2.0.0']);
1314+
});
1315+
12591316
it('unselects a package and refreshes the pack list', async () => {
12601317
const unselectSpy = jest.spyOn((componentsPacksWebviewMain as any).manageComponentsActions, 'unselectPackage').mockResolvedValue(undefined);
12611318

src/views/manage-components-packs/components-packs-webview-main.ts

Lines changed: 39 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import * as vscode from 'vscode';
1818
import * as manifest from '../../manifest';
1919
import * as Messages from './messages';
2020
import path, { dirname } from 'path';
21-
import { ComponentInstance, CsolutionService, CtAggregate, CtRoot, PackReference, PacksInfo, Results, UsedItems } from '../../json-rpc/csolution-rpc-client';
21+
import { ComponentInstance, CsolutionService, CtAggregate, CtRoot, Pack, PackReference, PacksInfo, Results, UsedItems } from '../../json-rpc/csolution-rpc-client';
2222
import { IOpenFileExternal } from '../../open-file-external-if';
2323
import { ProjectFileUpdater, ProjectFileUpdaterImpl } from '../../solutions/edit/project-file-updater';
2424
import { SolutionLoadStateChangeEvent, SolutionManager } from '../../solutions/solution-manager';
@@ -29,9 +29,9 @@ import { COutlineItem } from '../solution-outline/tree-structure/solution-outlin
2929
import { WebviewManager, WebviewManagerOptions } from '../webview-manager';
3030
import { BuildContext, Project, TargetSetData } from './components-data';
3131
import { ComponentsPacksActions, CurrentProject, normalizeForCompare } from './components-packs-actions';
32-
import { ComponentRowDataType, ComponentScope } from './data/component-tools';
32+
import { ComponentRowDataType, ComponentScope, PackRowDataType, PackRowReferenceDataType } from './data/component-tools';
3333
import { componentTreeWalker } from './data/component-tree-walker';
34-
import { uniqWith, cloneDeep } from 'lodash';
34+
import { cloneDeep, uniqWith } from 'lodash';
3535
import { parsePackId } from './data/pack-parse';
3636
import { lineOf, readTextFile } from '../../utils/fs-utils';
3737
import { stripTwoExtensions } from '../../utils/string-utils';
@@ -52,6 +52,32 @@ const createProject = (cprojectPath: string): Project => {
5252
return { projectId: cprojectPath, projectName: getFileNameNoExt(cprojectPath) };
5353
};
5454

55+
const packsRowFromInfo = (packInfo: Pack, solutionPath: string): PackRowDataType => {
56+
const pack = parsePackId(packInfo.id);
57+
const solutionDir = dirname(solutionPath);
58+
const references = uniqWith((packInfo.references || []).map(ref => ({
59+
...ref,
60+
relOrigin: backToForwardSlashes(path.relative(solutionDir, ref.origin)),
61+
relPath: ref.path ? backToForwardSlashes(path.relative(solutionDir, ref.path)) : undefined,
62+
} satisfies PackRowReferenceDataType)), (left, right) => left.origin === right.origin && left.pack === right.pack);
63+
64+
return {
65+
key: packInfo.id,
66+
name: pack ? (pack.vendor ? `${pack.vendor}::${pack.packName}` : pack.packName) : packInfo.id,
67+
packId: packInfo.id,
68+
versionUsed: pack?.versionOperator ? pack.versionOperator + pack.version : (pack?.version || ''),
69+
versionTarget: '',
70+
description: packInfo.description || '',
71+
used: packInfo.used || false,
72+
references,
73+
overviewLink: packInfo.doc || ''
74+
};
75+
};
76+
77+
const packsRowsFromInfo = (packsInfo: PacksInfo, solutionPath: string): PackRowDataType[] => {
78+
return (packsInfo.packs || []).map(pack => packsRowFromInfo(pack, solutionPath));
79+
};
80+
5581
export class ComponentsPacksWebviewMain {
5682
private readonly webviewManager: WebviewManager<Messages.IncomingMessage, Messages.OutgoingMessage>;
5783

@@ -618,8 +644,11 @@ export class ComponentsPacksWebviewMain {
618644
const actx = this.getActiveContext();
619645
await action(actx, target, packId);
620646
const requestAll = this.scope === ComponentScope.All;
621-
const packs = this.mapPacksFromService(await this.csolutionService.getPacksInfo({ context: actx, all: requestAll }));
622-
await this.webviewManager.sendMessage({ type: 'SET_PACKS_INFO', packs: packs?.packs || [] });
647+
const packs = await this.csolutionService.getPacksInfo({ context: actx, all: requestAll });
648+
await this.webviewManager.sendMessage({
649+
type: 'SET_PACKS_INFO',
650+
packs: packsRowsFromInfo(packs, this.currentProject?.solutionPath ?? '')
651+
});
623652
await this.sendDirtyState();
624653
} finally {
625654
await this.webviewManager.sendMessage({ type: 'SET_SOLUTION_STATE', stateMessage: undefined });
@@ -678,7 +707,10 @@ export class ComponentsPacksWebviewMain {
678707

679708
this.componentTree = this.manageComponentsActions.mapComponentsFromService(await this.csolutionService.getComponentsTree({ context: activeContext, all: requestAll }));
680709
this.validations = await this.csolutionService.validateComponents({ context: activeContext });
681-
const packsInfo = this.mapPacksFromService(await this.csolutionService.getPacksInfo({ context: activeContext, all: requestAll }));
710+
const packs = packsRowsFromInfo(
711+
await this.csolutionService.getPacksInfo({ context: activeContext, all: requestAll }),
712+
this.currentProject?.solutionPath ?? ''
713+
);
682714

683715
componentTreeWalker(this.componentTree, (node, type) => {
684716
if (type === 'aggregate' && (node as CtAggregate).options?.layer) {
@@ -698,7 +730,7 @@ export class ComponentsPacksWebviewMain {
698730
componentScope: this.scope,
699731
availableTargetTypes: this.getTargetSetData(),
700732
selectedTargetType: this.getSelectedTargetSetData(),
701-
packs: packsInfo.packs,
733+
packs,
702734
cbuildPackPath: cbuildPackPath,
703735
solution: {
704736
dir: this.solutionManager.getCsolution()?.solutionDir,
@@ -717,33 +749,6 @@ export class ComponentsPacksWebviewMain {
717749
await this.sendDirtyState();
718750
};
719751

720-
private mapPacksFromService(packs: PacksInfo) {
721-
if (packs.packs) {
722-
// merge duplicate versions to one.
723-
packs.packs = uniqWith(packs.packs, (a, b) => {
724-
const pa = parsePackId(a.id);
725-
const pb = parsePackId(b.id);
726-
if (pa?.vendor === pb?.vendor && pa?.packName === pb?.packName) {
727-
const refs = uniqWith([... (a.references || []), ...(b.references || [])], (a, b) => a.origin === b.origin && a.pack === b.pack);
728-
a.references = b.references = refs;
729-
return true;
730-
}
731-
return false;
732-
});
733-
734-
// replace origin file names to relative
735-
packs.packs.forEach(pack => {
736-
pack.references?.forEach(ref => {
737-
ref.origin = backToForwardSlashes(ref.origin);
738-
ref.path = backToForwardSlashes(
739-
path.relative(dirname(this.currentProject?.solutionPath ?? ''), ref.origin)
740-
);
741-
});
742-
});
743-
}
744-
return packs;
745-
}
746-
747752
private async clearComponents() {
748753
if (this.componentTree) {
749754
this.componentTree.classes = [];

src/views/manage-components-packs/data/component-tools.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,15 @@ export interface PackRowDataType {
8080
versionUsed: string;
8181
versionTarget: string;
8282
used: boolean;
83-
references: PackReference[];
83+
references: PackRowReferenceDataType[];
8484
latestOnlineVersion?: string;
8585
}
8686

87+
export interface PackRowReferenceDataType extends PackReference {
88+
relOrigin: string;
89+
relPath?: string;
90+
}
91+
8792
export type OriginDataType = {
8893
type: string;
8994
label: string;

src/views/manage-components-packs/messages.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@
1414
* limitations under the License.
1515
*/
1616

17-
import { CtRoot, Pack, Result } from '../../json-rpc/csolution-rpc-client';
17+
import { CtRoot, Result } from '../../json-rpc/csolution-rpc-client';
1818
import { Project, TargetSetData, UiErrorMessage } from './components-data';
19-
import { ComponentRowDataType, ComponentScope } from './data/component-tools';
19+
import { ComponentRowDataType, ComponentScope, PackRowDataType } from './data/component-tools';
2020

2121
export type SolutionInfo = {
2222
dir?: string;
@@ -59,7 +59,7 @@ export type OutgoingMessage
5959
export type IncomingMessage
6060
= { type: 'SELECTED_PROJECT', project: Project }
6161
| { type: 'SET_COMPONENT_TREE', tree: CtRoot, validations?: Result[], scope?: ComponentScope }
62-
| { type: 'SET_PACKS_INFO', packs: Pack[] }
62+
| { type: 'SET_PACKS_INFO', packs: PackRowDataType[] }
6363
| { type: 'IS_DIRTY', isDirty: boolean }
6464
| { type: 'SET_SOLUTION_STATE', stateMessage: string | undefined }
6565
| { type: 'SET_ERROR_MESSAGES', messages: UiErrorMessage[] }
@@ -73,7 +73,7 @@ export type IncomingMessage
7373
availableTargetTypes: TargetSetData[],
7474
selectedTargetType?: TargetSetData,
7575
selectedLayer?: string,
76-
packs: Pack[],
76+
packs: PackRowDataType[],
7777
cbuildPackPath: string,
7878
solution: SolutionInfo,
7979
isDirty?: boolean,

src/views/manage-components-packs/view/components/pack-properties.test.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@ describe('PackPropertiesDialog', () => {
1818
key: 'ARM::CMSIS@6.1.0',
1919
overviewLink: 'https://arm.com/cmsis/overview',
2020
used: true,
21-
references: [{ pack: 'ARM::CMSIS@6.1.0', resolvedPack: 'ARM::CMSIS@6.1.0', path: 'path/to/project1.cproject.yml', origin: 'origin1', selected: true }, { pack: 'ARM::CMSIS@6.1.0', resolvedPack: 'ARM::CMSIS@6.1.0', path: 'path/to/project2.cproject.yml', origin: 'origin2', selected: true }],
21+
references: [
22+
{ pack: 'ARM::CMSIS@6.1.0', resolvedPack: 'ARM::CMSIS@6.1.0', origin: 'path/to/project1.cproject.yml', relOrigin: 'path/to/project1.cproject.yml', selected: true },
23+
{ pack: 'ARM::CMSIS@6.1.0', resolvedPack: 'ARM::CMSIS@6.1.0', origin: 'path/to/project2.cproject.yml', relOrigin: 'path/to/project2.cproject.yml', selected: true }
24+
],
2225
name: 'CMSIS',
2326
versionUsed: '6.1.0',
2427
versionTarget: 'ARM::CMSIS@6.1.0',
@@ -109,7 +112,7 @@ describe('PackPropertiesDialog', () => {
109112

110113
it('shows current pack as latest installed and disables update button when no upgrades are available', () => {
111114
const pack = createMockPack({
112-
references: [{ pack: 'ARM::CMSIS@6.1.0', resolvedPack: 'ARM::CMSIS@6.1.0', path: 'path/to/project1.cproject.yml', origin: 'origin1', selected: true }],
115+
references: [{ pack: 'ARM::CMSIS@6.1.0', resolvedPack: 'ARM::CMSIS@6.1.0', origin: 'path/to/project1.cproject.yml', relOrigin: 'path/to/project1.cproject.yml', selected: true }],
113116
});
114117

115118
render(<PackPropertiesDialog pack={pack} state={{ unlilnkRequestStack: [], selectedTargetType: selectedTargetType }} allOrigins={createMockAllOrigins()} onClose={mockOnClose} />);
@@ -122,7 +125,7 @@ describe('PackPropertiesDialog', () => {
122125

123126
it('shows upgrade pack version and marks unlock request when update is clicked', async () => {
124127
const pack = createMockPack({
125-
references: [{ pack: 'ARM::CMSIS@6.1.0', resolvedPack: 'ARM::CMSIS@6.1.0', path: 'path/to/project1.cproject.yml', origin: 'origin1', selected: true, upgrade: '6.2.0' }],
128+
references: [{ pack: 'ARM::CMSIS@6.1.0', resolvedPack: 'ARM::CMSIS@6.1.0', origin: 'path/to/project1.cproject.yml', relOrigin: 'path/to/project1.cproject.yml', selected: true, upgrade: '6.2.0' }],
126129
});
127130

128131
render(<PackPropertiesDialog pack={pack} state={{ unlilnkRequestStack: [], selectedTargetType: selectedTargetType }} allOrigins={createMockAllOrigins()} onClose={mockOnClose} />);

src/views/manage-components-packs/view/components/pack-properties.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,9 @@
55
import './components-properties.css';
66
import React, { useState, useEffect, useCallback } from 'react';
77
import { Button, Card, Col, Divider, Dropdown, Input, Modal, Row, Space, Tooltip } from 'antd';
8-
import { OriginDataType, PackRowDataType } from '../../data/component-tools';
8+
import { OriginDataType, PackRowDataType, PackRowReferenceDataType } from '../../data/component-tools';
99
import DraggableModalWrapper from './draggable-modal-wrapper';
1010
import { CompactDropdown } from '../../../common/components/compact-dropdown';
11-
import { PackReference } from '../../../../json-rpc/csolution-rpc-client';
1211
import { parsePackId } from '../../data/pack-parse';
1312
import { EditFilled, MinusSquareOutlined, PlusSquareOutlined } from '@ant-design/icons';
1413
import { CmsisCodicon } from '../../../common/components/cmsis-codicon';
@@ -55,12 +54,13 @@ export const PackPropertiesDialog: React.FC<PackPropertiesDialogProperties> = ({
5554
const handleOk = useCallback(() => {
5655
if (!pack) return;
5756

58-
const references: PackReference[] = allOrigins
57+
const references: PackRowReferenceDataType[] = allOrigins
5958
.map(o => ({
60-
origin: o.relativePath,
59+
origin: o.path,
6160
pack: `${pack.name}${o.versionOperator}${o.version}`,
6261
selected: o.selected,
6362
path: o.path,
63+
relOrigin: o.relativePath,
6464
resolvedPack: pack.packId,
6565
}));
6666

src/views/manage-components-packs/view/components/packs-view.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ describe('PacksView', () => {
2727
overviewLink: '/path/to/overview.html',
2828
versionUsed: '6.0.0',
2929
used: true,
30-
references: [{ path: 'path/to/solution.cproject.yml', pack: '', origin: '', selected: false }]
30+
references: [{ pack: '', origin: 'path/to/solution.cproject.yml', relOrigin: 'path/to/solution.cproject.yml', selected: false }]
3131
};
3232

3333
const defaultState: ComponentsState = {

src/views/manage-components-packs/view/components/packs-view.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import { IncomingMessage, OutgoingMessage } from '../../messages';
1515
import { PackPropertiesDialog } from './pack-properties';
1616
import { ComponentPackTargetSelect } from './component-pack-target-select';
1717
import { buildAllOrigins } from '../helpers/components-packs-helpers';
18-
import { PackReference } from '../../../../json-rpc/csolution-rpc-client';
1918

2019
const { Search } = Input;
2120

@@ -74,7 +73,7 @@ export const PacksView: React.FC<PacksProps> = ({ state, openFile, messageHandle
7473
openFile(ref.origin, false, `- pack: ${ref.pack}`);
7574
}
7675
return false;
77-
}}><EditFilled /></a> ./{ref.path} <span className='faded'>({p?.versionOperator}{p?.version ?? pack?.version})</span></div>);
76+
}}><EditFilled /></a> ./{ref.relOrigin} <span className='faded'>({p?.versionOperator}{p?.version ?? pack?.version})</span></div>);
7877
})) ?? []
7978
];
8079

@@ -160,8 +159,8 @@ export const PacksView: React.FC<PacksProps> = ({ state, openFile, messageHandle
160159
selectPack(undefined);
161160
};
162161

163-
const referenceFromContext = (relativePath: string, pack: PackRowDataType): PackReference[] => {
164-
return pack.references.filter(ref => ref.origin?.endsWith(relativePath));
162+
const referenceFromContext = (relativePath: string, pack: PackRowDataType): PackRowDataType['references'] => {
163+
return pack.references.filter(ref => ref.relOrigin.endsWith(relativePath));
165164
};
166165

167166
const allOrigins = React.useMemo(

0 commit comments

Comments
 (0)