Auto-build: copilot (python) from 7fa279c

This commit is contained in:
github-actions[bot]
2026-05-17 11:22:33 +00:00
commit bbd2f7a8c1
207 changed files with 98901 additions and 0 deletions
@@ -0,0 +1,514 @@
# subsystem-info v1.0 — Compact summary of 1C subsystem structure
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
[Parameter(Mandatory=$true)][Alias('Path')][string]$SubsystemPath,
[ValidateSet("overview","content","ci","tree","full")]
[string]$Mode = "overview",
[string]$Name,
[int]$Limit = 150,
[int]$Offset = 0,
[string]$OutFile
)
$ErrorActionPreference = 'Stop'
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
# --- Output helper ---
$script:lines = @()
function Out([string]$text) { $script:lines += $text }
# --- Resolve path ---
if (-not [System.IO.Path]::IsPathRooted($SubsystemPath)) {
$SubsystemPath = Join-Path (Get-Location).Path $SubsystemPath
}
# --- Helper: get LocalString text ---
function Get-MLText($node) {
if (-not $node -or -not $node.HasChildNodes) { return "" }
foreach ($item in $node.ChildNodes) {
if ($item.NodeType -ne 'Element') { continue }
$lang = ""; $content = ""
foreach ($c in $item.ChildNodes) {
if ($c.NodeType -ne 'Element') { continue }
if ($c.LocalName -eq "lang") { $lang = $c.InnerText }
if ($c.LocalName -eq "content") { $content = $c.InnerText }
}
if ($lang -eq "ru" -and $content) { return $content }
}
# fallback: first item
foreach ($item in $node.ChildNodes) {
if ($item.NodeType -ne 'Element') { continue }
foreach ($c in $item.ChildNodes) {
if ($c.NodeType -ne 'Element') { continue }
if ($c.LocalName -eq "content" -and $c.InnerText) { return $c.InnerText }
}
}
return ""
}
# --- Helper: load subsystem XML ---
function Load-SubsystemXml([string]$xmlPath) {
[xml]$doc = Get-Content -Path $xmlPath -Encoding UTF8
$ns = New-Object System.Xml.XmlNamespaceManager($doc.NameTable)
$ns.AddNamespace("md", "http://v8.1c.ru/8.3/MDClasses")
$ns.AddNamespace("v8", "http://v8.1c.ru/8.1/data/core")
$ns.AddNamespace("xr", "http://v8.1c.ru/8.3/xcf/readable")
$ns.AddNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance")
$sub = $doc.SelectSingleNode("/md:MetaDataObject/md:Subsystem", $ns)
if (-not $sub) {
Write-Host "[ERROR] Not a valid subsystem XML: $xmlPath"
exit 1
}
return @{ Doc=$doc; Ns=$ns; Sub=$sub }
}
# --- Helper: get content items ---
function Get-ContentItems($props, $ns) {
$items = @()
$contentNode = $props.SelectSingleNode("md:Content", $ns)
if (-not $contentNode -or -not $contentNode.HasChildNodes) { return $items }
foreach ($item in $contentNode.SelectNodes("xr:Item", $ns)) {
$items += $item.InnerText
}
return $items
}
# --- Helper: get child subsystem names ---
function Get-ChildNames($sub, $ns) {
$names = @()
$co = $sub.SelectSingleNode("md:ChildObjects", $ns)
if (-not $co -or -not $co.HasChildNodes) { return $names }
foreach ($child in $co.ChildNodes) {
if ($child.NodeType -eq 'Element' -and $child.LocalName -eq "Subsystem") {
$names += $child.InnerText
}
}
return $names
}
# --- Helper: group content by type ---
function Group-ContentByType($items) {
$groups = [ordered]@{}
foreach ($item in $items) {
if ($item -match '^([^.]+)\.(.+)$') {
$type = $Matches[1]
$name = $Matches[2]
} elseif ($item -match '^[0-9a-fA-F]{8}-') {
$type = "[UUID]"
$name = $item
} else {
$type = "[Other]"
$name = $item
}
if (-not $groups.Contains($type)) { $groups[$type] = @() }
$groups[$type] += $name
}
return $groups
}
# --- Helper: find subsystem dir from XML path ---
function Get-SubsystemDir([string]$xmlPath) {
$dir = [System.IO.Path]::GetDirectoryName($xmlPath)
$baseName = [System.IO.Path]::GetFileNameWithoutExtension($xmlPath)
return Join-Path $dir $baseName
}
# --- Show functions for full mode ---
function Show-Overview {
Out "Подсистема: $subName"
if ($synonym -and $synonym -ne $subName) { Out "Синоним: $synonym" }
if ($commentText) { Out "Комментарий: $commentText" }
Out "ВключатьВКомандныйИнтерфейс: $inclCI"
Out "ИспользоватьОднуКоманду: $useOneCmd"
if ($explanation) { Out "Пояснение: $explanation" }
if ($picText) { Out "Картинка: $picText" }
if ($contentItems.Count -gt 0) {
$parts = @()
foreach ($type in $groups.Keys) {
$parts += "$type`: $($groups[$type].Count)"
}
Out "Состав: $($contentItems.Count) объектов ($($parts -join ', '))"
} else {
Out "Состав: пусто"
}
if ($childNames.Count -gt 0) {
Out "Дочерние подсистемы ($($childNames.Count)): $($childNames -join ', ')"
}
if ($hasCI) {
Out "Командный интерфейс: есть"
}
}
function Show-Content {
Out "Состав подсистемы $subName ($($contentItems.Count) объектов):"
Out ""
if ($Name) {
if ($groups.Contains($Name)) {
$filtered = $groups[$Name]
Out "$Name ($($filtered.Count)):"
foreach ($n in $filtered) { Out " $n" }
} else {
Out "[INFO] Тип '$Name' не найден в составе."
Out "Доступные типы: $($groups.Keys -join ', ')"
}
} else {
foreach ($type in $groups.Keys) {
Out "$type ($($groups[$type].Count)):"
foreach ($n in $groups[$type]) { Out " $n" }
Out ""
}
}
}
function Show-CI {
$localSubDir = Get-SubsystemDir $SubsystemPath
$localCiPath = Join-Path (Join-Path $localSubDir "Ext") "CommandInterface.xml"
if (-not (Test-Path $localCiPath)) {
Out "Командный интерфейс: $subName"
Out ""
Out "Файл CommandInterface.xml не найден."
Out "Путь: $localCiPath"
} else {
[xml]$ciDoc = Get-Content -Path $localCiPath -Encoding UTF8
$ciNs = New-Object System.Xml.XmlNamespaceManager($ciDoc.NameTable)
$ciNs.AddNamespace("ci", "http://v8.1c.ru/8.3/xcf/extrnprops")
$ciNs.AddNamespace("xr", "http://v8.1c.ru/8.3/xcf/readable")
$ciRoot = $ciDoc.DocumentElement
Out "Командный интерфейс: $subName"
Out ""
# --- CommandsVisibility ---
$visSection = $ciRoot.SelectSingleNode("ci:CommandsVisibility", $ciNs)
if ($visSection) {
$hidden = @(); $shown = @()
foreach ($cmd in $visSection.SelectNodes("ci:Command", $ciNs)) {
$cmdName = $cmd.GetAttribute("name")
$vis = $cmd.SelectSingleNode("ci:Visibility/xr:Common", $ciNs)
if ($vis -and $vis.InnerText -eq "false") { $hidden += $cmdName }
else { $shown += $cmdName }
}
$total = $hidden.Count + $shown.Count
if (-not $Name -or $Name -eq "visibility") {
Out "Видимость ($total):"
if ($hidden.Count -gt 0) {
Out " СКРЫТО ($($hidden.Count)):"
foreach ($h in $hidden) { Out " $h" }
}
if ($shown.Count -gt 0) {
Out " ПОКАЗАНО ($($shown.Count)):"
foreach ($s in $shown) { Out " $s" }
}
Out ""
}
}
# --- CommandsPlacement ---
$placeSection = $ciRoot.SelectSingleNode("ci:CommandsPlacement", $ciNs)
if ($placeSection) {
$placements = @()
foreach ($cmd in $placeSection.SelectNodes("ci:Command", $ciNs)) {
$cmdName = $cmd.GetAttribute("name")
$grp = $cmd.SelectSingleNode("ci:CommandGroup", $ciNs)
$pl = $cmd.SelectSingleNode("ci:Placement", $ciNs)
$grpText = if ($grp) { $grp.InnerText } else { "?" }
$plText = if ($pl) { $pl.InnerText } else { "?" }
$placements += @{ Name=$cmdName; Group=$grpText; Placement=$plText }
}
if ((-not $Name -or $Name -eq "placement") -and $placements.Count -gt 0) {
Out "Размещение ($($placements.Count)):"
$arrow = [char]0x2192
foreach ($p in $placements) {
Out " $($p.Name) $arrow $($p.Group) ($($p.Placement))"
}
Out ""
}
}
# --- CommandsOrder ---
$orderSection = $ciRoot.SelectSingleNode("ci:CommandsOrder", $ciNs)
if ($orderSection) {
$orderGroups = [ordered]@{}
foreach ($cmd in $orderSection.SelectNodes("ci:Command", $ciNs)) {
$cmdName = $cmd.GetAttribute("name")
$grp = $cmd.SelectSingleNode("ci:CommandGroup", $ciNs)
$grpText = if ($grp) { $grp.InnerText } else { "?" }
if (-not $orderGroups.Contains($grpText)) { $orderGroups[$grpText] = @() }
$orderGroups[$grpText] += $cmdName
}
$totalOrder = 0
foreach ($k in $orderGroups.Keys) { $totalOrder += $orderGroups[$k].Count }
if ((-not $Name -or $Name -eq "order") -and $totalOrder -gt 0) {
Out "Порядок команд ($totalOrder):"
foreach ($grpName in $orderGroups.Keys) {
Out " [$grpName]:"
foreach ($c in $orderGroups[$grpName]) { Out " $c" }
}
Out ""
}
}
# --- SubsystemsOrder ---
$subOrderSection = $ciRoot.SelectSingleNode("ci:SubsystemsOrder", $ciNs)
if ($subOrderSection) {
$subOrder = @()
foreach ($s in $subOrderSection.SelectNodes("ci:Subsystem", $ciNs)) {
$subOrder += $s.InnerText
}
if ((-not $Name -or $Name -eq "subsystems") -and $subOrder.Count -gt 0) {
Out "Порядок подсистем ($($subOrder.Count)):"
for ($i = 0; $i -lt $subOrder.Count; $i++) {
Out " $($i+1). $($subOrder[$i])"
}
Out ""
}
}
# --- GroupsOrder ---
$grpOrderSection = $ciRoot.SelectSingleNode("ci:GroupsOrder", $ciNs)
if ($grpOrderSection) {
$grpOrder = @()
foreach ($g in $grpOrderSection.SelectNodes("ci:Group", $ciNs)) {
$grpOrder += $g.InnerText
}
if ((-not $Name -or $Name -eq "groups") -and $grpOrder.Count -gt 0) {
Out "Порядок групп ($($grpOrder.Count)):"
foreach ($g in $grpOrder) { Out " $g" }
}
}
}
}
# ============================================================
# Mode: tree
# ============================================================
if ($Mode -eq "tree") {
$isDir = Test-Path $SubsystemPath -PathType Container
$rootDir = $null
$rootXml = $null
if ($isDir) {
# Subsystems/ directory — show all top-level subsystems
$rootDir = $SubsystemPath
} else {
# Specific subsystem XML — show tree from this subsystem
if (-not (Test-Path $SubsystemPath)) {
Write-Host "[ERROR] File not found: $SubsystemPath"
exit 1
}
$rootXml = $SubsystemPath
}
# Box-drawing chars (PS 5.1 compatible)
$script:T_BRANCH = [char]0x251C + [char]0x2500 + [char]0x2500 + " " # ├──
$script:T_LAST = [char]0x2514 + [char]0x2500 + [char]0x2500 + " " # └──
$script:T_PIPE = [char]0x2502 + " " # │
$script:T_ARROW = [char]0x2192 # →
function Get-TreeLine([string]$xmlPath) {
$parsed = Load-SubsystemXml $xmlPath
$sub = $parsed.Sub; $ns = $parsed.Ns
$props = $sub.SelectSingleNode("md:Properties", $ns)
$name = $props.SelectSingleNode("md:Name", $ns).InnerText
$markers = @()
$subDir = Get-SubsystemDir $xmlPath
$ciPath = Join-Path (Join-Path $subDir "Ext") "CommandInterface.xml"
if (Test-Path $ciPath) { $markers += "CI" }
$useOne = $props.SelectSingleNode("md:UseOneCommand", $ns)
if ($useOne -and $useOne.InnerText -eq "true") { $markers += "OneCmd" }
$inclCI = $props.SelectSingleNode("md:IncludeInCommandInterface", $ns)
if ($inclCI -and $inclCI.InnerText -eq "false") { $markers += "Скрыт" }
$markerStr = if ($markers.Count -gt 0) { " [$($markers -join ', ')]" } else { "" }
$contentItems = @(Get-ContentItems $props $ns)
$childNames = @(Get-ChildNames $sub $ns)
$childStr = if ($childNames.Count -gt 0) { ", $($childNames.Count) дочерних" } else { "" }
return @{
Label = "$name$markerStr ($($contentItems.Count) объектов$childStr)"
SubDir = $subDir
ChildNames = $childNames
}
}
function Build-TreeEntry([string]$xmlPath, [string]$prefix, [bool]$isLast, [bool]$isRoot) {
$info = Get-TreeLine $xmlPath
$connector = if ($isRoot) { "" } elseif ($isLast) { $script:T_LAST } else { $script:T_BRANCH }
Out "$prefix$connector$($info.Label)"
if ($info.ChildNames.Count -gt 0) {
$childPrefix = if ($isRoot) { "" } elseif ($isLast) { "$prefix " } else { "$prefix$($script:T_PIPE)" }
$subsDir = Join-Path $info.SubDir "Subsystems"
for ($i = 0; $i -lt $info.ChildNames.Count; $i++) {
$childXml = Join-Path $subsDir "$($info.ChildNames[$i]).xml"
$childIsLast = ($i -eq $info.ChildNames.Count - 1)
if (Test-Path $childXml) {
Build-TreeEntry $childXml $childPrefix $childIsLast $false
} else {
$conn2 = if ($childIsLast) { $script:T_LAST } else { $script:T_BRANCH }
Out "$childPrefix$conn2$($info.ChildNames[$i]) [NOT FOUND]"
}
}
}
}
if ($rootDir) {
$label = Split-Path $rootDir -Leaf
Out "Дерево подсистем от: $label/"
Out ""
$xmlFiles = @(Get-ChildItem $rootDir -Filter "*.xml" -File | Sort-Object Name)
if ($Name) {
$xmlFiles = @($xmlFiles | Where-Object { $_.BaseName -eq $Name })
if ($xmlFiles.Count -eq 0) {
Write-Host "[ERROR] Subsystem '$Name' not found in $rootDir"
exit 1
}
}
for ($i = 0; $i -lt $xmlFiles.Count; $i++) {
Build-TreeEntry $xmlFiles[$i].FullName "" ($i -eq $xmlFiles.Count - 1) $true
}
} else {
Build-TreeEntry $rootXml "" $true $true
}
} elseif ($Mode -eq "ci") {
# ============================================================
# Mode: ci — CommandInterface.xml
# ============================================================
if (Test-Path $SubsystemPath -PathType Container) {
Write-Host "[ERROR] ci mode requires a subsystem .xml file, not a directory"
exit 1
}
# File not found — check Dir/Name/Name.xml → Dir/Name.xml
if (-not (Test-Path $SubsystemPath)) {
$fn = [System.IO.Path]::GetFileNameWithoutExtension($SubsystemPath)
$pd = Split-Path $SubsystemPath
if ($fn -eq (Split-Path $pd -Leaf)) {
$c = Join-Path (Split-Path $pd) "$fn.xml"
if (Test-Path $c) { $SubsystemPath = $c }
}
}
if (-not (Test-Path $SubsystemPath)) {
Write-Host "[ERROR] File not found: $SubsystemPath"
exit 1
}
$parsed = Load-SubsystemXml $SubsystemPath
$sub = $parsed.Sub; $ns = $parsed.Ns
$props = $sub.SelectSingleNode("md:Properties", $ns)
$subName = $props.SelectSingleNode("md:Name", $ns).InnerText
Show-CI
} else {
# ============================================================
# Mode: overview / content — requires a subsystem XML file
# ============================================================
if (Test-Path $SubsystemPath -PathType Container) {
$dirName = Split-Path $SubsystemPath -Leaf
$candidate = Join-Path $SubsystemPath "$dirName.xml"
$sibling = Join-Path (Split-Path $SubsystemPath) "$dirName.xml"
if (Test-Path $candidate) {
$SubsystemPath = $candidate
} elseif (Test-Path $sibling) {
$SubsystemPath = $sibling
} else {
Write-Host "[ERROR] No $dirName.xml found in directory. Use -Mode tree for directory listing."
exit 1
}
}
# File not found — check Dir/Name/Name.xml → Dir/Name.xml
if (-not (Test-Path $SubsystemPath)) {
$fn = [System.IO.Path]::GetFileNameWithoutExtension($SubsystemPath)
$pd = Split-Path $SubsystemPath
if ($fn -eq (Split-Path $pd -Leaf)) {
$c = Join-Path (Split-Path $pd) "$fn.xml"
if (Test-Path $c) { $SubsystemPath = $c }
}
}
if (-not (Test-Path $SubsystemPath)) {
Write-Host "[ERROR] File not found: $SubsystemPath"
exit 1
}
$parsed = Load-SubsystemXml $SubsystemPath
$sub = $parsed.Sub; $ns = $parsed.Ns
$props = $sub.SelectSingleNode("md:Properties", $ns)
$subName = $props.SelectSingleNode("md:Name", $ns).InnerText
$synonym = Get-MLText $props.SelectSingleNode("md:Synonym", $ns)
$comment = $props.SelectSingleNode("md:Comment", $ns)
$commentText = if ($comment -and $comment.InnerText) { $comment.InnerText } else { "" }
$inclHelp = $props.SelectSingleNode("md:IncludeHelpInContents", $ns).InnerText
$inclCI = $props.SelectSingleNode("md:IncludeInCommandInterface", $ns).InnerText
$useOneCmd = $props.SelectSingleNode("md:UseOneCommand", $ns).InnerText
$explanation = Get-MLText $props.SelectSingleNode("md:Explanation", $ns)
# Picture
$picNode = $props.SelectSingleNode("md:Picture", $ns)
$picText = ""
if ($picNode -and $picNode.HasChildNodes) {
$picRef = $picNode.SelectSingleNode("xr:Ref", $ns)
if ($picRef -and $picRef.InnerText) { $picText = $picRef.InnerText }
}
# Content
$contentItems = @(Get-ContentItems $props $ns)
$groups = Group-ContentByType $contentItems
# Children
$childNames = @(Get-ChildNames $sub $ns)
# CI presence
$subDir = Get-SubsystemDir $SubsystemPath
$ciPath = Join-Path (Join-Path $subDir "Ext") "CommandInterface.xml"
$hasCI = Test-Path $ciPath
if ($Mode -eq "overview") {
Show-Overview
} elseif ($Mode -eq "content") {
Show-Content
} elseif ($Mode -eq "full") {
Show-Overview
Out ""; Out "--- content ---"; Out ""
Show-Content
Out ""; Out "--- ci ---"; Out ""
Show-CI
}
}
# --- Pagination and output ---
$totalLines = $script:lines.Count
$outLines = $script:lines
if ($Offset -gt 0) {
if ($Offset -ge $totalLines) {
Write-Host "[INFO] Offset $Offset exceeds total lines ($totalLines). Nothing to show."
exit 0
}
$outLines = $outLines[$Offset..($totalLines - 1)]
}
if ($Limit -gt 0 -and $outLines.Count -gt $Limit) {
$shown = $outLines[0..($Limit - 1)]
$remaining = $totalLines - $Offset - $Limit
$shown += ""
$shown += "[ОБРЕЗАНО] Показано $Limit из $totalLines строк. Используйте -Offset $($Offset + $Limit) для продолжения."
$outLines = $shown
}
if ($OutFile) {
if (-not [System.IO.Path]::IsPathRooted($OutFile)) {
$OutFile = Join-Path (Get-Location).Path $OutFile
}
$utf8 = New-Object System.Text.UTF8Encoding($true)
[System.IO.File]::WriteAllLines($OutFile, $outLines, $utf8)
Write-Host "Output written to $OutFile"
} else {
foreach ($l in $outLines) { Write-Host $l }
}
@@ -0,0 +1,525 @@
#!/usr/bin/env python3
# subsystem-info v1.0 — Compact summary of 1C subsystem structure
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
import os
import re
import sys
from collections import OrderedDict
from lxml import etree
sys.stdout.reconfigure(encoding="utf-8")
sys.stderr.reconfigure(encoding="utf-8")
# --- Argument parsing ---
parser = argparse.ArgumentParser(description="Analyze 1C subsystem structure", allow_abbrev=False)
parser.add_argument("-SubsystemPath", "-Path", required=True, help="Path to subsystem XML or Subsystems/ directory")
parser.add_argument("-Mode", choices=["overview", "content", "ci", "tree", "full"], default="overview", help="Output mode")
parser.add_argument("-Name", default="", help="Filter by name/type")
parser.add_argument("-Limit", type=int, default=150, help="Max lines to show")
parser.add_argument("-Offset", type=int, default=0, help="Lines to skip")
parser.add_argument("-OutFile", default="", help="Write output to file")
args = parser.parse_args()
# --- Output helper ---
lines_buf = []
def out(text=""):
lines_buf.append(text)
# --- Resolve path ---
subsystem_path = args.SubsystemPath
if not os.path.isabs(subsystem_path):
subsystem_path = os.path.join(os.getcwd(), subsystem_path)
NS = {
"md": "http://v8.1c.ru/8.3/MDClasses",
"v8": "http://v8.1c.ru/8.1/data/core",
"xr": "http://v8.1c.ru/8.3/xcf/readable",
"xsi": "http://www.w3.org/2001/XMLSchema-instance",
}
CI_NS = {
"ci": "http://v8.1c.ru/8.3/xcf/extrnprops",
"xr": "http://v8.1c.ru/8.3/xcf/readable",
}
# --- Helper: get LocalString text ---
def get_ml_text(node):
if node is None:
return ""
# Look for v8:item children
for item in node:
if not isinstance(item.tag, str):
continue
lang = ""
content = ""
for c in item:
if not isinstance(c.tag, str):
continue
local = etree.QName(c.tag).localname
if local == "lang":
lang = c.text or ""
if local == "content":
content = c.text or ""
if lang == "ru" and content:
return content
# fallback: first item
for item in node:
if not isinstance(item.tag, str):
continue
for c in item:
if not isinstance(c.tag, str):
continue
local = etree.QName(c.tag).localname
if local == "content" and c.text:
return c.text
return ""
# --- Helper: load subsystem XML ---
def load_subsystem_xml(xml_path):
tree = etree.parse(xml_path, etree.XMLParser(remove_blank_text=False))
doc_root = tree.getroot()
sub = doc_root.find("md:Subsystem", NS)
if sub is None:
print(f"[ERROR] Not a valid subsystem XML: {xml_path}", file=sys.stderr)
sys.exit(1)
return {"Doc": doc_root, "Sub": sub}
# --- Helper: get content items ---
def get_content_items(props):
items = []
content_node = props.find("md:Content", NS)
if content_node is None:
return items
for item in content_node.findall("xr:Item", NS):
if item.text:
items.append(item.text)
return items
# --- Helper: get child subsystem names ---
def get_child_names(sub):
names = []
co = sub.find("md:ChildObjects", NS)
if co is None:
return names
for child in co:
if not isinstance(child.tag, str):
continue
if etree.QName(child.tag).localname == "Subsystem":
names.append(child.text or "")
return names
# --- Helper: group content by type ---
def group_content_by_type(items):
groups = OrderedDict()
for item in items:
m = re.match(r'^([^.]+)\.(.+)$', item)
if m:
type_name = m.group(1)
name = m.group(2)
elif re.match(r'^[0-9a-fA-F]{8}-', item):
type_name = "[UUID]"
name = item
else:
type_name = "[Other]"
name = item
if type_name not in groups:
groups[type_name] = []
groups[type_name].append(name)
return groups
# --- Helper: find subsystem dir from XML path ---
def get_subsystem_dir(xml_path):
dir_name = os.path.dirname(xml_path)
base_name = os.path.splitext(os.path.basename(xml_path))[0]
return os.path.join(dir_name, base_name)
# --- Show functions ---
def show_overview(sub_name, synonym, comment_text, incl_ci, use_one_cmd,
explanation, pic_text, content_items, groups, child_names, has_ci):
out(f"Подсистема: {sub_name}")
if synonym and synonym != sub_name:
out(f"Синоним: {synonym}")
if comment_text:
out(f"Комментарий: {comment_text}")
out(f"ВключатьВКомандныйИнтерфейс: {incl_ci}")
out(f"ИспользоватьОднуКоманду: {use_one_cmd}")
if explanation:
out(f"Пояснение: {explanation}")
if pic_text:
out(f"Картинка: {pic_text}")
if len(content_items) > 0:
parts = []
for type_name in groups:
parts.append(f"{type_name}: {len(groups[type_name])}")
out(f"Состав: {len(content_items)} объектов ({', '.join(parts)})")
else:
out("Состав: пусто")
if len(child_names) > 0:
out(f"Дочерние подсистемы ({len(child_names)}): {', '.join(child_names)}")
if has_ci:
out("Командный интерфейс: есть")
def show_content(sub_name, content_items, groups, name_filter):
out(f"Состав подсистемы {sub_name} ({len(content_items)} объектов):")
out()
if name_filter:
if name_filter in groups:
filtered = groups[name_filter]
out(f"{name_filter} ({len(filtered)}):")
for n in filtered:
out(f" {n}")
else:
out(f"[INFO] Тип '{name_filter}' не найден в составе.")
out(f"Доступные типы: {', '.join(groups.keys())}")
else:
for type_name in groups:
out(f"{type_name} ({len(groups[type_name])}):")
for n in groups[type_name]:
out(f" {n}")
out()
def show_ci(sub_name, subsystem_path_local):
local_sub_dir = get_subsystem_dir(subsystem_path_local)
local_ci_path = os.path.join(local_sub_dir, "Ext", "CommandInterface.xml")
if not os.path.isfile(local_ci_path):
out(f"Командный интерфейс: {sub_name}")
out()
out("Файл CommandInterface.xml не найден.")
out(f"Путь: {local_ci_path}")
else:
ci_tree = etree.parse(local_ci_path, etree.XMLParser(remove_blank_text=False))
ci_root = ci_tree.getroot()
out(f"Командный интерфейс: {sub_name}")
out()
# --- CommandsVisibility ---
vis_section = ci_root.find("ci:CommandsVisibility", CI_NS)
if vis_section is not None:
hidden = []
shown = []
for cmd in vis_section.findall("ci:Command", CI_NS):
cmd_name = cmd.get("name", "")
vis = cmd.find("ci:Visibility/xr:Common", CI_NS)
if vis is not None and vis.text == "false":
hidden.append(cmd_name)
else:
shown.append(cmd_name)
total = len(hidden) + len(shown)
if not args.Name or args.Name == "visibility":
out(f"Видимость ({total}):")
if hidden:
out(f" СКРЫТО ({len(hidden)}):")
for h in hidden:
out(f" {h}")
if shown:
out(f" ПОКАЗАНО ({len(shown)}):")
for s in shown:
out(f" {s}")
out()
# --- CommandsPlacement ---
place_section = ci_root.find("ci:CommandsPlacement", CI_NS)
if place_section is not None:
placements = []
for cmd in place_section.findall("ci:Command", CI_NS):
cmd_name = cmd.get("name", "")
grp = cmd.find("ci:CommandGroup", CI_NS)
pl = cmd.find("ci:Placement", CI_NS)
grp_text = grp.text if grp is not None and grp.text else "?"
pl_text = pl.text if pl is not None and pl.text else "?"
placements.append({"Name": cmd_name, "Group": grp_text, "Placement": pl_text})
if (not args.Name or args.Name == "placement") and placements:
arrow = "\u2192"
out(f"Размещение ({len(placements)}):")
for p in placements:
out(f" {p['Name']} {arrow} {p['Group']} ({p['Placement']})")
out()
# --- CommandsOrder ---
order_section = ci_root.find("ci:CommandsOrder", CI_NS)
if order_section is not None:
order_groups = OrderedDict()
for cmd in order_section.findall("ci:Command", CI_NS):
cmd_name = cmd.get("name", "")
grp = cmd.find("ci:CommandGroup", CI_NS)
grp_text = grp.text if grp is not None and grp.text else "?"
if grp_text not in order_groups:
order_groups[grp_text] = []
order_groups[grp_text].append(cmd_name)
total_order = sum(len(v) for v in order_groups.values())
if (not args.Name or args.Name == "order") and total_order > 0:
out(f"Порядок команд ({total_order}):")
for grp_name, cmds in order_groups.items():
out(f" [{grp_name}]:")
for c in cmds:
out(f" {c}")
out()
# --- SubsystemsOrder ---
sub_order_section = ci_root.find("ci:SubsystemsOrder", CI_NS)
if sub_order_section is not None:
sub_order = []
for s in sub_order_section.findall("ci:Subsystem", CI_NS):
if s.text:
sub_order.append(s.text)
if (not args.Name or args.Name == "subsystems") and sub_order:
out(f"Порядок подсистем ({len(sub_order)}):")
for i, s in enumerate(sub_order):
out(f" {i + 1}. {s}")
out()
# --- GroupsOrder ---
grp_order_section = ci_root.find("ci:GroupsOrder", CI_NS)
if grp_order_section is not None:
grp_order = []
for g in grp_order_section.findall("ci:Group", CI_NS):
if g.text:
grp_order.append(g.text)
if (not args.Name or args.Name == "groups") and grp_order:
out(f"Порядок групп ({len(grp_order)}):")
for g in grp_order:
out(f" {g}")
# ============================================================
# Mode: tree
# ============================================================
if args.Mode == "tree":
is_dir = os.path.isdir(subsystem_path)
root_dir = None
root_xml = None
if is_dir:
root_dir = subsystem_path
else:
if not os.path.isfile(subsystem_path):
print(f"[ERROR] File not found: {subsystem_path}", file=sys.stderr)
sys.exit(1)
root_xml = subsystem_path
# Box-drawing chars
T_BRANCH = "\u251C\u2500\u2500 " # ├──
T_LAST = "\u2514\u2500\u2500 " # └──
T_PIPE = "\u2502 " # │
T_ARROW = "\u2192" # →
def get_tree_line(xml_path):
parsed = load_subsystem_xml(xml_path)
sub = parsed["Sub"]
props = sub.find("md:Properties", NS)
name_node = props.find("md:Name", NS)
name = name_node.text if name_node is not None else ""
markers = []
sub_dir = get_subsystem_dir(xml_path)
ci_path = os.path.join(sub_dir, "Ext", "CommandInterface.xml")
if os.path.isfile(ci_path):
markers.append("CI")
use_one = props.find("md:UseOneCommand", NS)
if use_one is not None and use_one.text == "true":
markers.append("OneCmd")
incl_ci_node = props.find("md:IncludeInCommandInterface", NS)
if incl_ci_node is not None and incl_ci_node.text == "false":
markers.append("Скрыт")
marker_str = f" [{', '.join(markers)}]" if markers else ""
content_items = get_content_items(props)
child_names = get_child_names(sub)
child_str = f", {len(child_names)} дочерних" if child_names else ""
return {
"Label": f"{name}{marker_str} ({len(content_items)} объектов{child_str})",
"SubDir": sub_dir,
"ChildNames": child_names,
}
def build_tree_entry(xml_path, prefix, is_last, is_root):
info = get_tree_line(xml_path)
if is_root:
connector = ""
elif is_last:
connector = T_LAST
else:
connector = T_BRANCH
out(f"{prefix}{connector}{info['Label']}")
if info["ChildNames"]:
if is_root:
child_prefix = ""
elif is_last:
child_prefix = prefix + " "
else:
child_prefix = prefix + T_PIPE
subs_dir = os.path.join(info["SubDir"], "Subsystems")
for i, child_name in enumerate(info["ChildNames"]):
child_xml = os.path.join(subs_dir, f"{child_name}.xml")
child_is_last = (i == len(info["ChildNames"]) - 1)
if os.path.isfile(child_xml):
build_tree_entry(child_xml, child_prefix, child_is_last, False)
else:
conn2 = T_LAST if child_is_last else T_BRANCH
out(f"{child_prefix}{conn2}{child_name} [NOT FOUND]")
if root_dir:
label = os.path.basename(root_dir)
out(f"Дерево подсистем от: {label}/")
out()
xml_files = sorted(
[f for f in os.listdir(root_dir) if f.lower().endswith(".xml") and os.path.isfile(os.path.join(root_dir, f))],
key=lambda x: x.lower()
)
if args.Name:
xml_files = [f for f in xml_files if os.path.splitext(f)[0] == args.Name]
if not xml_files:
print(f"[ERROR] Subsystem '{args.Name}' not found in {root_dir}", file=sys.stderr)
sys.exit(1)
for i, fname in enumerate(xml_files):
build_tree_entry(os.path.join(root_dir, fname), "", i == len(xml_files) - 1, True)
else:
build_tree_entry(root_xml, "", True, True)
elif args.Mode == "ci":
# ============================================================
# Mode: ci -- CommandInterface.xml
# ============================================================
if os.path.isdir(subsystem_path):
print("[ERROR] ci mode requires a subsystem .xml file, not a directory", file=sys.stderr)
sys.exit(1)
# File not found -- check Dir/Name/Name.xml -> Dir/Name.xml
if not os.path.isfile(subsystem_path):
fn = os.path.splitext(os.path.basename(subsystem_path))[0]
pd = os.path.dirname(subsystem_path)
if fn == os.path.basename(pd):
c = os.path.join(os.path.dirname(pd), f"{fn}.xml")
if os.path.isfile(c):
subsystem_path = c
if not os.path.isfile(subsystem_path):
print(f"[ERROR] File not found: {subsystem_path}", file=sys.stderr)
sys.exit(1)
parsed = load_subsystem_xml(subsystem_path)
sub = parsed["Sub"]
props = sub.find("md:Properties", NS)
name_node = props.find("md:Name", NS)
sub_name = name_node.text if name_node is not None else ""
show_ci(sub_name, subsystem_path)
else:
# ============================================================
# Mode: overview / content / full -- requires a subsystem XML file
# ============================================================
if os.path.isdir(subsystem_path):
dir_name = os.path.basename(subsystem_path)
candidate = os.path.join(subsystem_path, f"{dir_name}.xml")
sibling = os.path.join(os.path.dirname(subsystem_path), f"{dir_name}.xml")
if os.path.isfile(candidate):
subsystem_path = candidate
elif os.path.isfile(sibling):
subsystem_path = sibling
else:
print(f"[ERROR] No {dir_name}.xml found in directory. Use -Mode tree for directory listing.", file=sys.stderr)
sys.exit(1)
# File not found -- check Dir/Name/Name.xml -> Dir/Name.xml
if not os.path.isfile(subsystem_path):
fn = os.path.splitext(os.path.basename(subsystem_path))[0]
pd = os.path.dirname(subsystem_path)
if fn == os.path.basename(pd):
c = os.path.join(os.path.dirname(pd), f"{fn}.xml")
if os.path.isfile(c):
subsystem_path = c
if not os.path.isfile(subsystem_path):
print(f"[ERROR] File not found: {subsystem_path}", file=sys.stderr)
sys.exit(1)
parsed = load_subsystem_xml(subsystem_path)
sub = parsed["Sub"]
props = sub.find("md:Properties", NS)
name_node = props.find("md:Name", NS)
sub_name = name_node.text if name_node is not None else ""
synonym = get_ml_text(props.find("md:Synonym", NS))
comment_node = props.find("md:Comment", NS)
comment_text = comment_node.text if comment_node is not None and comment_node.text else ""
incl_help_node = props.find("md:IncludeHelpInContents", NS)
incl_help = incl_help_node.text if incl_help_node is not None else ""
incl_ci_node = props.find("md:IncludeInCommandInterface", NS)
incl_ci = incl_ci_node.text if incl_ci_node is not None else ""
use_one_cmd_node = props.find("md:UseOneCommand", NS)
use_one_cmd = use_one_cmd_node.text if use_one_cmd_node is not None else ""
explanation = get_ml_text(props.find("md:Explanation", NS))
# Picture
pic_node = props.find("md:Picture", NS)
pic_text = ""
if pic_node is not None and len(pic_node) > 0:
pic_ref = pic_node.find("xr:Ref", NS)
if pic_ref is not None and pic_ref.text:
pic_text = pic_ref.text
# Content
content_items = get_content_items(props)
groups = group_content_by_type(content_items)
# Children
child_names = get_child_names(sub)
# CI presence
sub_dir = get_subsystem_dir(subsystem_path)
ci_path = os.path.join(sub_dir, "Ext", "CommandInterface.xml")
has_ci = os.path.isfile(ci_path)
if args.Mode == "overview":
show_overview(sub_name, synonym, comment_text, incl_ci, use_one_cmd,
explanation, pic_text, content_items, groups, child_names, has_ci)
elif args.Mode == "content":
show_content(sub_name, content_items, groups, args.Name)
elif args.Mode == "full":
show_overview(sub_name, synonym, comment_text, incl_ci, use_one_cmd,
explanation, pic_text, content_items, groups, child_names, has_ci)
out()
out("--- content ---")
out()
show_content(sub_name, content_items, groups, args.Name)
out()
out("--- ci ---")
out()
show_ci(sub_name, subsystem_path)
# --- Pagination and output ---
total_lines = len(lines_buf)
out_lines = lines_buf[:]
if args.Offset > 0:
if args.Offset >= total_lines:
print(f"[INFO] Offset {args.Offset} exceeds total lines ({total_lines}). Nothing to show.")
sys.exit(0)
out_lines = out_lines[args.Offset:]
if args.Limit > 0 and len(out_lines) > args.Limit:
shown = out_lines[:args.Limit]
remaining = total_lines - args.Offset - args.Limit
shown.append("")
shown.append(f"[ОБРЕЗАНО] Показано {args.Limit} из {total_lines} строк. Используйте -Offset {args.Offset + args.Limit} для продолжения.")
out_lines = shown
if args.OutFile:
out_file = args.OutFile
if not os.path.isabs(out_file):
out_file = os.path.join(os.getcwd(), out_file)
with open(out_file, "w", encoding="utf-8-sig") as f:
f.write("\n".join(out_lines))
print(f"Output written to {out_file}")
else:
for line in out_lines:
print(line)