feat(skd): @name= in set-structure, Folder in selection

- skd-edit: set-structure supports @name= for naming groupings (e.g. "Поле @name=ДанныеОтчета > details")
- skd-edit: add-selection supports Folder(Title: field1, field2) syntax for SelectedItemFolder
- skd-compile: selection supports {"folder": "Title", "items": [...]} for SelectedItemFolder
- Both generate lwsTitle, nested SelectedItemField items, and placement=Auto

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Nick Shirokov
2026-04-06 19:23:18 +03:00
parent d755c41233
commit 9bfc431a6a
4 changed files with 100 additions and 0 deletions
@@ -1367,6 +1367,22 @@ function Emit-Selection {
X "$indent`t`t<dcsset:field>$(Esc-Xml $item)</dcsset:field>"
X "$indent`t</dcsset:item>"
}
} elseif ($item.folder) {
X "$indent`t<dcsset:item xsi:type=`"dcsset:SelectedItemFolder`">"
X "$indent`t`t<dcsset:lwsTitle>"
X "$indent`t`t`t<v8:item>"
X "$indent`t`t`t`t<v8:lang>ru</v8:lang>"
X "$indent`t`t`t`t<v8:content>$(Esc-Xml "$($item.folder)")</v8:content>"
X "$indent`t`t`t</v8:item>"
X "$indent`t`t</dcsset:lwsTitle>"
foreach ($sub in $item.items) {
$subName = if ($sub -is [string]) { $sub } else { "$($sub.field)" }
X "$indent`t`t<dcsset:item xsi:type=`"dcsset:SelectedItemField`">"
X "$indent`t`t`t<dcsset:field>$(Esc-Xml $subName)</dcsset:field>"
X "$indent`t`t</dcsset:item>"
}
X "$indent`t`t<dcsset:placement>Auto</dcsset:placement>"
X "$indent`t</dcsset:item>"
} else {
X "$indent`t<dcsset:item xsi:type=`"dcsset:SelectedItemField`">"
X "$indent`t`t<dcsset:field>$(Esc-Xml "$($item.field)")</dcsset:field>"
@@ -1154,6 +1154,21 @@ def emit_selection(lines, items, indent, skip_auto=False):
lines.append(f'{indent}\t<dcsset:item xsi:type="dcsset:SelectedItemField">')
lines.append(f'{indent}\t\t<dcsset:field>{esc_xml(item)}</dcsset:field>')
lines.append(f'{indent}\t</dcsset:item>')
elif item.get('folder'):
lines.append(f'{indent}\t<dcsset:item xsi:type="dcsset:SelectedItemFolder">')
lines.append(f'{indent}\t\t<dcsset:lwsTitle>')
lines.append(f'{indent}\t\t\t<v8:item>')
lines.append(f'{indent}\t\t\t\t<v8:lang>ru</v8:lang>')
lines.append(f'{indent}\t\t\t\t<v8:content>{esc_xml(str(item["folder"]))}</v8:content>')
lines.append(f'{indent}\t\t\t</v8:item>')
lines.append(f'{indent}\t\t</dcsset:lwsTitle>')
for sub in (item.get('items') or []):
sub_name = str(sub.get('field', sub)) if isinstance(sub, dict) else str(sub)
lines.append(f'{indent}\t\t<dcsset:item xsi:type="dcsset:SelectedItemField">')
lines.append(f'{indent}\t\t\t<dcsset:field>{esc_xml(sub_name)}</dcsset:field>')
lines.append(f'{indent}\t\t</dcsset:item>')
lines.append(f'{indent}\t\t<dcsset:placement>Auto</dcsset:placement>')
lines.append(f'{indent}\t</dcsset:item>')
else:
lines.append(f'{indent}\t<dcsset:item xsi:type="dcsset:SelectedItemField">')
lines.append(f'{indent}\t\t<dcsset:field>{esc_xml(str(item["field"]))}</dcsset:field>')
@@ -543,6 +543,11 @@ function Parse-StructureShorthand {
$seg = $segments[$i].Trim()
$group = @{ type = "group" }
if ($seg -match '@name=(.+)') {
$group["name"] = $Matches[1].Trim()
$seg = ($seg -replace '\s*@name=.+', '').Trim()
}
if ($seg -match '^(?i)(details|детали)$') {
$group["groupBy"] = @()
} else {
@@ -861,6 +866,32 @@ function Build-SelectionItemFragment {
$lines = @()
if ($fieldName -eq "Auto") {
$lines += "$i<dcsset:item xsi:type=`"dcsset:SelectedItemAuto`"/>"
} elseif ($fieldName -match '^Folder\((.+)\)$') {
$inner = $Matches[1]
$colonIdx = $inner.IndexOf(':')
if ($colonIdx -gt 0) {
$title = $inner.Substring(0, $colonIdx).Trim()
$items = $inner.Substring($colonIdx + 1) -split ',' | ForEach-Object { $_.Trim() } | Where-Object { $_ }
} else {
$title = ""
$items = $inner -split ',' | ForEach-Object { $_.Trim() } | Where-Object { $_ }
}
$lines += "$i<dcsset:item xsi:type=`"dcsset:SelectedItemFolder`">"
if ($title) {
$lines += "$i`t<dcsset:lwsTitle>"
$lines += "$i`t`t<v8:item>"
$lines += "$i`t`t`t<v8:lang>ru</v8:lang>"
$lines += "$i`t`t`t<v8:content>$(Esc-Xml $title)</v8:content>"
$lines += "$i`t`t</v8:item>"
$lines += "$i`t</dcsset:lwsTitle>"
}
foreach ($item in $items) {
$lines += "$i`t<dcsset:item xsi:type=`"dcsset:SelectedItemField`">"
$lines += "$i`t`t<dcsset:field>$(Esc-Xml $item)</dcsset:field>"
$lines += "$i`t</dcsset:item>"
}
$lines += "$i`t<dcsset:placement>Auto</dcsset:placement>"
$lines += "$i</dcsset:item>"
} else {
$lines += "$i<dcsset:item xsi:type=`"dcsset:SelectedItemField`">"
$lines += "$i`t<dcsset:field>$(Esc-Xml $fieldName)</dcsset:field>"
@@ -1070,6 +1101,11 @@ function Build-StructureItemFragment {
$lines = @()
$lines += "$i<dcsset:item xsi:type=`"dcsset:StructureItemGroup`">"
# name
if ($item["name"]) {
$lines += "$i`t<dcsset:name>$(Esc-Xml $item["name"])</dcsset:name>"
}
# groupItems
$groupBy = $item["groupBy"]
if (-not $groupBy -or $groupBy.Count -eq 0) {
@@ -509,6 +509,11 @@ def parse_structure_shorthand(s):
seg = segments[i].strip()
group = {"type": "group"}
name_m = re.search(r'\s*@name=(.+)', seg)
if name_m:
group["name"] = name_m.group(1).strip()
seg = re.sub(r'\s*@name=.+', '', seg).strip()
if re.match(r'^(?i)(details|\u0434\u0435\u0442\u0430\u043b\u0438)$', seg):
group["groupBy"] = []
else:
@@ -767,6 +772,31 @@ def build_selection_item_fragment(field_name, indent):
i = indent
if field_name == "Auto":
return f'{i}<dcsset:item xsi:type="dcsset:SelectedItemAuto"/>'
m = re.match(r'^Folder\((.+)\)$', field_name)
if m:
inner = m.group(1)
colon_idx = inner.find(':')
if colon_idx > 0:
title = inner[:colon_idx].strip()
items = [x.strip() for x in inner[colon_idx + 1:].split(',') if x.strip()]
else:
title = ""
items = [x.strip() for x in inner.split(',') if x.strip()]
lines = [f'{i}<dcsset:item xsi:type="dcsset:SelectedItemFolder">']
if title:
lines.append(f"{i}\t<dcsset:lwsTitle>")
lines.append(f"{i}\t\t<v8:item>")
lines.append(f"{i}\t\t\t<v8:lang>ru</v8:lang>")
lines.append(f"{i}\t\t\t<v8:content>{esc_xml(title)}</v8:content>")
lines.append(f"{i}\t\t</v8:item>")
lines.append(f"{i}\t</dcsset:lwsTitle>")
for item in items:
lines.append(f'{i}\t<dcsset:item xsi:type="dcsset:SelectedItemField">')
lines.append(f"{i}\t\t<dcsset:field>{esc_xml(item)}</dcsset:field>")
lines.append(f"{i}\t</dcsset:item>")
lines.append(f"{i}\t<dcsset:placement>Auto</dcsset:placement>")
lines.append(f"{i}</dcsset:item>")
return "\r\n".join(lines)
lines = [
f'{i}<dcsset:item xsi:type="dcsset:SelectedItemField">',
f"{i}\t<dcsset:field>{esc_xml(field_name)}</dcsset:field>",
@@ -944,6 +974,9 @@ def build_structure_item_fragment(item, indent):
i = indent
lines = [f'{i}<dcsset:item xsi:type="dcsset:StructureItemGroup">']
if item.get("name"):
lines.append(f"{i}\t<dcsset:name>{esc_xml(item['name'])}</dcsset:name>")
group_by = item.get("groupBy", [])
if not group_by:
lines.append(f"{i}\t<dcsset:groupItems/>")