1
0
mirror of https://github.com/zerobig/vscode-1c-metadata-viewer.git synced 2024-11-21 17:56:31 +02:00

Фильтрация по подсистемам #29

This commit is contained in:
Ilya Bushin 2024-06-11 00:11:58 +03:00
parent bd37cdaa5f
commit 744965517b
6 changed files with 278 additions and 35 deletions

View File

@ -2,7 +2,7 @@
"name": "vscode-1c-metadata-viewer",
"displayName": "1C Metadata Viewer",
"description": "Explore 1C:Enterprise 8 configuration in the usual way",
"version": "0.2.1",
"version": "0.2.2",
"publisher": "zerobig",
"license": "MIT",
"engines": {
@ -101,6 +101,14 @@
{
"command": "metadataViewer.openMetadataProperties",
"title": "%1c-metadata-viewer.openMetadataProperties.title%"
},
{
"command": "metadataViewer.filterBySubsystem",
"title": "%1c-metadata-viewer.filterBySubsystem.title%"
},
{
"command": "metadataViewer.clearFilter",
"title": "%1c-metadata-viewer.clearFilter.title%"
}
],
"menus": {
@ -173,7 +181,7 @@
{
"command": "metadataViewer.openXml",
"group": "xml",
"when": "viewItem && viewItem != command"
"when": "viewItem && viewItem != command && !(viewItem =~ /subsystem/)"
},
{
"command": "metadataViewer.openPredefinedData",
@ -188,6 +196,14 @@
"command": "metadataViewer.openMetadataProperties",
"group": "metadata",
"when": "viewItem == main"
},
{
"command": "metadataViewer.filterBySubsystem",
"when": "viewItem =~ /subsystem/ && viewItem not in filteredConfigArray"
},
{
"command": "metadataViewer.clearFilter",
"when": "viewItem =~ /subsystem/ && viewItem in filteredConfigArray"
}
]
},

View File

@ -14,5 +14,7 @@
"1c-metadata-viewer.openPredefinedData.title": "Open predefined data",
"1c-metadata-viewer.searchDepth.title": "The number of nesting levels of directories in which 1C:Enterprise configurations are searched",
"1c-metadata-viewer.openHandler.title": "Open handler",
"1c-metadata-viewer.openMetadataProperties.title": "Properties"
"1c-metadata-viewer.openMetadataProperties.title": "Properties",
"1c-metadata-viewer.filterBySubsystem.title": "Filter by subsystem",
"1c-metadata-viewer.clearFilter.title": "Clear subsystem filter"
}

View File

@ -14,5 +14,7 @@
"1c-metadata-viewer.openPredefinedData.title": "Открыть предопределенные данные",
"1c-metadata-viewer.searchDepth.title": "Количество уровней вложенности каталогов в которых осуществляется поиск конфигураций 1С:Предприятия",
"1c-metadata-viewer.openHandler.title": "Открыть обработчик",
"1c-metadata-viewer.openMetadataProperties.title": "Свойства"
"1c-metadata-viewer.openMetadataProperties.title": "Свойства",
"1c-metadata-viewer.filterBySubsystem.title": "Фильтровать по подсистеме",
"1c-metadata-viewer.clearFilter.title": "Очистить фильтр по подсистеме"
}

View File

