diff --git a/package.json b/package.json index c286169e..7d389cb7 100644 --- a/package.json +++ b/package.json @@ -649,7 +649,7 @@ { "category": "CMSIS", "command": "cmsis-csolution.mergeFile", - "title": "Merge Updated Config File", + "title": "Open Merge View", "icon": "$(git-merge)" }, { diff --git a/src/views/solution-outline/tree-structure/solution-outline-file-item.test.ts b/src/views/solution-outline/tree-structure/solution-outline-file-item.test.ts index 6f5793da..5093a8bd 100644 --- a/src/views/solution-outline/tree-structure/solution-outline-file-item.test.ts +++ b/src/views/solution-outline/tree-structure/solution-outline-file-item.test.ts @@ -95,15 +95,12 @@ describe('FileItem', () => { // check attributes and merge features for each file node const fileLabels = ['RTX_Config.c', 'RTX_Config.h']; - const fileStatuses = ['- (X) \'RTX_Config.c\' is incompatible. A file update is mandatory.', '- (?) \'RTX_Config.h\' has corrections. A file update is suggested.']; - const fileDescriptions = ['(X)', '(?)']; createdChildren.forEach((child, idx) => { expect(child.getTag()).toBe('file'); expect(child.getAttribute('label')).toContain(fileLabels[idx]); expect(child.getAttribute('update')).toContain('update'); expect(child.getAttribute('base')).toContain('base'); - expect(child.getAttribute('description')).toContain(fileDescriptions[idx]); - expect(child.getAttribute('tooltip')).toContain(fileStatuses[idx]); + expect(child.getAttribute('description')).toBeUndefined(); }); }); diff --git a/src/views/solution-outline/tree-structure/solution-outline-file-item.ts b/src/views/solution-outline/tree-structure/solution-outline-file-item.ts index e027d845..9c41eb5b 100644 --- a/src/views/solution-outline/tree-structure/solution-outline-file-item.ts +++ b/src/views/solution-outline/tree-structure/solution-outline-file-item.ts @@ -18,7 +18,7 @@ import path from 'path'; import { CTreeItem, ITreeItem } from '../../../generic/tree-item'; import { FILE_TAGS } from '../../../solutions/constants'; import { COutlineItem } from './solution-outline-item'; -import { getStatusTooltip, setContextMenuAttributes, setHeaderContext, setMergeDescription, setMergeFileContext } from './solution-outline-utils'; +import { setContextMenuAttributes, setHeaderContext, setMergeFileContext } from './solution-outline-utils'; import { matchesContext } from '../../../utils/context-utils'; import { SolutionOutlineItemBuilder } from './solution-outline-item-builder'; import { CSolution } from '../../../solutions/csolution'; @@ -131,24 +131,6 @@ export class FileItemBuilder extends SolutionOutlineItemBuilder { // assign merge context setMergeFileContext(cfileItem); - - // assign description - setMergeDescription(cfileItem, fileStatus); - - // set tooltip - const existingTooltip = cfileItem.getValue('tooltip'); - const label = cfileItem.getValue('label'); - - if (label) { - const statusTooltip = getStatusTooltip(label, fileStatus); - - if (existingTooltip) { - cfileItem.setAttribute('tooltip', existingTooltip + '\n' + statusTooltip); - } else { - cfileItem.setAttribute('tooltip', statusTooltip); - } - } - cfileItem.setAttribute('local', localPath); cfileItem.setAttribute('update', updatePath); cfileItem.setAttribute('base', basePath); diff --git a/src/views/solution-outline/tree-structure/solution-outline-hardware-item.test.ts b/src/views/solution-outline/tree-structure/solution-outline-hardware-item.test.ts index 9540e2b9..c5c3ff62 100644 --- a/src/views/solution-outline/tree-structure/solution-outline-hardware-item.test.ts +++ b/src/views/solution-outline/tree-structure/solution-outline-hardware-item.test.ts @@ -107,8 +107,6 @@ describe('HardwareItemBuilder', () => { const gotBase = base ? path.basename(base) : ''; const wantBase = 'Hello+CS300.dbgconf.base@0.0.1'; expect(gotBase).toEqual(wantBase); - - expect(device?.getAttribute('tooltip')).toContain('- (?) \'Hello+CS300.dbgconf\' has corrections. A file update is suggested.'); }); }); diff --git a/src/views/solution-outline/tree-structure/solution-outline-project-items.ts b/src/views/solution-outline/tree-structure/solution-outline-project-items.ts index eb863dec..949d61a5 100644 --- a/src/views/solution-outline/tree-structure/solution-outline-project-items.ts +++ b/src/views/solution-outline/tree-structure/solution-outline-project-items.ts @@ -21,12 +21,12 @@ import { FileItemBuilder } from './solution-outline-file-item'; import { COutlineItem } from './solution-outline-item'; import * as manifest from '../../../manifest'; import { CSolution } from '../../../solutions/csolution'; -import { getMapFilePath, getStatusTooltip, setDocContext, setHeaderContext, setLinkerContext, setMergeDescription, setMergeUpdate } from './solution-outline-utils'; +import { getMapFilePath, setDocContext, setHeaderContext, setLinkerContext } from './solution-outline-utils'; import { CProjectYamlFile } from '../../../solutions/files/cproject-yaml-file'; import { SolutionOutlineItemBuilder } from './solution-outline-item-builder'; export class ProjectItemsBuilder extends SolutionOutlineItemBuilder { - private _lastPrioritizedComponentList: COutlineItem[] = []; + private readonly _lastPrioritizedComponentList: COutlineItem[] = []; public get lastPrioritizedComponentList(): COutlineItem[] { return this._lastPrioritizedComponentList; @@ -222,62 +222,9 @@ export class ProjectItemsBuilder extends SolutionOutlineItemBuilder { } this.addComponentOptions(componentNodes); - - // add merge description - const components = Array.from(componentNodes.values()); - const fileStatus = this.getMergeDescriptionAtParentComponentLevel(components); - if (fileStatus) { - // assign description - setMergeDescription(componentsItem, fileStatus); - - // assign tooltip with component ids - const prioritizedList = this._lastPrioritizedComponentList; - let newTooltip = 'Components with updated configuration files:'; - for (const comp of prioritizedList) { - const compId = comp.getAttribute('label'); - const compStatus = comp.getAttribute('status'); - if (compId && compStatus) { - const update = comp.getAttribute('update'); - newTooltip += `\n- ${update} ${compId}: ${compStatus}`; - } - } - componentsItem.setAttribute('tooltip', newTooltip); - } return true; // do have components to edit } - private getMergeDescriptionAtParentComponentLevel(components: COutlineItem[]): string | undefined { - let result: string | undefined = undefined; - const updateRequired: COutlineItem[] = []; - const updateRecommended: COutlineItem[] = []; - const updateSuggested: COutlineItem[] = []; - - for (const component of components) { - const status = component.getAttribute('status'); - if (!status) { - continue; - } - if (status == 'update required') { - updateRequired.push(component); - } else if (status == 'update recommended') { - updateRecommended.push(component); - } else if (status == 'update suggested') { - updateSuggested.push(component); - } - } - - const prioritizedList = [...updateRequired, ...updateRecommended, ...updateSuggested]; - this._lastPrioritizedComponentList = prioritizedList; - const prioritizedFile = prioritizedList[0]; - - const fileStatus = prioritizedFile?.getAttribute('status'); - if (fileStatus) { - result = fileStatus; - } - - return result; - } - private addComponentOptions(componentNodes: Map) { const components = componentNodes.values(); @@ -437,31 +384,6 @@ export class ProjectItemsBuilder extends SolutionOutlineItemBuilder { // set status at component level node.setAttribute('status', fileStatus); - - // assign description - setMergeDescription(node, fileStatus); - - // assign status symbol for merge update action - setMergeUpdate(node, fileStatus); - - // set tooltip - const prevTooltip = node.getValue('tooltip'); - let newTooltip: string = ''; - for (const file of prioritizedList) { - const fileLabel = file.getValue('file'); - const fileStatus = file.getValue('status'); - if (fileLabel && fileStatus) { - const tooltip = getStatusTooltip(fileLabel, fileStatus); - newTooltip += `\n ${tooltip}`; - } - } - - if (prevTooltip) { - node.setAttribute('tooltip', prevTooltip + '\n' + newTooltip); - } else { - node.setAttribute('tooltip', newTooltip); - } - } private getPrioritizedMergeFile(files: ITreeItem[]): ITreeItem[] { diff --git a/src/views/solution-outline/tree-structure/solution-outline-tree.ts b/src/views/solution-outline/tree-structure/solution-outline-tree.ts index ec6ffff6..0fa76e4f 100644 --- a/src/views/solution-outline/tree-structure/solution-outline-tree.ts +++ b/src/views/solution-outline/tree-structure/solution-outline-tree.ts @@ -23,7 +23,6 @@ import * as manifest from '../../../manifest'; import { HardwareItemBuilder } from './solution-outline-hardware-item'; import { ProjectItemsBuilder } from './solution-outline-project-items'; import { getFileNameNoExt } from '../../../utils/path-utils'; -import { setMergeDescription } from './solution-outline-utils'; import { CProjectYamlFile } from '../../../solutions/files/cproject-yaml-file'; import { SolutionOutlineItemBuilder } from './solution-outline-item-builder'; @@ -122,26 +121,6 @@ export class SolutionOutlineTree extends SolutionOutlineItemBuilder { if (cproject) { const projectItems = new ProjectItemsBuilder(this.csolution, this.rpcData, context); projectItems.addProjectChildren(this.csolution, cprojectItem, cprojectFile, cbuild); - - // get prioritized component list and set merge description if available - const prioritizedList = projectItems.lastPrioritizedComponentList; - if (prioritizedList && prioritizedList.length > 0) { - const fileStatus = prioritizedList[0].getAttribute('status'); - if (fileStatus) { - setMergeDescription(cprojectItem, fileStatus); - - // set tooltip - const existingTooltip = cprojectItem.getAttribute('tooltip'); - const description = cprojectItem.getAttribute('description'); - const newTooltip = `- ${description} Component config files: ${fileStatus}`; - - if (existingTooltip) { - cprojectItem.setAttribute('tooltip', existingTooltip + '\n' + newTooltip); - } else { - cprojectItem.setAttribute('tooltip', newTooltip); - } - } - } } else { cprojectItem.setAttribute('description', 'error loading project'); } diff --git a/src/views/solution-outline/tree-structure/solution-outline-utils.ts b/src/views/solution-outline/tree-structure/solution-outline-utils.ts index de66ae88..2f659388 100644 --- a/src/views/solution-outline/tree-structure/solution-outline-utils.ts +++ b/src/views/solution-outline/tree-structure/solution-outline-utils.ts @@ -52,19 +52,6 @@ export function setMergeFiles(component: COutlineItem, file: ITreeItem { expect(refreshSpy).toHaveBeenCalled(); }); + + it('should return merge decoration for files with merge feature', () => { + const filePath = path.join('src', 'merge', 'file.c'); + const uri = URI.file(filePath); + + const root = new COutlineItem('root'); + const project = root.createChild('project'); + const group = project.createChild('group'); + const file = group.createChild('file'); + file.setTag('file'); + file.setAttribute('resourcePath', uri.fsPath); + file.addFeature('mergeFile:'); + + treeViewDecorationProvider.setTreeRoot(root); + + const result = treeViewDecorationProvider.provideFileDecoration(uri); + + expect(result).toEqual({ + badge: TreeViewFileDecorationProvider.mergeBadge, + tooltip: TreeViewFileDecorationProvider.mergeTooltip, + color: { id: TreeViewFileDecorationProvider.mergeColor }, + }); + }); + + it('should prioritize excluded decoration over merge decoration', () => { + const filePath = path.join('src', 'merge', 'excluded.c'); + const uri = URI.file(filePath); + + const root = new COutlineItem('root'); + const project = root.createChild('project'); + const group = project.createChild('group'); + const file = group.createChild('file'); + file.setTag('file'); + file.setAttribute('resourcePath', uri.fsPath); + file.addFeature('mergeFile:'); + file.setAttribute('excluded', '1'); + + treeViewDecorationProvider.setTreeRoot(root); + + const result = treeViewDecorationProvider.provideFileDecoration(uri); + + expect(result).toEqual({ + badge: TreeViewFileDecorationProvider.excludedBadge, + tooltip: TreeViewFileDecorationProvider.excludedTooltip, + color: { id: TreeViewFileDecorationProvider.excludedColor }, + }); + }); + + it('should prioritize merge decoration over pack-sourced decoration', () => { + const packCachePath = 'MOCKED_CMSIS_PACK_ROOT'; + jest.spyOn(path_utils, 'getCmsisPackRoot').mockReturnValue(packCachePath); + + const filePath = path.join(packCachePath, 'root', 'merge.c'); + const uri = URI.file(filePath); + + const root = new COutlineItem('root'); + const project = root.createChild('project'); + const group = project.createChild('group'); + const file = group.createChild('file'); + file.setTag('file'); + file.setAttribute('resourcePath', uri.fsPath); + file.addFeature('mergeFile:'); + + treeViewDecorationProvider.setTreeRoot(root); + + const result = treeViewDecorationProvider.provideFileDecoration(uri); + + expect(result).toEqual({ + badge: TreeViewFileDecorationProvider.mergeBadge, + tooltip: TreeViewFileDecorationProvider.mergeTooltip, + color: { id: TreeViewFileDecorationProvider.mergeColor }, + }); + }); }); diff --git a/src/views/solution-outline/treeview-decoration-provider.ts b/src/views/solution-outline/treeview-decoration-provider.ts index 18b73409..7ff579a7 100644 --- a/src/views/solution-outline/treeview-decoration-provider.ts +++ b/src/views/solution-outline/treeview-decoration-provider.ts @@ -20,12 +20,17 @@ import { ThemeProvider } from '../../vscode-api/theme-provider'; import { URI } from 'vscode-uri'; import { getCmsisPackRoot } from '../../utils/path-utils'; import { COutlineItem } from './tree-structure/solution-outline-item'; +import * as manifest from '../../manifest'; export class TreeViewFileDecorationProvider implements vscode.FileDecorationProvider { static readonly badge: string = 'P'; static readonly tooltip: string = 'Pack sourced'; static readonly themeColor: string = 'descriptionForeground'; + static readonly mergeBadge: string = 'N'; + static readonly mergeTooltip: string = 'New Version Available for Merge'; + static readonly mergeColor: string = 'charts.blue'; + static readonly excludedBadge: string = 'X'; static readonly excludedTooltip: string = 'Excluded from build'; static readonly excludedColor: string = 'errorForeground'; @@ -60,6 +65,16 @@ export class TreeViewFileDecorationProvider implements vscode.FileDecorationProv }; } + // Merge-enabled files have higher priority than pack-sourced files + const features = fileItem?.getFeatures().split(';') ?? []; + if (features.includes(manifest.MERGE_FILE_CONTEXT)) { + return { + badge: TreeViewFileDecorationProvider.mergeBadge, + tooltip: TreeViewFileDecorationProvider.mergeTooltip, + color: this.themeProvider.getThemeColor(TreeViewFileDecorationProvider.mergeColor), + }; + } + // Check for pack-sourced files const cmsisPackRoot = getCmsisPackRoot(); const cmsisPackRootUri = URI.file(cmsisPackRoot);