@ -10,15 +10,18 @@ import {
import { posix, resolve } from 'path';
import { Metadata } from './edtMetadataInterfaces';
import { ProgressLocation, window } from 'vscode';
import { NodeWithIdTreeDataProvider } from '../metadataView';
export class Edt {
private xmlPath: vscode.Uri;
private dataProvider: NodeWithIdTreeDataProvider;
constructor(xmlPath: vscode.Uri) {
constructor(xmlPath: vscode.Uri, dataProvider: NodeWithIdTreeDataProvider) {
this.xmlPath = xmlPath;
this.dataProvider = dataProvider;
}
createTreeElements(root: TreeItem) {
createTreeElements(root: TreeItem, subsystemFilter: string[]) {
window.withProgress({
location: ProgressLocation.Notification,
title: "Происходит загрузка конфигурации",
@ -54,6 +57,10 @@ export class Edt {
const subTree: TreeItem[] = [...treeItem?.children ?? []];
for (const [indexOfObjects, obj] of objects.entries()) {
if (subsystemFilter.length && subsystemFilter.indexOf(obj) === -1) {
continue;
}
count++;
if (count % Math.round(total / 100) === 0) {
@ -74,10 +81,59 @@ export class Edt {
treeItem!.children = subTree;
}
this.dataProvider.update();
}
}
console.timeEnd('edtDownload');
}); // WithProgress
if (subsystemFilter.length) {
// Нумераторы и последовательности в документах
if (root.children![3].children![1].children?.length === 0) {
root.children![3].children?.splice(1, 1);
}
if (root.children![3].children![0].children?.length === 0) {
root.children![3].children?.splice(0, 1);
}
// Очищаю пустые элементы
const indexesToDelete: number[] = [];
root.children?.forEach((ch, index) => {
if (!ch.children || ch.children.length === 0) {
indexesToDelete.push(index);
}
});
indexesToDelete.sort((a, b) => b - a);
indexesToDelete.forEach((d) => root.children?.splice(d, 1));
// Отдельно очищаю раздел "Общие"
indexesToDelete.splice(0);
root.children![0].children?.forEach((ch, index) => {
if (!ch.children || ch.children.length === 0) {
indexesToDelete.push(index);
}
});
indexesToDelete.sort((a, b) => b - a);
indexesToDelete.forEach((d) => root.children![0].children?.splice(d, 1));
// Ненужные вложенные подсистемы
this.removeSubSystems(root.children![0].children![0], subsystemFilter);
this.dataProvider.update();
}
}, ); // WithProgress
}
removeSubSystems(subsystemsTreeItem: TreeItem, subsystemFilter: string[]) {
const indexesToDelete: number[] = [];
subsystemsTreeItem.children?.forEach((ch, index) => {
if (subsystemFilter.indexOf(`Subsystem.${ch.label}`) === -1) {
indexesToDelete.push(index);
} else {
this.removeSubSystems(ch, subsystemFilter);
}
});
indexesToDelete.sort((a, b) => b - a);
indexesToDelete.forEach((d) => subsystemsTreeItem.children?.splice(d, 1));
}
async createElement(rootPath: string, objName: string) {
@ -116,16 +172,23 @@ export class Edt {
const treeItemPath = `${treeItemIdSlash}${CreatePath(objectPath)}`;
switch (objName.split('.')[0]) {
case 'Subsystem':
case 'Subsystem': {
const { chilldren, content } = this.getSubsystemChildren(
elementObject,
folderUri,
posix.join(rootPath, objectPath)
);
return GetTreeItem(treeItemId, elementName ?? objName, {
icon: 'subsystem',
children: this.getSubsystemChildren(
elementObject,
folderUri,
posix.join(rootPath, objectPath)
),
context: `subsystem_${rootPath}`,
children: chilldren,
command: 'metadataViewer.filterBySubsystem',
commandTitle: 'Filter by subsystem',
commandArguments: content,
configType: 'edt'
});
}
case 'CommonModule':
return GetTreeItem(treeItemId, elementName ?? objName, {
icon: 'commonModule', context: 'module', path: treeItemPath,
@ -310,8 +373,19 @@ export class Edt {
return null;
}
getSubsystemChildren(obj: any, folderUri: vscode.Uri, path: string): TreeItem[] | undefined {
getSubsystemChildren(obj: any, folderUri: vscode.Uri, path: string): {chilldren: TreeItem[] | undefined, content: string[] } {
const subtreeItems: TreeItem[] = [];
// добавляю к фильтру сами подсистемы с иерархией
const subsystemContent: string[] = [
...path.slice(path.indexOf('Subsystem')).replace(/Subsystems\//g, 'Subsystem.').split('/')
];
const rootPath = path.slice(0, path.indexOf('Subsystem') - 1);
if (obj.content && obj.content.length > 0) {
for (const content of obj.content) {
subsystemContent.push(content);
}
}
if (obj.subsystems && obj.subsystems.length > 0) {
for (const subsystem of obj.subsystems) {
@ -333,15 +407,22 @@ export class Edt {
const elementObject = element[Object.keys(element)[1]];
const elementName = elementObject.name;
subtreeItems.push(GetTreeItem(`${subPath}/${elementObject.$_uuid}`, elementName ?? subsystem, {
icon: 'subsystem', children: this.getSubsystemChildren(elementObject, folderUri, subPath),
const { chilldren, content } = this.getSubsystemChildren(elementObject, folderUri, subPath);
subtreeItems.push(GetTreeItem(`${rootPath}/${elementObject.$_uuid}`, elementName ?? subsystem, {
icon: 'subsystem',
context: `subsystem_${rootPath}`,
children: chilldren,
command: 'metadataViewer.filterBySubsystem',
commandTitle: 'Filter by subsystem',
commandArguments: content,
configType: 'edt'
}));
});
}
}
return subtreeItems;
return { chilldren: subtreeItems, content: subsystemContent };
}
fillObjectItemsByMetadata(idPrefix: string, metadataType: string, metadata: Metadata) {

View File

@ -86,7 +86,7 @@ export class TreeItem extends vscode.TreeItem {
// TODO: Ужасная функция!!!1 Первая очередь на рефакторинг!
export function CreatePath(name: string): string {
return name
.replace("Subsystem.", "Subsystems/")
.replace(/Subsystem\./g, "Subsystems/")
.replace("CommonModule.", "CommonModules/")
.replace("SessionParameter.", "SessionParameters/")
.replace("Role.", "Roles/")
@ -154,7 +154,7 @@ export function GetTreeItem(
treeItem.command = {
command: params.command,
title: params.commandTitle,
arguments: [...(params.commandArguments ?? []), treeItem.configType],
arguments: params.commandArguments ?? [],
};
}

View File

@ -57,13 +57,16 @@ interface MetadataObjects {
export class MetadataView {
rootPath?: vscode.Uri;
panel: vscode.WebviewPanel | undefined = undefined;
// Фильтр нужен по каждой конфигурации отдельно
subsystemFilter: { id: string; objects: string[] }[] = [];
dataProvider: NodeWithIdTreeDataProvider | null = null;
constructor(context: vscode.ExtensionContext) {
this.rootPath = (vscode.workspace.workspaceFolders && (vscode.workspace.workspaceFolders.length > 0))
? vscode.workspace.workspaceFolders[0].uri : undefined;
const dataProvider = new NodeWithIdTreeDataProvider();
const view = vscode.window.createTreeView('metadataView', { treeDataProvider: dataProvider, showCollapseAll: true });
this.dataProvider = new NodeWithIdTreeDataProvider();
const view = vscode.window.createTreeView('metadataView', { treeDataProvider: this.dataProvider, showCollapseAll: true });
context.subscriptions.push(view);
view.onDidExpandElement(e => {
@ -71,13 +74,17 @@ export class MetadataView {
});
vscode.workspace.workspaceFolders?.forEach(folder => {
LoadAndParseConfigurationXml(folder.uri, dataProvider);
if (this.dataProvider) {
LoadAndParseConfigurationXml(folder.uri, this.dataProvider);
}
});
vscode.commands.registerCommand('metadataViewer.showTemplate', (template, configType) => this.openTemplate(context, template, configType));
vscode.commands.registerCommand('metadataViewer.openPredefinedData', (item) => this.openPredefinedData(context, item));
vscode.commands.registerCommand('metadataViewer.openHandler', (item) => this.openHandler(item));
vscode.commands.registerCommand('metadataViewer.openMetadataProperties', (item) => this.openMetadataProperties(context, item));
vscode.commands.registerCommand('metadataViewer.filterBySubsystem', (item) => this.filterBySubsystem(item, true));
vscode.commands.registerCommand('metadataViewer.clearFilter', (item) => this.filterBySubsystem(item, false));
}
// Открытие макета
@ -283,6 +290,39 @@ export class MetadataView {
}
}
private filterBySubsystem(item: TreeItem, setFilter: boolean): void {
if (tree.length && tree[0].children?.length) {
const pathArray = item.id.split('/');
pathArray.pop();
const config = tree[0].children.find((c) => pathArray.join('/') === c.id);
if (config) {
// Устанавливаю пустую конфигурацию чтобы не было конфликта идентификаторов
const configIndex = tree[0].children.indexOf(config);
tree[0].children[configIndex].children = CreateMetadata(config.id);
this.dataProvider?.update();
// Устанавливаю признак фильтрации
if (this.subsystemFilter.find((sf) => sf.id === config.id)) {
this.subsystemFilter = this.subsystemFilter.map((sf) => {
if (sf.id === config.id) {
return { id: config.id, objects: setFilter ? item.command?.arguments ?? [] : [] };
}
return sf;
});
} else {
this.subsystemFilter.push({ id: config.id, objects: item.command?.arguments ?? [] });
}
// Заполняю дерево конфигурации с фильтром
this.expand(tree[0].children[configIndex]);
vscode.commands.executeCommand('setContext', 'filteredConfigArray',
this.subsystemFilter.filter((sf) => sf.objects.length !== 0).map((sf) => `subsystem_${sf.id}`));
}
}
}
private expand(element: TreeItem) {
if (!element.isConfiguration) {
return;
@ -308,12 +348,51 @@ export class MetadataView {
const result = parser.parse(Buffer.from(configXml));
const typedResult = result as MetadataFile;
CreateTreeElements(element, typedResult);
const currentFilter = this.subsystemFilter.find((sf) => sf.id === element.id)?.objects ?? [];
CreateTreeElements(this.rootPath!,
element,
typedResult,
currentFilter);
if (currentFilter.length) {
// Нумераторы и последовательности в документах
if (element.children![3].children![1].children?.length === 0) {
element.children![3].children?.splice(1, 1);
}
if (element.children![3].children![0].children?.length === 0) {
element.children![3].children?.splice(0, 1);
}
// Очищаю пустые элементы
const indexesToDelete: number[] = [];
element.children?.forEach((ch, index) => {
if (!ch.children || ch.children.length === 0) {
indexesToDelete.push(index);
}
});
indexesToDelete.sort((a, b) => b - a);
indexesToDelete.forEach((d) => element.children?.splice(d, 1));
// Отдельно очищаю раздел "Общие"
indexesToDelete.splice(0);
element.children![0].children?.forEach((ch, index) => {
if (!ch.children || ch.children.length === 0) {
indexesToDelete.push(index);
}
});
indexesToDelete.sort((a, b) => b - a);
indexesToDelete.forEach((d) => element.children![0].children?.splice(d, 1));
// Ненужные вложенные подсистемы
removeSubSystems(element.children![0].children![0], currentFilter);
}
this.dataProvider?.update();
});
} else {
const edt = new Edt(this.rootPath.with({ path: posix.join(
element.id, 'Configuration', 'Configuration.mdo') }));
edt.createTreeElements(element);
element.id, 'Configuration', 'Configuration.mdo') }), this.dataProvider!);
edt.createTreeElements(element, this.subsystemFilter.find((sf) => sf.id === element.id)?.objects ?? []);
}
}
}
@ -400,7 +479,7 @@ function LoadAndParseConfigurationXml(uri: vscode.Uri, dataProvider: NodeWithIdT
});
}
function CreateTreeElements(element: TreeItem, metadataFile: MetadataFile) {
function CreateTreeElements(rootPath: vscode.Uri, element: TreeItem, metadataFile: MetadataFile, subsystemFilter: string[]) {
const versionMetadata = metadataFile.ConfigDumpInfo.ConfigVersions.Metadata;
const treeItemIdSlash = element.id + '/';
@ -443,17 +522,28 @@ function CreateTreeElements(element: TreeItem, metadataFile: MetadataFile) {
if (current.$_name.split('.').length !== 2) {
return previous;
}
if (subsystemFilter.length && subsystemFilter.indexOf(current.$_name) === -1) {
return previous;
}
const treeItemId = treeItemIdSlash + current.$_id;
const treeItemPath = `${treeItemIdSlash}${CreatePath(current.$_name)}`;
switch (true) {
case current.$_name.startsWith('Subsystem.'):
case current.$_name.startsWith('Subsystem.'): {
const chilldren = GetSubsystemChildren(rootPath, element.id, versionMetadata, current.$_name);
previous.subsystem.push(GetTreeItem(
treeItemId, current.$_name,
{ icon: 'subsystem', children: GetSubsystemChildren(versionMetadata, current.$_name) }));
treeItemId, current.$_name, {
icon: 'subsystem',
context: `subsystem_${element.id}`,
children: chilldren,
command: 'metadataViewer.filterBySubsystem',
commandTitle: 'Filter by subsystem',
commandArguments: CollectSubsystemContent(rootPath, treeItemPath) }));
break;
}
case current.$_name.startsWith('CommonModule.'):
previous.commonModule.push(GetTreeItem(
treeItemId, current.$_name,
@ -897,24 +987,76 @@ function SearchTree(element: TreeItem, matchingId: string): TreeItem | null {
return null;
}
function GetSubsystemChildren(versionMetadata: VersionMetadata[],
function GetSubsystemChildren(
rootPath: vscode.Uri,
rootId: string,
versionMetadata: VersionMetadata[],
name: string,
level = 2
): TreeItem[] | undefined {
const filtered = versionMetadata
.filter(f => f.$_name.startsWith(name) && f.$_name.split('.').length === 2 * level);
.filter(f => f.$_name.startsWith(`${name}.`) && f.$_name.split('.').length === 2 * level);
if (filtered.length !== 0) {
return filtered
.map(m => GetTreeItem(
'', m.$_name, {
icon: 'subsystem',
children: GetSubsystemChildren(versionMetadata, m.$_name, level + 1) }));
.map(m => {
const chilldren = GetSubsystemChildren(rootPath, rootId, versionMetadata, m.$_name, level + 1);
return GetTreeItem(
rootId + '/' + m.$_id, m.$_name, {
icon: 'subsystem',
context: `subsystem_${rootId}`,
children: chilldren,
command: 'metadataViewer.filterBySubsystem',
commandTitle: 'Filter by subsystem',
commandArguments: CollectSubsystemContent(rootPath, `${rootId}/${CreatePath(m.$_name)}`.replace(/\./g, '/'))
}
);
});
}
return undefined;
}
function CollectSubsystemContent(rootPath: vscode.Uri, treeItemPath: string): string[] {
// добавляю к фильтру сами подсистемы с иерархией
const subsystemContent: string[] = [
...treeItemPath.slice(treeItemPath.indexOf('Subsystem')).replace(/Subsystems\//g, 'Subsystem.').split('/')
];
const path = treeItemPath + '.xml';
vscode.workspace.fs.readFile(rootPath.with({ path }))
.then(configXml => {
const parser = new XMLParser({
ignoreAttributes : false,
attributeNamePrefix: '$_',
});
const result = parser.parse(Buffer.from(configXml));
const content = result.MetaDataObject.Subsystem.Properties.Content["xr:Item"];
if (content && content.length > 0) {
for (const contentElem of content) {
subsystemContent.push(contentElem["#text"]);
}
}
});
return subsystemContent;
}
function removeSubSystems(subsystemsTreeItem: TreeItem, subsystemFilter: string[]) {
const indexesToDelete: number[] = [];
subsystemsTreeItem.children?.forEach((ch, index) => {
if (subsystemFilter.indexOf(`Subsystem.${ch.label}`) === -1) {
indexesToDelete.push(index);
} else {
removeSubSystems(ch, subsystemFilter);
}
});
indexesToDelete.sort((a, b) => b - a);
indexesToDelete.forEach((d) => subsystemsTreeItem.children?.splice(d, 1));
}
export class NodeWithIdTreeDataProvider implements vscode.TreeDataProvider<TreeItem> {
private _onDidChangeTreeData: vscode.EventEmitter<any> = new vscode.EventEmitter<any>();
readonly onDidChangeTreeData: vscode.Event<any> = this._onDidChangeTreeData.event;