# с gitverse
npm install -g git+https://gitverse.ru/mikle33/logipard
# или: с github
npm install -g git+https://github.com/sbtrn-devil/logipard
# или: с npm
# TODO
# с gitverse
npm install git+https://gitverse.ru/mikle33/logipard
# или: с github
npm install git+https://github.com/sbtrn-devil/logipard
# или: с npm
# TODO
lp-cli [cmd line options]
node_modules/.bin/lp-cli [cmd line options]
node_modules\.bin\lp-cli [cmd line options]
-h
или --help
для справки по опциям командной строки (их достаточно немного, спецификация основной части работы
предполагается через конфигурационный файл (или файлы)).<PROJECT_ROOT>
), и, возможно, даже создали в ней package.json
(будем считать, что это библиотека для Node.js).lp-cli [options]
(если вы установили локально,
то внесите соответствующую поправку).<PROJECT_ROOT>/index.js
module.exports.leftpad = function leftpad(str, len, padding = ' ') {
str = str + '';
if (str.length >= len) return str;
padding = (padding = padding + '').length ? padding : ' ';
while (padding.length + str.length < len) {
padding = padding + padding;
}
return padding.substring(0, len - str.length) + str;
}
<PROJECT_ROOT>/index.js
//#LP functions/leftpad { <#./%title: leftpad(str, len[, padding])#>
// Pad a string from left to the given minimum length with whitespaces or a given padding string.
//#LP ./str %arg: String to be padded
//#LP ./len %arg: Number, minimum required length of the padded string (<#ref leftpad/str#>)
//#LP ./padding %arg: String, optional. If specified, this string or its portion will be used for the padding instead of spaces.
//
// The string is repeated as required if it is less than the length required for the padding, and its leftmost part of the required length is used.
module.exports.leftpad = function leftpad(str, len, padding = ' ') {
... // almost everything within can be left as is so far
//#LP ./%return: The padded string with minimum length of (<#ref leftpad/len#>)
return padding.substring(0, len - str.length) + str;
}
//#LP }
//
), возможно с некоторым кодом перед ними в начале строки, причём первый комментарий
начинается с токена #LP
, и до ближайшей строки, которая не заканчивается однострочным комментарием. В нашем случае, //
после ./padding
нужна для того, чтобы комментарий // The string is ...
был включён в последовательность.
С другой стороны, линия с комментарием // almost everything ...
отделена от последовательности линией без комментария, так что это "просто комментарий в коде", который не принимается во внимание.functions
: элемент автоматически введён в модель в силу того, что мы задали его члены-подэлементы, будем считать его контейнером для списка функцийfunctions/leftpad
: основной элемент документации для нашей функции, содержит всё, что к ней относится (т. е. следующие элементы)functions/leftpad/%title
(идёт от ./%title
): элемент, содержащий человекочитаемый заголовок для главного элементаfunctions/leftpad/str
, functions/leftpad/len
, functions/leftpad/padding
: элементы, документирующие аргументы функции; заметьте, что каждый из них помечен тегом модели %arg
functions/leftpad/%return
: элемент, документирующий возвращаемое значение нашей функции%arg
: тег модели с вот таким именем, введён в модель в силу того, что мы его использовали%arg
,
тот факт, что документируемая сущность - именно функция) сугубо конвенциональны, как и их имена. С точки зрения модели документации Logipard, они все - просто элементы общего вида, и на данном этапе их интерпретация
в тех качествах, которые описаны выше, пока что существует исключительно умозрительно.%return
рядом с оператором возврата, а не в одном блоке с описаниями параметров -
опять-таки, исключительно для примера, можно было разместить его и более традиционно.) Но в общем случае их можно разместить в любом месте файла с исходным кодом, или в другом файле, или даже разместить различные
подэлементы в совершенно разных местах. Это, возможно, не очень полезно для вещей вроде аргументов функции и её возвращаемого значения, но информация, имеющая отношение к функции и желательная к размещению
в её информационном узле, может не ограничиваться только этими данными.%
. По удачному стечению обстоятельств, мы уже
так и сделали.domain.our-unique.leftpad.for-nodejs/functions
domain.our-unique.leftpad.for-nodejs/functions/leftpad
domain.our-unique.leftpad.for-nodejs/functions/leftpad/%title
domain.our-unique.leftpad.for-nodejs/functions/leftpad/str
domain.our-unique.leftpad.for-nodejs/functions/leftpad/len
domain.our-unique.leftpad.for-nodejs/functions/leftpad/padding
domain.our-unique.leftpad.for-nodejs/functions/leftpad/%return
%arg
(это, по определённым причинам, можно оставить как есть)<PROJECT_ROOT>/index.js
//#LP-alias M: domain.our-unique.leftpad.for-nodejs
// теперь вместо domain.our-unique.leftpad.for-nodejs/functions/... можно использовать M/functions
//#LP M/functions/leftpad { <#./%title: leftpad(str, len[, padding])#>
...
// ...остальная часть файла может быть оставлена без изменений, в ней используются относительные имена
//#LP-alias M: domain.our-unique.leftpad.for-nodejs
, скорее всего, будет общей частью для всех файлов, содержащих документацию по нашему проекту, и к этой общей части, возможно,
со временем добавятся ещё некоторые. В целях дальнейшего уменьшения дубликации, можно вынести данный фрагмент в отдельный файл и просто подключать его везде, где нужно...<PROJECT_ROOT>/leftpad-module-inc.lp-txt
#LP-alias M: domain.our-unique.leftpad.for-nodejs
<PROJECT_ROOT>/index.js
//#LP-include leftpad-module-inc.lp-txt
//#LP M/functions/leftpad { <#./%title: leftpad(str, len[, padding])#>
...
// ...остальная часть файла всё ещё остаётся без изменений
<PROJECT_ROOT>/leftpad-doc-html.tpl.html
<html>
<head>
<style>
body {
font-family: sans-serif;
}
code {
font-family: monospace;
background: lightgray;
margin: 0;
padding: 0;
}
pre {
font-family: monospace;
background: lightgray;
margin: 0;
padding: 0;
}
table, th, td { border: 1px solid; border-spacing: 0; border-collapse: collapse; }
table { margin: 0.5em }
CSS_TARGET
</style>
</head>
<body>
<div style="display: flex; flex-direction: column; height: 100%; overflow: hidden">
<div style="border-bottom: double 3px">
<center style="font-size: 200%">leftpad for node.js</center>
<center style="font-size: 75%">1.0.0</center>
</div>
<div style="padding: 0; margin: 0; overflow: clip; height: 0; flex-grow: 1">
HTML_TARGET
</div>
</div>
</body>
</html>
CSS_TARGET
и HTML_TARGET
.<PROJECT_ROOT>/lp-config.json
//#charset utf-8
// (Этот комментарий выше будет принят во внимание; предполагается, что вы сохраняете данный файл в UTF-8)
// Заметим, что это не очень похоже на корректный JSON (взять хотя бы комментарии), но пока не обращайте на это внимание,
// просто скопируйте и перенесите в файл всё как есть.
{
"+ config": {
},
"lp-extract": {
"+ config": {
// обратите внимание, что неабсолютные пути - относительно корня проекта (то есть, места, в котором вы сохраняете данный файл)
"+ excludeInFiles": ["node_modules/**"]
},
"items": [
{
// секция для основной кодовой базы, в нашем случае это все JS-файлы
"inFiles": ["**/*.js"],
"excludeInFiles": [],
"outDir": "lp-extract.gen",
"reader": "${LP_HOME}/lpxread-basic" $, // символ $ в конце - не опечатка!
"lpxread-basic": {
"srcType": "generic-c-like"
}
},
{
// помните leftpad-module-inc.lp-txt? он попадает под эту секцию
"inFiles": ["**/*-inc.lp-txt"],
"excludeInFiles": [],
"forLPInclude": true,
"outDir": "lp-extract.gen/lp-includes",
"reader": "${LP_HOME}/lpxread-basic" $,
"lpxread-basic": {
"srcType": "lp-text"
}
}
]
},
"lp-compile": {
"+ config": {
},
"items": [
{
"inRootDir": "lp-extract.gen",
"lpIncLookupDirName": "lp-includes",
"writer": "${LP_HOME}/lpcwrite-basic-json" $,
"lpcwrite-basic-json": {
// можете настроить это под имя вашего проекта
"outFile": "lp-compile.gen/leftpad-doc-fdom.json"
}
}
]
},
"lp-generate": {
"+ config": {
},
"items": [
{
"inFile": "lp-compile.gen/leftpad-doc-fdom.json", // здесь то же, что в outFile в секции lp-compile
"writer": "${LP_HOME}/lpgwrite-example" $,
"lpgwrite-example": {
// тут очень много магии, просто скопируйте весь этот фрагмент
"program": file("${LP_HOME}/lpgwrite-example-docprg.lpson" $, {
"docprgPrologue": [ { "nameAlias": "M", "name": "domain.our-unique.leftpad.for-nodejs" } ],
"docRootItems": {
"query": [{ "with": "M/functions" }],
"sort": { "byMember": "%order", "keyFormat": "ds-natural", "order": "asc" }
},
"LS_EXTENDS": "Extends (is a)",
"LS_MEMBERS": "Members",
"LS_NAME": "Name",
"LS_DESCRIPTION": "Description",
"LS_MEMBERS_FROM_EXTENTS": "Members from extents",
"LS_ARGUMENTS": "Arguments",
"LS_RETURNS": "Returns:",
"LS_ERRORS": "Errors:",
"LS_MEMBERS_DETAILED": "Members (detailed)",
"LS_MEMBERS_FROM_EXTENTS_DETAILED": "Members from extents (detailed)",
"LS_ARGUMENTS_DETAILED": "Arguments (detailed)",
"LS_NOTES": "Notes",
"LS_PROPERTIES": "Properties",
"LS_PROPERTIES_FROM_EXTENTS": "Properties from extents",
"LS_METHODS": "Methods",
"LS_METHODS_FROM_EXTENTS": "Methods from extents"
}),
"renders": [
{
"docModel": "DocMain",
"renderer": "${LP_HOME}/lpgwrite-example-render-html" $,
"lpgwrite-example-render-html": {
"outFile": "lp-generate.gen/leftpad-doc.html",
"emitToc": true,
"inTemplateFile": "leftpad-doc-html.tpl.html",
"htmlPlaceholder": "HTML_TARGET",
"cssPlaceholder": "CSS_TARGET",
"localizedKeywords": {
"SNAPBACK": "Snapback",
"SNAPBACK_AND_SCROLL": "Snapback & Scroll",
"ELEVATE": "Elevate",
"RESET": "Reset",
"ELEVATE_TO": "Elevate to...",
"COPY_ITEM_NAME": "Copy this item's LP FDOM full name to clipboard:",
"ITEM_UNFOLDED_ELSEWHERE": "Item unfolded elsewhere on page, click/tap to unfold here...",
"MORE": "More... >>",
"TABLE_OF_CONTENTS": "Table of contents"
}
}
}
]
}
}
]
}
}
<PROJECT_ROOT>
, и вы работаете под пользователем, имеющим право на запись в него, вызовите CLI:lp-cli lp-config.json
=== Performing stage: EXTRACT ===
EXTRACT: 15.039ms
=== Performing stage: COMPILE ===
COMPILE: 18.592ms
=== Performing stage: GENERATE ===
Start render
lpgwrite-example-render-html: file lp-generate.gen/leftpad-doc.html created
GENERATE: 69.098ms
lp-generate.gen
, в котором должен находиться файл leftpad-doc.html
. Это и есть наша страница с документацией, можно открыть её в браузере.
Неплохо для начала? Заметим ещё кое-что: это полностью самодостаточный HTML-файл, можно перемещать его как угодно, не боясь потерять зависимости, и ещё он статический и удобный для индексации поисковиками
при размещении в интернете.lp-compile.gen
и файл leftpad-doc-fdom.json
в нём. Это база данных модели вашей документации (в данном случае, в JSON-формате).<PROJECT_ROOT>/readme.lp-txt
#LP-include leftpad-module-inc.lp-txt
#-LP обратите внимание на разный синтаксис, когда мы используем чисто-текстово-образные файлы
# кстати, фрагменты, начинающиеся со строки #-LP, считаются не аннотационными комментариями, и Logipard
# не включает их в БД и документмацию, они длятся до следующего разметочного тега #LP.
# То есть, вот этот параграф - всё ещё комментарий LP (решётки в начале на самом деле не нужны, но лучше соблюдать
# единообразие визуального стиля). Также обратите внимание, что согласованные отступы (т. е. одинаковое количество одного типа пробелов
# в начале каждой промежуточной линии в пределах #LP-фрагмента) учитываются и корректно обрабатываются.
#LP M/readme { <#./%title: leftpad: quickstart#>
String left pad, revised and improved.
#LP ./install { <#./%title: Install#> <#./%order: 1#>
```
$ npm install git+https://<whereismygit.com>/leftpad
# TODO: actual location
```
#LP }
#LP ./usage { <#./%title: Usage#> <#./%order: 2#>
```
const { leftpad } = require('./leftpad');
leftpad('foo', 5);
// ' foo'
leftpad('foo', 2);
// 'foo'
leftpad('foo', 10, '+-=');
// '+-=+-=+foo'
```
#LP }
#-LP Кстати, не помешает добавить ссылку на инструкцию по использованию в раздел документируемой функции...
#LP M/functions/leftpad/usage { <#./%title: Usage#>
See some usage examples under <#ref readme/usage#>.
#LP }
#-LP И ещё добавим в секцию списка функций немного официального содержимого...
#LP M/functions: <#./%title: Functions reference#>
Reference on the library functions.
#-LP Тем не менее, имейте в виду один подводный камень при использовании многострочного #LP...: синтаксиса: рамки такого фрагмента ограничены следующим не-<#...#> #LP тегом или #-LP комментарием
# Поэтому строки после данного комментария снова находятся в M/readme (а сам комментарий, соответственно, заканчивается только на теге #LP)
#LP ./versions { <#./%title: Versions summary#> <#./%order: 3#>
#LP ./1.0.0: Initial release version.
#LP ./0.0.9: Pre-release version.
Was not documented with LP, so it pretty sucked.
#LP }
#LP }
<PROJECT_ROOT>/lp-config.json
...
"lp-extract": {
... // под "items"...
"items": [
// добавьте третий элемент (для учёта нового readme.lp-txt):
...,
{
"inFiles": ["**/*.lp-txt"],
"excludeInFiles": [],
"outDir": "lp-extract.gen",
"reader": "${LP_HOME}/lpxread-basic" $,
"lpxread-basic": {
"srcType": "lp-text"
}
}
]
},
...
"lp-generate": {
... // первый (и пока единственный) элемент под "items"...
"items": [
{
"inFile": "lp-compile.gen/leftpad-doc-fdom.json",
"writer": "${LP_HOME}/lpgwrite-example" $,
"lpgwrite-example": {
... // в основном оставьте, как есть, кроме....
"docRootItems": {
"query": [{ "with": ["M/readme", "M/functions"] }], // <-- замените содержимое члена "query" на вот такое
],
... // всё остальное остаётся как есть
},
... // и здесь тоже
},
// далее, добавьте вот такой второй элемент в "items":
{
"inFile": "lp-compile.gen/leftpad-doc-fdom.json", // обратите внимание - всё ещё то же самое, что outFile в секции lp-compile
"writer": "${LP_HOME}/lpgwrite-example" $,
"lpgwrite-example": {
"program": file("${LP_HOME}/lpgwrite-example-docprg.lpson" $, {
"docprgPrologue": [ { "nameAlias": "M", "name": "domain.our-unique.leftpad.for-nodejs" } ],
"docRootItems": {
"query": [{ "with": ["M/readme"] }],
],
"sort": { "byMember": "%order", "keyFormat": "natural", "order": "asc" }
}
}),
"renders": [
{
"docModel": "DocMain",
"renderer": "${LP_HOME}/lpgwrite-example-render-md" $,
"lpgwrite-example-render-md": {
"outFile": "lp-generate.gen/leftpad-README.md",
"emitToc": true,
"addSourceRef": false,
"header": "# leftpad #\n\n---",
}
}
]
}
}
// всё остальное остаётся как есть
}
...
lp-cli lp-config.json
lp-generate.gen
: должен появиться новый файл leftpad-README.md
(посмотрите его каким-нибудь подручным MD-просмотрщиком), а файл leftpad-doc.html
обновился -
теперь в нём есть та же информация, что и в readme, при этом осталась справка по функциям, и заметны прочие улучшения, о которых можно было догадаться при редактировании исходника readme./
). Так, например, на картинке из нашего примера узел с кратким именем subNodeA-3-1
имеет полное имя nodeA/subNodeA-3/subNodeA-3-1
, а узел
с кратким именем %tag3
имеет полное имя moreTags/%tag3
./
, с одним дополнением: последовательность
между одиночными или двойными кавычками считается одним символом, и может включать символы, обычно не разрешённые. Например, spare/can
- не корректное краткое имя
(хотя является корректным полным именем), а space time
- вообще не корректное имя, но "spare/can"
и 'space time'
- оба корректные краткие имена. Или ещё, например,
A/"B/C"/D
- трёхкомпонентное полное имя, состоящее из кратких имён A
, "B/C"
, D
. Кавычки должны иметь парные закрывающие, кроме случаев, когда они находятся внутри
другого типа кавычек."shortName"
- не то же самое имя,
что shortName
, и не то же самое, что "shortName "
).#
(например, #include
, ##mp
). Это включает и одиночный #
. Такие имена зарезервированы в качестве приватных генерируемых. Они могут
появляться в скомпилированной модели, но только не в качестве кратких имён, явно заданных пользователем.:
, {
, }
(например, a::b
, {ARG}
). :
, {
и }
зарезервированы в качестве ограничителей для входного синтаксиса..
, ..
, ...
, и т. д.). Они зарезервированы для обозначения текущего/предыдущего/пред-предыдущего/и т. д. уровней имени-пути
и не могут быть собственными именами узлов.~
(одиночная тильда) - каждое употребление этого краткого имени преобразуется в уникальное приватное сгенерированное краткое имя, и не может быть
собственным именем узла."#name"
, "."
, "~"
, '{ARG}'
, 'a::b'
- корректные краткие имена).%title
может указывать
заголовок для родительского узла. Рекомендуется начинать такие краткие имена с символа '%'
, чтобы подчеркнуть их особую роль и упростить их фильтрацию от "обычных" подузлов.node #tag1 #tag2 ...
%function
, %arg
, и т. п.), каждый из которых также является FDOM узлом. Такой подход,
помимо уменьшения числа базовых сущностей в моделе, позволяет тегам самим быть помеченными и иметь подузлы метаданных, что позволяет создавать достаточно сложные терминологии.%extends
и пометить его всеми узлами, описывающими базовые классы.<запрос1> / <запрос2> / ... / <запросN>
.[node-fn-1, node-fn-2, ...]
Т. к. коллекция может быть пустой, но не может содержать нулевых узлов, все нулевые узлы, возможно перечисленные в этом списке,
по факту исключаются из коллекции (они не попадают в перечисление и не учитываются никаким образом, в том числе при подсчёте размера коллекции).[node-fn-1, node-fn-2, <coll-spec-1>, node-fn-3...]
Расширенный вариант списка узлов. Элемненты спецификации коллекции воспринимаются как элементы списка,
полученные распаковкой соответствующей коллекции; узлы-дубликаты игнорируются (узел включается в коллекцию не более одного раза).<coll-spec-1> + <coll-spec-2> + ...
Коллекция, которая включает каждый узел из каждой указанной коллекции.<coll-spec-1> & <coll-spec-2> & ...
Коллекция, которая включает только узлы, имеющиеся одновременно во всех указанных коллекциях.<coll-spec-1> - <coll-spec-2> - ...
Коллекция, которая включает только узлы, которые находятся в первой коллекции списка, но не во второй или в каких-либо последующих коллекциях из списка.SomeCollAlias
Алиас - "именованная закладка" для коллекции в контексте запроса, чтобы можно было запомнить её и сослаться на неё позже. Алиас, по конвенции, обозначается корректным кратким именем FDOM.BasicNodes + [advanced/%node1, advanced/%node3, MoreAdvancedNodes] + ((EvenMoreNodesA & EvenMoreNodesB) - [%node4])
.true
- данное условие всегда соблюдается, false
- данное условие всегда не соблюдается.isAnyOf <спецификация-коллекции>
: условие соблюдается, если узел - один из заданной коллекции.hasMembersThat <подусловие>
: условие соблюдается, если под-условие выполняется по крайней мере для одного из ненулевых членов узла.hasMembersNamed <регулярное выражение>
: условие соблюдается, если узел имеет по крайней мере один ненулевой член с краткими именем, подходящим под заданное регулярное выражение. Это сокращённый вариант
для hasMembersThat (named <регулярное выражение>)
(см. ниже), могущий иметь более оптимальную реализацию.hasAnyOfTags <спецификация-коллекции>
: условие соблюдается, если узел имеет по крайней мере один тег из заданной коллекции узлов-тегов.hasAllOfTags <спецификация-коллекции>
: условие соблюдается, если узел имеет все теги из заданной коллекции узлов-тегов.hasParentThat <подусловие>
: условие соблюдается, если подусловие соблюдается для непосредственного родителя узла.named <регулярное выражение>
: условие соблюдается, если краткое имя узла подходит под заданное регулярное выражение.and <список подусловий>
: условие соблюдается, если соблюдаются все подусловия из заданного списка.or <список подусловий>
: условие соблюдается, если соблюдается по крайней мере одно подусловие из заданного списка.not <подусловие>
: условие соблюдается, если подусловие не соблюдается, и не соблюдается, если подусловие соблюдается.SomeConditionAlias
: условие может быть помечено алиасом-закладкой (по конвенции обозначаемым корректным кратким именем FDOM), который может быть использован далее в пределах данного запроса или других
запросов в данном контексте.alias ShortName
- задать локальный алиас для текущей коллекции (он действует только до завершения текущего запроса). Не изменяет текущую коллекцию, просто задаёт для неё алиас, который может быть использован в последующих
частях запроса.membersThat <спецификация-условия> [on <спецификация-коллекции>] [recursive]
- собрать узлы-члены из каждого элемента текущей коллекции, такие, что каждый член удовлетворяет заданному условию (
✖ Задание условий
),
и заменить текущую коллекцию этим набором узлов. Вместо текущей коллекции можно задать поиск по явно заданной. Данный запрос может быть рекурсивным (
✖ Рекурсивные запросы
).node
node/memberA
node/memberB
node/memberC
node
membersThat (not (named /^.*A/))
даёт следующую коллекцию:node/memberB
node/memberC
itemsThat <спецификация-условия> [on <спецификация-коллекции>] [recursive]
- отобрать из текущей коллекции элементы, удовлетворяющие заданному условию (
✖ Задание условий
), и заменить текущую коллекцию
набором этих узлов. Вместо текущей коллекции можно задать поиск по явно заданной. Данный запрос может быть рекурсивным (
✖ Рекурсивные запросы
).node1/memberA
node2/memberB
node3/memberC
itemsThat (not (named /^.*A/))
даёт следующую коллекцию:node2/memberB
node3/memberC
tagsThat <спецификация-коллекции> [on <спецификация-коллекции>] [recursive]
- собрать узлы-теги с каждого элемента коллекции, такие, что каждый тег удовлетворяет заданному условию (
✖ Задание условий
), и заменить текущую
коллекцию набором этих узлов. Вместо текущей коллекции можно задать поиск по явно заданной. Данный запрос может быть рекурсивным (
✖ Рекурсивные запросы
).
Хотя этот запрос можно использовать на любой коллекции, типовое использование для него - на одиночном элементе (коллекции из одного элемента).inMembersThat <спецификация-условия> [on <спецификация-коллекции>] [recursive] query <список-базовых-запросов>
- собрать узлы-члены от каждого элемента текущей коллекции, такие, что каждый член удовлетворяет заданному условию
(
✖ Задание условий
), и заменить текущую коллекцию результатом заданного списка запросов на коллекции эти узлов (членов исходных). Вместо текущей коллекции можно задать начало
поиска по подзапросам на явно заданной. Данный запрос может быть рекурсивным (
✖ Рекурсивные запросы
).node
node/memberA
node/memberB
node/memberB/%data
node/memberC
node/other/%data
node
inMembersThat (named /%^member/) query (hasMembersNamed /^%data$/)
выдаёт следующую коллекцию:node/memberB/%data
node/other
имеет член %data
, но он не проходит по условию в inMembersThat
, поэтому не попадает в коллекцию для поиска по подзапросу.)inTagsThat <спецификация-условия> [on <спецификация-коллекции>] [recursive] query <список-базовых-запросов>
- собрать узлы-теги с каждого элемента текущей коллекции, такие, что каждый тег удовлетворяет заданному условию
(
✖ Задание условий
), и заменить текущую коллекцию результатом заданного списка запросов на коллекции этих узлов (тегов от исходных). Вместо текущей коллекции можно задать начало
поиска по подзапросам на явно заданной. Данный запрос может быть рекурсивным (
✖ Рекурсивные запросы
).%tag1
%tag2
%tag2/%isLangObject
%tag2/%subInfo
%tag3
%tag3/%subInfo
node1 #%tag1
node2 #%tag1 #%tag2
node3 #%tag3 #%tag2
node4 #%tag4
inTagsThat (hasMembersNamed /^%isLangObject$/) query membersThat (named /^%subInfo$/)
.node1
node2
node3
%tag2/%subInfo
node1
node4
inItemsThat <спецификация-условия> [on <спецификация-коллекции>] [recursive] query <список-базовых-запросов>
- взять каждый элемент текущей коллекции, удовлетворяющий заданному условию (
✖ Задание условий
), и заменить
текущую коллекцию результатом заданного списка запросов на коллекции этих узлов (элементов исходной коллекции). Вместо текущей коллекции можно задать начало поиска по подзапросам на явно заданной.
Данный запрос может быть рекурсивным (
✖ Рекурсивные запросы
).node1
node1/member1
node1/member2
node1/member2/%flagged
node2
node2/member2
node2/member2/%flagged
node3/member1
node3/member1/%flagged
inItemsThat (hasMembersNamed /^member1$/) query membersThat (haveMembersNamed /^%flagged$/)
. Выполненный на коллекции:node1
node2
node3
node1/member2
node3/member1
subtractQuery [on <спецификация-коллекции>] <спикок-базовых-запросов>
- выполнить заданный список запросов и вычесть результат из текущей коллекции. Коллекция, на которой выполняется подзапрос - по умолчанию,
исходная коллекция для самого subtractQuery
(т. е. текущая), но можно указать вместо неё явно заданную.node1
node2
node2/%flag
node3
subtractQuery itemsThat (haveMembersNamed /^%flag$/)
. Выполненный на коллекции:node1
node2
node3
node1
node3
unionQuery [on <спецификация-коллекции>] <список-базовых-запросов>
- выполнить заданный список запросов и объединить результат с текущей коллекцией. Коллекция, на которой выполняется подзапрос - по умолчанию,
исходная коллекция для самого unionQuery
(т. е. текущая), но можно указать вместо неё явно заданную.node1
node2
node2/member
node3
unionQuery membersThat (true)
. Выполненный на коллекции:node1
node2
node3
node1
node2
node2/member
node3
intersectQuery [on <спецификация-коллекции>] <список-базовых-запросов>
- выполнить заданный список запросов и взять пересечение результата с текущей коллекцией. Коллекция, на которой выполняется подзапрос - по умолчанию,
исходная коллекция для самого intersectQuery
(т. е. текущая), но можно указать вместо неё явно заданную.node1
node2
node2/%flag
node3
intersectQuery itemsThat (haveMembersNamed /^%flag$/)
. Выполненный на коллекции:node1
node2
node3
node2
sideQuery [on <спецификация-коллекции>] <список-базовых-запросов>
- выполнить заданный список запросов, но оставить текущую коллекцию без изменений. Этот запрос имеет смысл только в случае, если список заканчивается заданием локального алиаса.
Коллекция, на которой выполняется подзапрос - по умолчанию, исходная коллекция для самого sideQuery
(т. е. текущая), но можно указать вместо неё явно заданную.tagsList #tag1 #tag2
node1 #tag1
node2 #tag2
node3 #tag3
tagsList
" будет такой: (sideQuery on [tagsList] allTagsThat (true) / alias TAGS) / allItemsThat (hasAnyOfTags TAGS)
.node1
node2
node3
node1
node2
classA
classB
classB/%extends #classA
classC
classD
classD/%extends #classB #classC
classD
inMembersThat (named /^%extends$/) allTagsThat (true)
, но такой запрос вернёт только узлы для
"непосредственно" расширяемых классов:classB
classC
inMembersThat (named /^%extends$/) recursive allTagsThat (true)
. Тогда мы получим ожидаемое:classA
(рекурсивно запрошенный через classB
)classB
classC
lp-cli --extract <файл-конфигурации>
lp-cli --compile <файл-конфигурации>
lp-cli --generate <файл-конфигурации>
lp-cli --compile --generate <файл-конфигурации>
//
), shell-подобных (#
), lua и SQL-подобных (--
) языках порграммирования, и, в качестве специального "языка программирования", чистый текст. Также служит примером реализации
считывателя стадии извлечения.
Этот считыватель задействуется в элементе задания стадии извлечения через задание параметра writer: "${LP_HOME}/lpxread-basic" $
в (
✖ reader
).#LP
или -#LP
, считается фрагментом входных данных для LP, относящихся к одиночному #LP-тегу разметки (фрагменты
-#LP игнорируются), например:code code // не LP комментарий
code //#LP: LP тег
code code code // продолжение LP тега
code //#LP: другой LP тег
code code code // продолжение другого LP тега
code
code // снова не LP комментарий comment (строка без комментария завершает непрерывность)
code code
code code code // ещё раз не LP comment
code //#LP: третий LP тег <#LP: теги, полностью записанные внутри строки, включая отступления, тоже допускаются#> в том числе
code code code //-#LP закомментированный LP тег
code //#LP: 4й LP тег
<#LP: LP тег
продолжение LP тега#>
<#LP: другой LP тег
продолжение другого LP тега#>
<#LP: третий LP тег <#LP: теги, полностью записанные внутри строки, включая отступления, тоже допускаются#> в том числе#>
<#LP: 4й LP тег#>
//#charset utf-8
(не более одного раза на файл, слово "charset" должно быть в нижнем регистре).
Что именно будет считаться за однострочный комментарий, зависит от типа исходника, заданного для этого элемента задания извлечения (см.
✖ Конфигурация lpxread-basic (элемент задания lp-extract)
).#-LP
(или #LP-
) комментарем, чтобы отметить окончание LP-тега, начатого по #LP tag: ...
.
Дополнлительно, в достаточно специфическом случае, когда у вас есть фрагмент кода, включающего разметку LP, следует разместить её междё строками-ограничителями #LP~delimiter~
,
чтобы предотвратить нарушение в формате извлекаемых данных.
Например:#LP~x~
```
#LP ./example: пример кода, содержащего дословную разметку LP <#~~ и неэкранированный дословный фрагмент ~~#>
```
#LP~x~
#LP~...~
будет выдано в извлечённый входной поток в точности дословно, хотя корректность фрагмента в целом как разметки LP по-прежнему требуется. Поэтому используйте данный способ
только для выделения фрагментов кода, и с осторожностью.lpxread-basic
с конфигурацией, специфичной для lpxread-basic, к элементу задания стадии извлечения, использующему lpxread-basic. Подчлены данного объекта описаны далее...{
"inFiles": ["**/*.js"],
"excludeInFiles": [],
"outDir": "...",
"reader": "logipard/lpxread-basic",
"lpxread-basic": {
"srcType": "generic-c-like"
}
}
inFiles
...generic-c-like
: C-подобные языки, разрешающие однострочные комментарии, начинающиеся с //
(семейство C family, семейство Java, JS, PHP, и т. д.)generic-sh-like
: языки, разрешающие однострочные комментарии, начинающиеся с #
(семейство sh, python, perl, PHP, и т. д.)lua-sql
: языки, разрешающие однострочные комментарии, начинающиеся с --
(наиболее известные - Lua и SQL)lp-text
: файл на чисто текстовой основе, однострочным комментарием считается каждая строкаwriter: "${LP_HOME}/lpgwrite-example" $
в
✖ writer
.writer: "${LP_HOME}/lpgwrite-i18n-assist" $
в
✖ writer
.{
// необязательно (обратите внимание, после '+' - ровно один пробел)
"+ config": {
...
},
// обязательно, конфигурация для стадии извлечения
"lp-extract": {
// необязательно
"+ config": {
...
},
// обязательно
"items": [
{
"SKIP": bool, // необязательно, не-false значение означает, что данный элемент закомментирован
"inRootDir": string-path, // необязательно, по уполчанию - корневой каталог проекта (см. ниже)
"inFiles": [ ...strings-file-globs ], // обязательно, должно быть относительно каталога, заданного в inRootDir
"excludeInFiles": [ ...strings-file-globs ], // необязательно, должно быть относительно каталога, заданного в inRootDir
"forLPInclude": bool, // необязательно, по умолчанию false
"outDir": string-path, // обязательно
"reader": string-path, // обязательно
... // все прочие параметры не распознаются непосредственно в Logipard и являются дополнительными, считывателям здесь могут потребоваться их собственные параметры
},
... // ноль, один или более элементов
]
},
// обязательно, конфигурация для стадии компиляции
"lp-compile": {
// необязательно
"+ config": {
...
},
// обязательно
"items": [
{
"SKIP": bool, // необязательно, не-false значение означает, что данный элемент закомментирован
"inRootDir": string-path, // обязательно
"outFile": string-path,
"writer": string-path,
},
... // ноль, один или более элементов
]
},
// обязательно, конфигурация для стадии генерации
"lp-generate": {
// необязательно
"+ config": {
},
// обязательно
"items": [
{
"SKIP": bool, // необязательно, не-false значение означает, что данный элемент закомментирован
"inFile": string-path,
"writer": string-path,
...
},
... // ноль, один или более элементов
]
}
}
"items"
, но в него также дополнительно добавляется содержимое объекта "+ config"
из соответствующей инструменту стадии, и дополнительно добавляется содержимое "+ config"
из корневого уровня (в порядке глобальный "+ config"
-> "+ config"
стадии -> элемент). Добавление
объектов делается путём неглубокого почленного слияния. В случае совпадения имён, член более следующего по порядку объекта заменяет член более предыдущего. Однако, возможно модифицировать это поведение
для элемента-массива или объекта: если ожидаемое имя члена - "id"
, то добавьте член с именем "+ id"
в "+ config"
- в этом случае, член "id" полученного элемента будет содержать подчлены/элементы
от "+ id"
из исходного(ых) "+ config"
, добавленные перед теми, которые укзаны в "id"
самого элемента.{
"+ config": {
...
"a": [1, 2],
"+ b": [3, 4],
"c": [5, 6],
"+ d": [7, 8],
...
},
"lp-...": {
"+ config": {
...
"a": [9, 10],
"b": [11, 12],
"+ c": [13, 14],
"+ d": [15, 16],
...
},
"items": [
...
{
// "a", "b", "c", "d" не заданы, в результирующем элемене конфигурации они будут:
// "a": [9, 10]
// "b": [3, 4, 11, 12]
// "c": [13, 14]
// "d": [7, 8, 15, 16]
},
{
...
"a": ["A", "B"], // в результате будет "a": ["A", "B"]
"b": ["A", "B"], // в результате будет "b": ["A", "B"]
"c": ["A", "B"], // в результате будет "c": [13, 14, "A", "B"]
"d": ["A", "B"], // в результате будет "d": [7, 8, 15, 16, "A", "B"]
...
}
]
}
}
Имя
| Описание
|
---|---|
✖ "+ config"
|
Параметры конфигурации, общие для всех элементов заданий во всех стадиях. Добавляются к каждому конкретному элементу конфигурации перед содержимым самого элемента и содержимым
"+ config" конкретной стадии. |
✖ lp-extract
|
Параметры конфигурации для элементов заданий на стадии извлечения.
|
✖ lp-compile
|
Параметры конфигурации для элементов задания на стадии компиляции.
|
✖ lp-generate
|
Параметры конфигурации для элементов задания на стадии генерации.
|
Имя
| Описание
|
---|---|
✖ "+ config"
|
Параметры конфигурации, общие для всех элементов заданий на стадии извлечения. Добавляется к каждому конкретному элементу конфигурации перед содержимым самого элемента, и после содержимого глобального
✖ "+ config"
.
|
✖ items[]
|
Массив конфигураций, задающих каждый элемент задания на стадии извлечения.
|
Имя
| Описание
|
---|---|
✖ SKIP
|
Булевой. Не-false значение сообщает Logipard, что этот элемент нужно пропустить. Используйте, чтобы "закомментировать" временно отключаемые элементы. Необязательно, по умолчанию false.
|
✖ inRootDir
|
Корневой каталог для поиска входных файлов, по умолчанию - корень проекта.
|
✖ inFiles
|
Строка, или массив строк, с glob-масками для имён файлов - задаёт множество файлов-исходников, попадающих под данный элемент задания.
Пути в масках относительны
✖ inRootDir
.
|
✖ excludeInFiles
|
Строка, или массив строк, с glob-масками для имён файлов - задаёт множество файлов для исключения из списка, попавшего под
✖ inFiles
.
Пути в масках относительны
✖ inRootDir
. Необязательный параметр.
|
✖ outDir
|
Строка, путь к каталогу, где будет размещены файлы во входном формате Logipard, извлечённые из файлов-исходников. Каталог с результатом извлечения предполагается временным по назначению, и должен быть добавлен
в игнор-список системы контроля версий. Обратите внимание, что один и тот же файл-исходник может быть обработан несколькими элементами задания стадии извлечения, но, если в разных элементах задний указан один и
тот же
outDir для извлечённых файлов, то более поздние задания могут перезаписать свой результат поверх результата более ранних - следует принимать это во внимание и заранее позаботиться, чтобы их выходные
пути не конфликтовали. |
✖ forLPInclude
|
Булевой, если true, то результаты извлечения из исходников данным элементом задания будут сохранены как файлы модулей, доступные для включения при помощи
LP-inc /LP-include
(см.
✖ Подключение файлов модулей
). Необязательно, по умолчанию false. |
✖ reader
|
Строка. Путь к JS-файлу считывателя стадии извлечения, относительно корня проекта (или абсолютный).
Считыватель стадии извлечения должен соответствовать
✖ Интерфейс считывателя стадии извлечения
. Logipard включает несколько встроенных считывателей стадии извлечения:
✖ Встроенные считыватели стадии извлечения
|
outDir
для извлечённых файлов, то более поздние задания могут перезаписать свой результат поверх результата более ранних - следует принимать это во внимание и заранее позаботиться, чтобы их выходные
пути не конфликтовали.LP-inc
/LP-include
(см.
✖ Подключение файлов модулей
). Необязательно, по умолчанию false.Имя
| Описание
|
---|---|
✖ "+ config"
|
Параметры конфигурации, общие для всех элементов заданий на стадии компиляции. Добавляется к каждому конкретному элементу конфигурации перед содержимым самого элемента, и после содержимого глобального
✖ "+ config"
.
|
✖ items[]
|
Массив конфигураций, задающих каждый элемент задания на стадии компиляции.
|
Имя
| Описание
|
---|---|
✖ SKIP
|
Булевой. Не-false значение сообщает Logipard, что этот элемент нужно пропустить. Используйте, чтобы "закомментировать" временно отключаемые элементы. Необязательно, по умолчанию false.
|
✖ inRootDir
|
Путь к корневому каталогу с входными файлами для модели, извлечёнными из исходников на стадии извлечения. В большинстве случаев, этот корень тот же, что был указан в
✖ outDir
.
|
✖ lpIncLookupDirName
|
Имя каталога для использования в каскадном поиске LP-inc/LP-include.
|
✖ writer
|
Строка. Путь к JS записывателя модели стадии компиляции.
Записыватель стадии компиляции должен соответствовать
✖ Интерфейс записывателя стадии компиляции
.
Logipard включает несколько встроенных записывателей стадии компиляции:
✖ Встроенные записыватели скомпилированной модели
|
<#LP-include filename#>
во входном файле LP, когда filename
не абсолютно и не явно локальное (т. е. не начинается с .
или ..
), файл ищется в расположении ./<value-of-lpIncLookupDirName>/filename
,
если не найден там - то ../<value-of-lpIncLookupDirName>/filename
, и так далее во всё более внешних каталогах (но не выше, чем inRootDir).Имя
| Описание
|
---|---|
✖ "+ config"
|
Параметры конфигурации, общие для всех элементов заданий на стадии генерации. Добавляется к каждому конкретному элементу конфигурации перед содержимым самого элемента, и после содержимого глобального
✖ "+ config"
.
|
✖ items[]
|
Массив конфигураций, задающих каждый элемент задания на стадии генерации.
|
Имя
| Описание
|
---|---|
✖ SKIP
|
Булевой. Не-false значение сообщает Logipard, что этот элемент нужно пропустить. Используйте, чтобы "закомментировать" временно отключаемые элементы. Необязательно, по умолчанию false.
|
✖ writer
|
Строка. Путь к JS записывателя стадии генерации (или просто "генератора").
Запиыватель стадии генерации должен соответствовать
✖ Интерфейс записывателя стадии генерации
.
Logipard включает несколько встроенных записывателей стадии генерации:
✖ Встроенные записыватели стадии генерации
|
reader
в элементе задания стадии извлечения (
✖ reader
).
Считыватель стадии извлечения должен быть реализован как модуль CommonJS со следующим интерфейсом...Имя
| Описание
|
---|---|
✖ async .parseInput({ buffer, itemConfig, filePath })
|
Распарсить контент файла, переданный как Buffer-объект Node.JS, и вернуть результат извлечения во входном формате LP, как одну слитную строку.
|
reader
через module.exports, например, так:exports.parseInput = async function parseInput({ buffer, itemConfig, filePath }) { ... }
Имя
| Описание
|
---|---|
✖ buffer
|
Buffer, входной файл, переданный в непреобразованной двоичной форме. Вопросы кодировки оставляются на усмотрение считывателя.
|
✖ itemConfig
|
словарь (как Object), фрагмент объекта конфигурации, относящиегося к данному элементу задания (
✖ items[]
).
Считыватель имеет доступ к любым членам объекта конфигурации для своего элемента задания, но хороший стиль - держать всю специфичную для него конигурацию в выделенном подобъекте,
размещённом в словаре под именем, указывающем на считывателя.
|
✖ filePath
|
строка, путь к файлу (не зависит от корня проекта, может самостоятельно применяться в
fs или path ).
Может быть полезно, если одного содержимого файла недостаточно для целей считывателя, и требуется некоторая дополнительная информация, связанная с данным файлом. |
Имя
| Описание
|
---|---|
✖ .lpNameRegexp(bounds,flags)
|
Получить RegExp для поиска или выделения строки, котрая подходит как имя элемента модели LP. В слуаче успешного сопоставления,
[0] match-объекта - строка с именем. |
✖ .parseName(nameString)
|
Распарсить строку с LP-именем в массив фрагментов.
|
✖ .currentScopeNodeName
|
Получить полное, без алиасов, имя текущего узла (как массив фрагментов имени). Только для чтения.
|
✖ .resolveParsedName(parsedName)
|
Получить полное FDOM-имя узла по массиву от распарсенного имени (полученному через
✖ .parseName(nameString)
). Полезно,
когда преполагается наличие в пользовательском теге FDOM-имени, которое процессор должен разрешить по таким же правилам, как оно бы разрешилось в
<#ref имя#> , использованном в этом же месте. |
✖ .items[]
|
Массив элементов, содержащихся в теге; каждый элемент - либо строка (контент), либо нестроковой объект (вложенный тег, который следует обработать с помощью
✖ async .processTag(tagItem)
).
|
✖ async .processTag(tagItem)
|
Обрботать (вложенный) элемент-тег так же, как его обработал бы LP, если бы встретил в строке штатным образом.
|
✖ .text
|
Контент тега как единая строка. Предполагается, что в контенте не имеется вложенных тегов, в противном случае это свойство будет null.
|
[0]
match-объекта - строка с именем.Имя
| Описание
|
---|---|
✖ bounds
|
Строка, необязательный параметр, по умолчанию
'' , может также быть '^' , '$' , '^$' . Задаёт граничные утверждения для включения в рег. выражение. Если содержит ^ ,
то в начало выражения добавляется ^ . Если содержит $ , то в конец выражения добавляется $ . |
✖ flags
|
Строка, необязательный параметр, по умолчанию
'g' . Список regexp-флагов для добавления на регулярное выражение. |
<#ref имя#>
, использованном в этом же месте.Имя
| Описание
|
---|---|
✖ parsedName
|
Массив, распарсенное имя, как его возвращает
✖ .parseName(nameString)
.
|
processTag
- записыватель всё равно не имеет достаточно осмысленного контекста, чтобы сделать с ним что-либо другое.Имя
| Описание
|
---|---|
✖ tagItem
|
Объект элемента-тега - значение, полученное из массива
✖ .items[]
.
|
writer
в элементе, описывающем задание компиляции (
✖ writer
).
Записыватель стадии компиляции должен быть реализован как модуль CommonJS, экспортирующий следующий интерфейс...Имя
| Описание
|
---|---|
✖
async .processCustomTag({ modelOutput, targetNodeName, tagName, toolkit, sourceFile })
|
Обработать пользовательский внутритекстовый тег в контенте указанного целевого узла. Интерпретация тега - на усмотрение записывателя: это может быть добавление в представление модели некого специфичного для этого
представления контента, или некие настройки в процесс вывода контента, и т. д.
|
✖ async .openModelOutput({ itemConfig, workDir })
|
Инициализировать хранилище скомпилированной модели, или открыть существующее для обновления.
|
✖ async .closeModelOutput({ modelOutput })
|
Завершить вывод модели и инвалидировать дескриптор. Записыватель будет открываться и закрываться ровно один раз за каждый элемент задания компиляции, назовём это сессией обновления модели.
|
✖ async .invalidateSourceFile({ modelOutput, sourceFile, newDependencies })
|
Инвалидировать заданный файл-источник. Весь контент и добавления тегов, заданные в этом файле (
✖ async .appendContent({ modelOutput, targetNodeName, content, sourceFile })
,
✖ async .tagTo({ modelOutput, tagNodeName, targetNodeName, sourceFile })
),
должны быть удалены из хранилища или перемещены в архив, т. к. предстоит их замена более новыми версиями. Обратите внимание, что файл-источник в данном случае, как и в других методах записывателя,
означает входной файл в формате LP созданный на стадии (
✖ Стадия извлечения
), а не тот исходный файл с аннотациями, который редактируется пользователем. ПРэтому
он будет иметь расширение
.lpinput и размещаться на пути, заданном в соответствующем задании извлечения в
✖ outDir
. |
✖ async .appendContent({ modelOutput, targetNodeName, content, sourceFile })
|
Дописать контент к заданному целевому узлу. Через этот метод добавляется только текстовый контент, для прочих компонентов контента предусмотрены другие методы.
|
✖ async .tagTo({ modelOutput, tagNodeName, targetNodeName, sourceFile })
|
Добавить узел-тег к целевому узлу в качестве тега. Равносильная формулировка - "пометить заданным узлом-тегом заданный целевой узел" (или "применить узел-тег к целевому узлу"). В любом варианте формулировки,
это означает, что узел
tagNodeName будет добавлен к списку тегов узла targetNodeName . |
✖
async .appendRef({ modelOutput, targetNodeName, refNodeName, refText, sourceFile })
|
Дописать к контенту целевого узла внутритекстовую ссылку.
|
writer
через module.exports - например, таким образом:exports.openModelOutput = async function openModelOutput({ itemConfig, workDir }) { ... }
exports.closeModelOutput = async function closeModelOutput({ modelOutput }) { ... }
exports.invalidateSourceFile = async function invalidateSourceFile({ modelOutput, sourceFile, newDependencies }) { ... }
exports.appendContent = async function appendContent({ modelOutput, targetNodeName, content, sourceFile }) { ... }
exports.tagTo = async function tagTo({ modelOutput, tagNodeName, targetNodeName, sourceFile }) { ... }
exports.appendRef = async function appendRef({ modelOutput, targetNodeName, refNodeName, refText, sourceFile }) { ... }
exports.processCustomTag = async function processCustomTag({ modelOutput, targetNodeName, tagName, toolkit, sourceFile }) { ... }
Имя
| Описание
|
---|---|
✖ modelOutput
|
Дескриптор вывода модели (тот, который был возвращён из
✖ async .openModelOutput({ itemConfig, workDir })
).
|
✖ targetNodeName
|
Строка, полное FDOM-имя целевого узла, в котором встретился пользовательский тег.
|
✖ tagName
| Строка, имя пользовательского тега.
|
✖ toolkit
|
Объект, набор вспомогательных функций, предоставляемых для обрабоки пользовательских тегов. См. (
✖
Инструментарий записывателя стадии компиляции для процессора пользовательских тегов
).
|
✖ sourceFile
|
Строка, путь к исходном файлу, из которого исходит команда с данным пользовательским тегом.
|
Имя
| Описание
|
---|---|
✖ itemConfig
|
Словарь (как Object), фрагмент объекта конфигурации, относящийся к данному элементу задания (
✖ items[]
).
Записыватель имеет доступ к любым членам объекта конфигурации для своего элемента задания, но хороший стиль - держать всю специфичную для него конигурацию в выделенном подобъекте,
размещённом в словаре под именем, указывающем на записывателя.
|
✖ workDir
|
Строка, путь к корневому каталогу проекта, в виде, готовом для непосредственного использования в
fs или path . Полезно, когда конфигурация записывателя содержит какие-либо пути до файлов/каталогов,
которые должны быть указаны относительно корневого каталога проекта. |
Имя
| Описание
|
---|---|
✖ modelOutput
|
Дескриптор вывода модели (тот, который был возвращён из
✖ async .openModelOutput({ itemConfig, workDir })
). После данного вызова полагается более не валидным.
|
.lpinput
и размещаться на пути, заданном в соответствующем задании извлечения в
✖ outDir
.Имя
| Описание
|
---|---|
✖ modelOutput
|
Дескриптор вывода модели (тот, который был возвращён из
✖ async .openModelOutput({ itemConfig, workDir })
).
|
✖ sourceFile
|
строка, путь к инвалидируемому файлу-источнику, относительно пути, указанного для задания компиляции в
✖ inRootDir
.
|
Имя
| Описание
|
---|---|
✖ modelOutput
|
Дескриптор вывода модели (тот, который был возвращён из
✖ async .openModelOutput({ itemConfig, workDir })
).
|
✖ targetNodeName
|
строка, полное FDOM-имя целевого узла, к которому дописывается контент.
|
✖ content
| строка, контент, добавляемый в целевой узел.
|
✖ sourceFile
|
строка, путь к файлу-источнику, из которого происходит данный контент, относительно пути, указанного для задания компиляции в
✖ inRootDir
.
|
tagNodeName
будет добавлен к списку тегов узла targetNodeName
.Имя
| Описание
|
---|---|
✖ modelOutput
|
Дескриптор вывода модели (тот, который был возвращён из
✖ async .openModelOutput({ itemConfig, workDir })
).
|
✖ tagNodeName
|
строка, полное FDOM-имя узла-тега, который применяется.
|
✖ targetNodeName
|
строка, полное FDOM-имя целевого узла, к которому применяется
tagNodeName . |
✖ sourceFile
|
строка, путь к входному файлу, из которого исходит команда на добавление тега, относительно пути указанного для задания компиляции
✖ inRootDir
.
Есть смысл хранить указание на источник, добавивший тег, в контексте последующей инвалидации (
✖ async .invalidateSourceFile({ modelOutput, sourceFile, newDependencies })
) - тег остаётся в силе, пока остаётся хотя бы один
не инвалидированный источник, добавляющий его. (Заметьте, что
tagTo может быть вызван для одной и той же комбинации tagNodeName и targetNodeName несколько раз с различными sourceFile .) |
tagTo
может быть вызван для одной и той же комбинации tagNodeName
и targetNodeName
несколько раз с различными sourceFile
.)tagTo
, и ровно один раз для каждого sourceFile
для которого будут вызваны
какие-либо tagTo
(или иные добавляющие контент методы). То есть, если тег остаётся в силе после всех команд на инвалидацию источника, то все вызовы tagTo
будут повторно валидировать применение тега из соответствующих
sourceFile
.Имя
| Описание
|
---|---|
✖ modelOutput
|
Дескриптор вывода модели (тот, который был возвращён из
✖ async .openModelOutput({ itemConfig, workDir })
).
|
✖ targetNodeName
|
Строка, полное FDOM-имя целевого узла, к которому в контент дописывается ссылка.
|
✖ refNodeName
|
Строка, полное FDOM-имя узла, на который указывает дописываемая ссылка.
|
✖ refText
|
Строка, альтернативный текст ссылки. Может быть пустым (и именно так и должен храниться в скомпилированной модели, т. к. генераторы могут считать это за указание к использованию для ссылки подходящего текста
по умолчанию).
|
✖ sourceFile
|
Строка, путь к входному файлу, из которого исходит команда на добавление этой ссылки, относительно пути указанного для задания компиляции
✖ inRootDir
.
|
writer
в элементе задания стадии генерации (
✖ writer
).
Записыватель стадии генерации должен быть реализован как модуль CommonJS со следующим интерфейсом...writer
через module.exports - например, таким образом:exports.perform = async function perform({ workDir, itemConfig, errors }) { ... }
Имя
| Описание
|
---|---|
✖ workDir
|
Строка, путь к корневому каталогу проекта, может использоваться как есть в
fs или path . |
✖ itemConfig
|
Словарь (как Object), фрагмент объекта конфигурации, относящийся к данному элементу задания (
✖ items[]
).
Записыватель имеет доступ к любым членам объекта конфигурации для своего элемента задания, но хороший стиль - держать всю специфичную для него конигурацию в выделенном подобъекте,
размещённом в словаре под именем, указывающем на записывателя.
|
✖ errors
|
Массив, коллекция ошибок (как объекты Error JS), возникших в процессе обработки - сюда следует добавлять ошибки по мере их возникновения.
|
lpgwrite-example
.
Он задаётся полем renders[]/renderer
внутри элемента задания стадии генерации для lpgwrite-example
(
✖ renderer
).
Рендерер должен быть реализован как модуль CommonJS со следующим интерфейсом...Имя
| Описание
|
---|---|
✖ async .render({ workDir, rendererConfig, input, errors })
|
Выдать выходной документ, используя конфигурацию рендерера и входные данные, предоставленные вызывателем (
✖
${LP_HOME}/lpgwrite-example: Пример-генератор одностраничной документации HTML/MD
).
|
renderer
через module.exports, например, так:exports.render = async function render({ workDir, rendererConfig, input, errors }) { ... }
Имя
| Описание
|
---|---|
✖ workDir
|
Строка, путь к корневому каталогу проекта, может использоваться как есть в
fs или path . |
✖ rendererConfig
|
Словарь (как Object), фрагмент конфигурации объекта, относящегося к элементу для данного рендерера (
✖ renders[]
).
Рендерер имеет доступ к любым членам объекта конфигурации для своего элемента, но хороший стиль - держать всю специфичную для него конигурацию в выделенном подобъекте,
размещённом в словаре под именем, указывающем на рендерер.
|
✖ input
|
Входные данные для создания документа. Объект в следующем формате:
✖ Входной формат для рендерера lpgwrite-example
.
|
✖ errors
|
Массив, коллекция ошибок (как объекты Error JS), возникших в процессе создания документа - сюда следует добавлять ошибки по мере их возникновения.
|
Имя
| Описание
|
---|---|
✖ .toc[]
|
Массив элементов для оглавления. Каждый элемент - словарь (как Object) со следующими членами...
|
✖ .itemsByUid[uid]
|
Словарь по UID (строке). Те же элементы, что в
✖ .items[]
, развёрнутые в один плоский список, но с ассоциативным ключом по UID (
✖ .uid
).
|
✖ .items[]
|
Массив элементов для показа, упорядоченные в предполагаемом порядке показа при нахождении на общей странице. Каждый элемент - словарь (как Object) со следующими членами...
|
Имя
| Описание
|
---|---|
✖ .title
| Строка, человекочитаемый заголовок элемента.
|
✖ .uid
|
Строка, UID элемента (ключ в
✖ .itemsByUid[uid]
).
|
✖ .subEntries[]
|
Массив (не null, всегда по крайней мере пустой), вложенные элементы данного элемента оглавления. Каждый элемент имеет такую же структуру, как корневой элемент
.toc[] , включая
следующий уровень .subEntries (и т. д.) |
Имя
| Описание
|
---|---|
✖ .uid
|
Строка, UID элемента, может использоваться для доступа к данному элементу через
✖ .itemsByUid[uid]
.
|
✖ .modelBasic[]
|
Базовая часть модели элемента, видимая в кратком режиме показа, должна показываться всегда. Массив (не null, всегда по крайней мере пустой) элементов в порядке показа, каждый элемент может содержать некоторые из следующих членов...
|
✖ .modelMore[]
|
Дополнительная часть модели элемента для показа в полном режиме, в дополнение к базовой части. Массив (не null, всегда по крайней мере пустой), который может содержать все те же элементы, что и
✖ .modelBasic[]
.
|
Имя
| Описание
|
---|---|
✖ .itemTitle
|
Строка. Если этот член присутствует, это означает, что данный элемент списка - заголовок элемента, и содержит человекочитаемый текст заголовка элемента.
|
✖ .uid
|
Строка, определён, только если присутствует
✖ .itemTitle
. UID целевого элемента (точнее, того, от которого заголовок), тот же самый, что в
✖ .uid
.
|
✖ .item
|
Строка. Если этот член присутствует, это означает, что данный элемент - место, в которое следует вывести вложенный FDOM-элемент, и содержит UID выводимого элемента, тот же, что
✖ .uid
.
Обратите внимание, что один и тот же элемент (с тем же UID) может появляться в документе несколько раз, и одно из таких мест появления будет указано как домашнее (основное) место размещения данного элемента - проверяйте флаг
✖ .isHomeLocation
,
если это понятие имеет значение в рамках выводимого формата документа.
|
✖ .isHomeLocation
|
Булевой, определён, только если присутствует
✖ .item
. Если true, то это место, предлагаемое как домашнее место размещения элемента. У каждого элемента есть только одно
домашнее место размещения.
|
✖ .printType
|
Строка, определён, только если присутствует
✖ .item
. Определяет предлагаемый режим показа для FDOM-элемента, вставляемого в данное место. Может быть один из:
|
✖ .text
|
Строка. Если этот член присутствует, то это фрагмент текста в Markdown. Некоторые HTML-подобные теги, регистро-зависимые, должны интерпретироваться как встроенные LP-ссылки (текстовые свойства закодированы по HTML):
|
✖ .openSection
|
Строка. Если этот член присутствует, это означает, что данный элемент списка открывает озаглавленную секцию, и член содержит идентификатор секции для соответствия идущему позже
✖ .closeSection
|
✖ .closeSection
|
Строка. Если этот член присутствует, то данный элемент списка закрывает озаглавленную секцию, и член содержит идентификатор секции для закрытия, соответствующий шедшему ранее
✖ .openSection
.
|
✖ .title
|
Строка, определён, только если присутствует
✖ .openSection
. Заголовок открываемой секции.
|
✖ .table
|
Если этот член присутствует, это означает блок с таблицей. Объект со следующими членами-свойствами...
|
✖ .list[][]
|
Массив массивов строк. Если этот член присутствует, это означает, что данный элемент списка - это список (одноуровневый, ненумерованный). Каждый элемент массива - элемент списка, каждый под-элемент -
Markdown-текст (аналогично
✖ .text
), предполагается, что под-элементы собраны в одну строку, следуя друг за другом
в порядке следования в массиве.
|
<lp-src file="filename"></lp-src>
(сам тег внутри не содержит текста, file
закодирован по HTML): встроенная ссылка на исходный входной файл LP-формата, только без .lpinput
-расширения. Присутствует всегда,
как его интерпретировать или игнорировать - на усмотрение рендерера.<lp-ref uid="UID" text="показываемый текст"></lp-ref>
(сам тег внутри не содержит текста, file
закодирован по HTML): встроенная LP-ссылка на элемент (такая, как задаётся по <#ref ...#>
). UID тот же, что в
✖ .uid
.
Показываемый текст может быть пустым, в этом случае рекомендуется использовать заголовок элемента (
✖ title
).Имя
| Описание
|
---|---|
✖ .headers[]
|
Массив заголовков колонок, в порядке показа колонок. Каждый элемент - строка с заголовком колонки как Markdown-текстом (аналогично
✖ .text
).
|
✖ .rows[]
|
Массив строк таблицы, в порядке показа. Каждый элемент списка - массив колонок, в порядке показа колонок, в котором каждый под-элемент - строка с текстом колонки как Markdown-текстом
(аналогично
✖ .text
).
|
translator
в элементе конфигурации задания для lpgwrite-example
(lpgwrite-example/translator
).
Транслятор должен быть реализован как модуль CommonJS со следующим интерфейсом...translator
через module.exports, например, так:exports.translate = async function translate(str, translatorArgs) { ... }
Имя
| Описание
|
---|---|
✖ str
|
Строка, собственно строка для перевода.
Предполагается Markdown-текстов, с возможным содержанием следующих HTML-подобных тегов:
|
✖ translatorArgs
|
Значение аргумента, заданное для переводчика в
✖ translatorArgs
. Передаётся как соответствующий объект конфигурации,
как он задан в конфигурационном файле.
|
<lp-ref item="FDOM имя">альт. текст ссылки</lp-ref>
: встроенная LP-ссылка. альт. текст ссылки
может быть переведён (обратите внимание, что он закодирован по HTML), остальную часть следует оставить
как есть.<lp-tag>...текст...</lp-tag>
: пользовательский тег разметки (из числа
✖ extraTags
). ...текст... - закодированный по HTML JSON-код объекта, и в таком же формате
должен отстаться после перевода. Какие бывают пользовательские теги и что в них следует переводить - на усмотрение переводчика. Фрагменты объекта, для которых это не ясно, следует оставить в исходном виде.writer: "${LP_HOME}/lpcwrite-basic-json" $
в
✖ writer
.lpcwrite-basic-json
в элементе конфигурации стадии компиляции:{
...
writer: "${LP_HOME}/lpcwrite-basic-json" $, // копировать дословно!
lpcwrite-basic-json: {
outFile: ...,
extraTags: { // optional
...
}
}
}
Имя
| Описание
|
---|---|
✖ extraTags
|
Объект, представляющий словарь формата
tagName: tagContentType . Описывает дополнительные теги, которые записыватель будет распознавать во входном потоке. Они будут добавлены в скомпилированное JSON-представление
модели как объекты customTag (см.
✖ .content [lpgread-basic-json]
).
Пользовательские теги, не описанные здесь, будут пропускаться с выдачей предупреждения. Обратите внимание, что имена тегов не чувствительны к регистру (приводятся к нижнему). |
✖ outFile
|
Строка. Путь к выходному JSON-файлу (если путь не абсолютный, то относительно корневого каталога проекта). Файл каждый раз перезаписывается полностью, но элементы представления модели, уже существовавшие в нём,
по возможности обновляются, а не перестраиваются с нуля, стараясь сохранить фрагменты данных, для которых источники по факту не изменялись.
|
tagName: tagContentType
. Описывает дополнительные теги, которые записыватель будет распознавать во входном потоке. Они будут добавлены в скомпилированное JSON-представление
модели как объекты customTag
(см.
✖ .content [lpgread-basic-json]
).
Пользовательские теги, не описанные здесь, будут пропускаться с выдачей предупреждения. Обратите внимание, что имена тегов не чувствительны к регистру (приводятся к нижнему).extraTags: {
"tagName1": <tag1 content type>, // строка
"tagName2": <tag2 content type>, // строка
...
}
lpgwirte-example
сам по себе определяет только общую, формато-независимую структуру документа, а вывод документа в конретном целевом формате доверяется под-плагину, называемому рендерер. Как можно понять из заголовка,
имеются встроенные рендереры для одностраничных HTML и MD документов, но на самом деле пользователь может создавать и задействовать и свои собственные рендереры.lpgwrite-example
добавляет поверх FDOM-терминологии несколько дополнительных конвенций:%title
содержит человекочитаемый заголовок элемента. Если члена %title
нет, то заголовок предполагается таким же, как краткое имя элемента.
Обычно элемент следует за открывающим фрагментом элемента, в виде <#./%title: Your title#>
(обратите внимание, что поначалу может часто напрашиваться пропуск ./
или :
- это ошибка; привыкнуть к правильному написанию - вопрос
опыта и некоторой практики).lpgwrite-example
в элементе конфигурации стадии генерации (
✖
${LP_HOME}/lpgwrite-example: Пример-генератор одностраничной документации HTML/MD
):{
...
writer: "${LP_HOME}/lpgwrite-example" $, // копировать дословно!
lpgwrite-example: {
trace: ...,
program: [...],
renders: [
{
docModel: ...,
renderer: ...,
... // дополнительная рендереро-специфичная конфигуркция
},
...
]
}
}
lpgwrite-example
, как перечислено далее:Имя
| Описание
|
---|---|
✖ renders[]
|
Список под-заданий по собственно выводу документа. В дополнение к нижеперечисленным членам, может содержать также дополнительные члены с рендереро-специфичными фрагментами конфигурации.
|
✖ trace
|
Булевой, необязательно. Если true, то обработка программы документа будет проходить с выводом дополнительных логов, позволяя более подробную трассировку того, что делается, а что не делается.
|
✖ program[]
|
Массив инструкций программы документа (см.
✖ Программа документа для ${LP_HOME}/lpgwrite-example
)
|
Имя
| Описание
|
---|---|
✖ renderer
|
Строка, путь к модулю рендерера. Рендерер должен следовать
✖ Интерфейс для рендерера lpgwrite-example
. В составе Logipard имеются следующие встроенные рендереры...
|
✖ docModel
|
Строка, используемая модель документа. Ссылается на docModel в программе документа, конкретно по значению
name в
✖ Определение модели документа
. |
lpgwrite-i18n-assist-trn-none
, но пользователь может создавать и задействовать свои
собственные переводчики.lpgwrite-i18n-assist
образует своего рода "субконвейер": его вывод является промежуточным, и предполагается, что его используют уже непосредственно генераторы документов, которые следуют за ним в
списке элементов lp-generate
.lpgwrite-i18n-assist
добавляет поверх FDOM следующую конвенцию:%title
, если есть, содержит человекочитаемый заголовок для его родительского элемента (аналогично
✖ ${LP_HOME}/lpgwrite-example
)%title
добавлен тег %noloc
, этот заголовок полагаеся непереводимым - он не будет включён в промежуточный файл, и будет фигурировать в переведённом FDOM-файле без изменений<#./%title: Этот заголовок будет переведён#>
, и <#./%title %noloc: Этот заголовок не будет переведён#>
.
Эта конвенция не противоречит аналогичной в
✖ ${LP_HOME}/lpgwrite-example
и органично её дополняет.lpgwrite-i18n-assist
в конфигурации для элемента генерации:...
{
inFile: ...,
writer: "${LP_HOME}/lpgwrite-i18n-assist" $, // копировать дословно!
lpgwrite-i18n-assist: {
translator: ...,
items: [
{
outFile: ...,
interimFile: ...,
interimFileCharset: ...,
translatorArgs: ...
}
]
}
},
...
lpgwrite-i18n-assist
, в следующем составе:Имя
| Описание
|
---|---|
✖ items[]
|
Массив. Элемнты для обработки в этом задании
lpgwrite-i18n-assist , используя один и тот же переводчик, заданный в
✖ translator
.
Каждый элемент в items[] - объект следующего вида: |
✖ translator
|
Строка, путь к модулю переводчика, абсолютный или относительно корня проекта. Переводчик должен следовать
✖ Интерфейс для переводчика lpgwrite-i18n-assist
.
Logipard включает встроенный переводчик-заглушку
✖
${LP_HOME}/lpgwrite-i18n-assist-trn-none: Переводчик-заглушка для генератора lpgwrite-i18n-assist
.
|
lpgwrite-i18n-assist
, используя один и тот же переводчик, заданный в
✖ translator
.
Каждый элемент в items[]
- объект следующего вида:Имя
| Описание
|
---|---|
✖ translatorArgs
|
Произвольное значение JSON/LPSON, необязательно (по умолчанию null). Объект или значение, которое будет передано переводчику в метод
✖ async .translate(str, translatorArgs)
.
|
✖ outFile
|
Строка. Путь к выходному файлу JSON FDOM с переведённым текстом, абсолютный или относительно корня проекта. Предполагается наличие расширения .json.
|
✖ interimFile
|
Строка. Путь к промежуточному файлу перевода, абсолютный или относительно корня проекта. В сущности, это почти обычный текстовый файл, поэтому предполагается наличие расширения .txt.
|
✖ interimFileCharset
|
Строка, необязательно (по умолчанию "utf-8"). Кодировка для использования в промежуточном файле.
|
...
## Item: /domain.logipard/interfaces/compile/%title
# lp-stage-plugin-ifs.lp-txt
/ "Para:Ev+yL9F/vTiMmuKTf0MCOtkPdxbajKJGYcTegdUiEhKX4g0C7A+PMVsfHPOVu90ZRrksqgrsekUutwoGUA72zw=="
Интерфейсы, относящиеся к стадии компиляции
\ "Para:Ev+yL9F/vTiMmuKTf0MCOtkPdxbajKJGYcTegdUiEhKX4g0C7A+PMVsfHPOVu90ZRrksqgrsekUutwoGUA72zw=="
## Item: /domain.logipard/interfaces/compile
## Item: /domain.logipard/interfaces/compile/writer-toolkit/%title
# internal/lp-compile-tools.js
/ "Para:vGDelX4EnoLn07hY9QgDuASeK7cUvLxrere0vuqNEu/pOGNVoVfpoUEsEtI0IW/gLrN3w2BHhUdktg51eEeEKg=="
Инструментарий записывателя стадии компиляции для процессора пользовательских тегов
\ "Para:vGDelX4EnoLn07hY9QgDuASeK7cUvLxrere0vuqNEu/pOGNVoVfpoUEsEtI0IW/gLrN3w2BHhUdktg51eEeEKg=="
...
/ "Para:..."
...\ "Para:..."
- ограничители, между которыми располагается фрагмент переведённого контента, который можно редактировать вручную. lpgwrite-i18n-assist
сохраняет эти фрагменты в текущем виде, пока не изменятся
соответствующие фрагменты контента в оригинале (но может менять их положение в файле при изменении FDOM-структуры).lpgwrite-i18n-assist
соблюдает разбиение на уровне одного параграфа или элемента списка на фрагмент, группирует
их по элементам модели и с сохранением взаимного порядка.Para
или коды в них - это теги для сопоставления фрагментам соответствующего исходного контента.
Строки вне этих фрагментов следует считать комментариями для удобства навигации, они могут меняться без каких-либо гарантий.Имя
| Описание
|
---|---|
✖ async loadFromFile(filePath [, extractSrcFile])
|
Загрузить модель в память и открыть для чтения в терминах FDOM (
✖ Запросы FDOM
). Функция на уровне модуля.
|
const { loadFromFile } = require('logipard/lpgread-basic-json');
async main() { // API загрузчика асинхронное
var reader = await loadFromFile("your-fdom.json");
// полагая, что ваша модель содержит элементы с такими именами, как ниже...
reader.nameAlias("domain.your.program", "M"); // задать алиас имени
var classesSection = reader.item("M/classes"); // <Item>, domain.your.program/classes
var classA = reader.item(classesSection, "classA"); // <Item>, domain.your.program/classes/classA
// найдём элементы для всех классов, которые расширяет A, и напечатаем их заголовки
var extends = reader.item("%extends"); // %extends
var queryCtxItemsExtByA = reader.newQueryContext(); // <QueryContext>
var itemsExtByA = queryCtxItemsExtByA.with(classA) // или .with(queryCtxItemsExtByA.collection(classA))
.query({ inMembersThat: { named: "^%extends$" }, recursive: true, query: { tagsThat: true }})
.teardownCollection(); // itemsExtByA = <Collection>
for (var itemExtByA of itemsExtByA) { // itemExtByA = <Item>
console.log(reader.item(itemExtByA, "%title").content[0]); // полагая, что все элементы имеют члены %title с чисто текстовым контентом
}
}
Имя
| Описание
|
---|---|
✖ filePath
|
Строка. Путь (такой же, как для методов
fs Node.JS) к JSON-файлу с представлением FDOM. |
✖ extractSrcFile
|
Булевой, необязательный параметр (по умолчанию false). Если true, то в текст будут вставлены имена файлов-источников LP.
Полезно при чтении с диагностическими целями, или имея таковые в виду.
|
Имя
| Описание
|
---|---|
✖ .item([itemRelTo,] name)
|
Получить элемент по его полному или относительному FDOM-имени. Аналогично
✖ .item([baseItem ,] name)
, но не поддерживает алиасы, т. к. используется без контекста.
|
✖ .itemByUid(uid)
|
Вернуть элемент по UID (см.
✖ .uid
). Поскольку это специфичный для данного считывателя метод, не предусмотренный терминологией FDOM,
он может вернуть
null для несуществующего элемента. |
✖ .newQueryContext()
| Создать новый объект контекста запроса.
|
Имя
| Описание
|
---|---|
✖ itemRelTo
|
Необязательный параметр. Если задан, то он обозначает элемент, путь к котрому принимается за базовый для
✖ name
, который в этом случае считается относительным путём.
Может быть одним из следующего:
|
✖ name
|
Имя элемента, полное, если
itemRelTo не задан, или относительное него, если задан. |
Имя
| Описание
|
---|---|
✖ .content [lpgread-basic-json]
|
Свойство только для чтения, массив элементов контента. Реализует
✖ .content
в варианте, специфичном для
✖ logipard/lpgread-basic-json.js
.
|
✖ .uid
|
Свойство только для чтения, строка. UID элемента в JSON-представлении модели, предоставляемый как ключ для быстрого доступа.
|
✖ .toString()
| Стандартное преобразование в строку JS
|
Имя
| Описание
|
---|---|
✖ .content
|
Свойство только для чтения. Контент элемента (текст, внутритекстовые ссылки, и всё прочее, что поддерживает модель, обслуживаемая данным считывателем.)
|
✖ .name
|
Свойство только для чтения, строка. Полный путь элемента (без именных алиасов).
|
✖ .shortName
|
Свойство только для чтения, строка. Краткое имя элемента (последний сегмент полного имени-пути).
|
✖ .tags
|
Свойство только для чтения. Коллекция тегов элемента.
|
✖ .members
|
Свойство только для чтения. Коллекция членов элемента.
|
✖ .isNull
|
Свойство только для чтения, булевое. Проверка, что элемент пустой (true) или нет (false).
|
✖ .parent
|
Свойство только для чтения,
✖ <Item>
. Возвращает родительский элемент (тот, членом которого является данный). Для корневого элемента
возвращает null (именно null-значение, а не нулевой элемент).
|
✖ .isConditionTrue(lpqCtx, condSpec)
|
Проверить, что элемент удовлетворяет некоторому условию; проверка должна делаться в рамках заданного контекста запроса (чтобы иметь возможность использовать алиасы условий и коллекций).
|
{ ref: <Item>, text: string }
(ref - это объект
✖ <Item> [lpgread-basic-json]
): внутритекстовая ссылка на FDOM-элемент{ customTag: object }
: пользовательский тег, как его записал
✖ ${LP_HOME}/lpcwrite-basic-json: Записыватель FDOM в JSON-файл
Имя
| Описание
|
---|---|
✖ .content
|
Свойство только для чтения. Контент элемента (текст, внутритекстовые ссылки, и всё прочее, что поддерживает модель, обслуживаемая данным считывателем.)
|
✖ .name
|
Свойство только для чтения, строка. Полный путь элемента (без именных алиасов).
|
✖ .shortName
|
Свойство только для чтения, строка. Краткое имя элемента (последний сегмент полного имени-пути).
|
✖ .tags
|
Свойство только для чтения. Коллекция тегов элемента.
|
✖ .members
|
Свойство только для чтения. Коллекция членов элемента.
|
✖ .isNull
|
Свойство только для чтения, булевое. Проверка, что элемент пустой (true) или нет (false).
|
✖ .parent
|
Свойство только для чтения,
✖ <Item>
. Возвращает родительский элемент (тот, членом которого является данный). Для корневого элемента
возвращает null (именно null-значение, а не нулевой элемент).
|
✖ .isConditionTrue(lpqCtx, condSpec)
|
Проверить, что элемент удовлетворяет некоторому условию; проверка должна делаться в рамках заданного контекста запроса (чтобы иметь возможность использовать алиасы условий и коллекций).
|
Имя
| Описание
|
---|---|
✖ .itemByUid(uid)
|
Вернуть элемент по UID (см.
✖ .uid
). Поскольку это специфичный для данного считывателя метод, не предусмотренный терминологией FDOM,
он может вернуть
null для несуществующего элемента. |
✖ .clearNameAlias(aliasName)
|
Очистить алиас элемента, заданный через
✖ .nameAlias(aliasName, item)
. Алиас более не является валидным, пока не будет задан заново.
|
✖ .clearCollectionAlias(collectionAliasName)
|
Очистить алиас коллекции, заданный через
✖ .collectionAlias(aliasName, ...collectionSpecs)
. Алиас более не является валидным, пока не будет задан заново.
|
✖ .clearQueryAlias(queryAliasName)
|
Очистить алиас запроса, заданный
✖ .queryAlias(aliasName, ...querySpecs)
. Алиас более не является валидным, пока не будет задан заново.
|
✖ .clearConditionAlias(conditionAliasName)
|
Очистить алиас условия, заданный
✖ .conditionAlias(aliasName, condSpec)
. Алиас более не является валидным, пока не будет задан заново.
|
Имя
| Описание
|
---|---|
✖ .nameAlias(aliasName, item)
|
Задать алиас имени элемента (который должен быть корректным кратким именем), могущий впоследствии использоваться как самостоятельное имя элемента, или как начальный сегмент для другого имени элемента в пределах данного
✖ <QueryContext>
.
Поведение в случае уже существующего алиаса с таким же именем зависит от реализации.
|
✖ .collectionAlias(aliasName, ...collectionSpecs)
|
Задать именованный алиас коллекции, который может быть использован позднее для ссылки на эту коллекцию в пределах данного контекста (
✖ <CollectionSpec>
). Коллекция строится из коллекций, соответствующих каждому элементу
списка спецификаций. Алиас - постоянный в пределах данного контекста, в отличие от локального алиаса (
✖ Задать локальный алиас коллекции ["alias ..."]
).
|
✖ .queryAlias(aliasName, ...querySpecs)
|
Задать именованный алиас запроса, который может быть использован позднее для ссылки на этот запрос в пределах данного контекста (
✖ <QuerySpec>
). Список трактуется как составной запрос.
|
✖ .conditionAlias(aliasName, condSpec)
|
Задать именованный алиас условия, который может быть использован позднее для ссылки на это условие в пределах данного контекста (
✖ <Condition>
).
|
✖ .item([baseItem ,] name)
|
Вернуть элемент по заданному имени-пути, полному или относительно заданного базового элемнета. Первый краткоимённый сегмент полного имени может быть алиасом имени, определённым в данном
✖ <QueryContext>
.
|
✖ .collection(...collectionSpecs)
|
Возвращает коллекцию, заданную списком спецификаций элементов коллекции. Каждый элемент списка -
✖ <CollectionSpec>
.
|
✖ .with(...collectionSpecs)
|
Задать текущую коллекцию для последующего запроса (вызова
✖ .query(...querySpecs)
). Коллекция строится из коллекций, соответствующих каждому элементу списка.
.with , по сути, инициирует цепочку запроса, но может быть использован и в её середине, чтобы заменить текущую коллекцию на некотором шаге. |
✖ .query(...querySpecs)
|
Выполнить запрос, или список запросов, трактуемый как составной запрос, с учётом того, что текущая коллекция задана предыдущим
✖ .with(...collectionSpecs)
или получена от предыдущих вызовов
.query .
Обратите внимание, что полученная коллекция не возвращается немедленно, а становится новой текущей коллекцией. |
✖ .teardownCollection()
|
Завершить запрос и вернуть результат (текущую коллекцию на момент вызова). Сама текущая коллекция сбрасывается, так что следующий запрос должен быть инициализирован повторно, начиная с
✖ .with(...collectionSpecs)
.
|
✖ .currentCollectionAlias(aliasName)
|
Задать именованный алиас коллекции для текущей коллекции, который может быть использован позже для ссылки на эту коллекцию в пределах данного контекста (
✖ <CollectionSpec>
). Может быть использован только
в процессе запроса (пока имеет значение текущая коллекция), в противном вызов данного метода - ошибка. Это локальный алиас запроса, в отличие от постоянного (
✖ Задать локальный алиас коллекции ["alias ..."]
).
|
✖ .compileQuery(...querySpecs)
|
Скомпилировать запрос в объект-дескриптор, который может быть использован позже для ссылки на этот запрос в пределах данного контекста (
✖ <QuerySpec>
). Список трактуется как составной запрос.
|
lpgwrite-example-render-html
в конфигурации для элемента генерации
✖ renders[]
:lpgwrite-example: {
...
renders: [
{
docModel: ...,
renderer: "${LP_HOME}/lpgwrite-example-render-html" $, // копировать дословно!
lpgwrite-example-render-html: {
outFile: ...,
emitToc: ...,
inTemplateFile: "logipard-doc.tpl.html",
cssClasses: {
// все члены этого объекта не обязательны, и cssClasses может быть опущен вообще целиком
itemTitle: ...,
rawTitle: ...,
paragraph: ...,
verbatimSpan: ...,
linkSpan: ...,
moreSpan: ...,
elsewhereSpan: ...,
actionSpan: ...,
offSiteBlock: ...
},
htmlPlaceholder: ...,
cssPlaceholder: ...,
extraTokens: {
TOKEN_ID: "token value",
ANOTHER_TOKEN_ID: "token value 2",
...
},
localizedKeywords: {
// настройте эти члены соответственно целевому языку
SNAPBACK: "Вернуть",
SNAPBACK_AND_SCROLL: "Вернуть и перейти",
ELEVATE: "Поднять",
RESET: "Сбросить",
ELEVATE_TO: "Поднять до...",
COPY_ITEM_NAME: "Копировать полное имя модели LP FDOM этого элемента в буфер:",
ITEM_UNFOLDED_ELSEWHERE: "Элемент развёрнут в другом месте страницы - нажмите, чтобы развернуть здесь...",
MORE: "Ещё... >>",
TABLE_OF_CONTENTS: "Содержание"
},
addSourceRef: ...
}
},
...
]
}
lpgwrite-example-render-html
внутри соответствующего элемента renders[]
, включает следующие члены...Имя
| Описание
|
---|---|
✖ outFile
|
Строка. Путь к выходному файлу документа (.html) для записи, абсолютный или относительно корня проекта.
|
✖ emitToc
|
Булевой, необязательно (по умолчанию true). Если true, то рендерер добавит в документ секцию с оглавлением ("Содержание").
|
✖ inTemplateFile
|
Строка. Путь к файлу шаблона для выходного HTML, абсолютный или относительно корня проекта.
Шаблон - это HTML-код генерируемого файла с добавленными в него маркерами для вставки сгенерированных CSS, HTML, и, при необходимости, дополнительными маркерами.
|
✖ cssClasses
|
Словарь строк, необязательно. Классы CSS для применения к определённым элементам выходного документа. Обратите внимание, что эти классы рассчитаны на каскадирование
со сгенерированными классами
lpgwrite-example-render-html , управляющими макетом вёрстки, поэтому должны содержать только данные, влияющие на внешний вид (font, color, background, padding, etc.),
а не расположение (display, grid или связанные с ним, flex или связанные с ним, position, z-order, и т. д.). |
✖ htmlPlaceholder
|
Строка. Текст маркера в шаблоне, в точности совпадающий с тем, который будет заменён на сгенерированный HTML-код, должен располагаться внутри тега
<body> .
Вставленный код будет обёрнут в одиночный <div> -элемент без явно указанных непосредственно на нём классов и стилей. |
✖ cssPlaceholder
|
Строка. Текст марекра в шаблоне, в точности совпадающий с тем, который будет заменён на сгенерированный CSS-код, должен располагаться внутри тега
<style> и не внутри какого-либо блока. |
✖ extraTokens
|
Словарь строк, необязательно. Любые дополнительные токены для замены в шаблоне. Ключи словаря - тексты маркеров, в точности совпадающие с теми, которые будут заменяться
(они не должны совпадать с теми, которые задействованы для
htmlPlaceholder , cssPlaceholder , или других таких же токенов), значения - дословный HTML-код для вставки на их место. |
✖ localizedKeywords
|
Словарь строк, необязательно. Список строк для применения в определённых местах UI сгенерированного документа, предполагаются соответствующими языку перевода целевого документа.
Эти строки - обычный текст.
|
✖ addSourceRef
|
Булевой, необязательно (по умолчанию = false). Если true, генератор добавит имена исходных файлов к текстовым фрагментам, это поможет напомнить о происхождении конкретного
куска текста. Этот режим полезен при вычитке и отладке черновика документа, особенно когда ваш проект и информация в нём разрастётся на достаточно большое число файлов.
|
lpgwrite-example-render-html
, управляющими макетом вёрстки, поэтому должны содержать только данные, влияющие на внешний вид (font, color, background, padding, etc.),
а не расположение (display, grid или связанные с ним, flex или связанные с ним, position, z-order, и т. д.).вот такого
). Он не касается блоков кода - они выполняются как теги <code>
и стилизируются через них.htmlPlaceholder
, cssPlaceholder
, или других таких же токенов), значения - дословный HTML-код для вставки на их место.lpgwrite-example-render-md
в конфигурации для элемента генерации
✖ renders[]
:lpgwrite-example: {
...
renders: [
{
docModel: ...,
renderer: "${LP_HOME}/lpgwrite-example-render-md" $, // копировать дословно!
lpgwrite-example-render-md: {
outFile: ...,
emitToc: ...,
header: ...,
footer: ...,
addSourceRef: ...
}
},
...
]
}
Имя
| Описание
|
---|---|
✖ outFile
|
Строка. Путь к записываемому файлу выходного документа (.md), абсолютный или относительно корня проекта.
|
✖ emitToc
|
Булевой, необязательно (по умолчанию = true). Если true, то рендерер добавит секцию с оглавлением в начале документа.
|
✖ header
|
Строка, необязательно. Если задано, то рендерер допишет эту строку в самое начало документа, перед оглавлением (если есть), что полезно для создания заголовка и аннотаций.
Эта строка - в разметке Markdown.
|
✖ footer
|
Строка, необязательно. Если задано, то рендерер допишет эту строку в самый конец документа. Эта строка - в разметке Markdown.
|
✖ addSourceRef
|
Булевой, необязательно (по умолчанию = false). Если true, генератор добавит имена исходных файлов к текстовым фрагментам, это поможет напомнить о происхождении конкретного
куска текста. Этот режим полезен при вычитке и отладке черновика документа, особенно когда ваш проект и информация в нём разрастётся на достаточно большое число файлов.
|
[UNTRANSLATED-<язык>]
,
по которому можно искать в промежуточном файле обновлённые и/или непереведённые строки.translatorArgs
внутри члена lpgwrite-i18n-assist
элемента конфигурации
✖ renders[]
для задания генерации:lpgwrite-i18n-assist: {
...
renders: [
{
docModel: ...,
renderer: "${LP_HOME}/lpgwrite-i18n-assist" $, // копировать дословно!
lpgwrite-i18n-assist: {
translator: "${LP_HOME}/lpgwrite-i18n-assist-trn-none" $, // копировать дословно!
items: [
{
...
translatorArgs: { lang: ... }
},
...
]
},
...
]
}
.lpinput
),
в каталог, указанный в
✖ outDir
в элементе задания стадии генерации.<#имя-тега ... #>
(имя тега - буквенно-цифровое, разрешаются -
). Теги могут быть вложенными. Помимо составляющих частей
тегов, остальной формат текста (чисто текст, Markdown, HTML, или что-либо ещё) непрозрачен с точки зрения входных данных LP, его интерпретация - задача для стадий компиляции и генерации.<#LP ./dataStruct { <#./%title: FDOM data structure#> <#./%order: 1#>
The FDOM data structure explanation starts best with a visual example...
<#img ./lp-model.png #>
The model data consists of *nodes*.
- blah
- blah blah
blah blah blah (see: <#ref dataStruct/parent-member#>, <#ref dataStruct/tagged-tag#> ).
blah blah total of 11 nodes.#>
<#~delimiter~ ... ~delimiter~#>
. Ограничитель - последовательность любых не-~
символов, в том числе пустая, она должна совпадать у
начинающего и заканчивающего ограничителей экранируемого фрагмента. Всё между ограничителями берётся как дословный простой текст:<#this-is-tag
это данные контента
<#this-is-tag-too и это тоже данные контента#>
<#~~
Это всё обычный текст, <#even-this#>
~~#>
Это снова данные контента <#and-a-tag#> (буквы в тегах разрешаются только латинские)
<#~a~
Это снова обычный текст, <#~~ и даже это ~~#>
~a~#>
#>
-
(также возможно написание <-#tag-name ... #>
) - такие теги считаются закомментированными и не влияют на контент и FDOM, хотя внутри они всё равно должны иметь
корректный формат (правильно согласованные открытия-закрытия тегов и экранировок).это данные <#with-markup-tag inside#>
а это <-#dropped-tag#> данные без тегов разметки <#-this-is-dropped-tag-either and this <#is-not#>, но игнорируется, т. к. внутри исключённого тега#> однозначно
это данные <#with-markup-tag inside#>
а это данные без тегов разметки однозначно
LP
, в том числе собственно <#LP ...#>
, зарезервированы для потока контента и директив Logipard (эти имена не чувствительны к регистру). Кроме того, <# ... #>
считается краткой формой записи для <#LP ...#>
. Также зарезервирован тег <#ref ...#>
(для ссылок на элементы документации Logipard, см. далее), это имя тоже не чувствительно к регистру. Все прочие теги
называются пользовательскими тегами, и их обработка предоставляется записывателю скомпилированной модели на стадии
✖ Стадия компиляции
. Их имена МОГУТ быть
чувствительными к регистру, на усмотрение реализации записывателя модели.<#LP itemName: ...контент... #>
или <# itemName: ...content...#>
. За именем элемента могут следовать имена FDOM-тегов, опционально начинающиеся с #
:
<#LP itemName tagItemName1 tagItemName2 ...: ...#>
, <#LP itemName #tagItemName1 tagItemName2 ...: ...#>
, <#LP itemName #tagItemName1 #tagItemname2 ...: ...#>
.<#LP A: это попадает в элемент A#>
<#LP B: это попадает в элемент B#>
<#LP C: это попадает в элемент C#>
<#LP D %tagDWithThis#> <-# если нет контента, только добавляются FDOM-теги, то можно не добавлять `:`#>
<#LP A: это попадает в элемент A
Может быть несколько потоков в один и тот же элемент - это разрешено, контент из каждого следующего поочерёдно дописывается к предыдущему.
Разрешено даже иметь такие потоки в разных входных файлах, но в этом случае следует иметь в виду, что порядок, в котором входные файлы будут
обработаны, не гаранитирован.
#>
<#LP Outer: это попадает в элемент Outer
<#LP Inner: это попадает в элемент Inner#>
это опять попадает в элемент Outer
#>
{
для ограничителя вместо :
. В этом случае, даже после окончания отступления, текущий уровень остаётся открытым до того момента, когда встретится тег
закрытия открытого отступления:<#LP A: это попадает в A
<#LP B { это попадает в B#>
это всё ещё попадает в B
<#LP }
это попадает в A (обратите внимание - если тег закрытия содержит некоторый контент, то важно разместить его на новой строке (или после тега разметки, по крайней мере <-# comment#>) после `}`) #>
это попадает в A
<#LP C/D { это попадает в C/D#>
<#LP E { открытые отступления могут быть вложенными#>
это попадает в E
<#LP } #>
это попадает в C/D
<#LP } #>
это попадает в A
#>
<#LP A: это попадает в A
<#LP.: это тоже попадает в A#>
#>
<#LP B/C: это попадает в B/C
<#LP.: это тоже попадает в B/C#>
Добавлять таким способом контент нет особого смысла, но это подходящий способ для добавления тега:
<#LP . %tag"B/C"WithThis #>
#>
<#LP D/E/F: это попадает в D/E/F
<#LP D/E/F/G: это попадает в D/E/F/G#>
это попадает в D/E/F
<#LP ./G: это опять попадает в D/E/F/G#>
#>
<#LP A
<#LP: это в A (как и <#LP#>) #>
#>
#
:<#LP #%tagToCurrentItem#>
..
, ...
и т. д. (как "надкаталогов") в качестве начальных сегментов означает имя на один, два, и т. д. уровня выше, чем текущее имя:<#LP D/E/F/G: это попадает в D/E/F/G
<#LP ..: это попадает в D/E/F#>
<#LP ...: это попадает в D/E#>
<#LP ....: это попадает в D#>
<#LP .....: это попадает в корневой элемент#>
#>
К слову, все потоки контента во входном файле - по сути, отступления на уровне корневого элемента.
Технически, можно добавлять контент и на уровень корневого элемента, но это имеет мало смысла и является плохим стилем.
<#LP A/B
<#LP C/D {#>
<#LP ..: это попадает в C#>
<#LP ...: здесь мог быть уровень корня, но у нас есть внешний уровень, с которого можно продолжить, так что это попадает в A/B #>
<#LP ....: это попадает в A #>
<#LP .....: и только это попадает в корень #>
Это снова попадает в C/D
<#LP } #>
Это снова попадает в A/B
#>
<#LP A/B
<#LP ./C/D/..: . - это A/B, ./C/D - это A/B/C/D, ./C/D/.. - это A/B/C, так что, в результате, это A/B/C #>
#>
<#LP A:
<#LP B/C: this is B/C (not A/B/C !)
<#LP D: это действительно D#>
<#LP C: это B/C#>
<#LP B: это B#>
<#LP A: уровень B закончился, но над ним есть A, так что это A#>
#>
#>
<#LP A/B/C
<#LP ./%tag: это A/B/C/%tag #>
<#LP A C/%tag: мы в A и добавляем ему тег A/B/C/%tag
<#ref ./B#> - ссылка на A/B
#>
#>
<#LP A:
<#LP B/C { #>
Это попадает в B/C
<#LP } #> <-#закрывает B/C#>
Это попадает в A
#>
<#LP A:
<#LP B/C/D { #>
Это попадает в B/C/D
<#LP } C #> <-#закрывает C, но оставляет B (обратите внимание - краткое имя на той же строке, что `}`) #>
Это попадает в B
<#LP } #> <-# закрывает "оставшуюся" часть отступления, то есть B (можно было бы также использовать <#LP } B#> с тем же результатом)#>
Это попадает в A
#>
.
:<#LP A:
<#LP B/C/D { #>
Это попадает в B/C/D
<#LP } C/. #>
Это попадает в B/C (попало бы в B, если бы мы использовали "} C")
<#LP } #>
Снова в A
#>
<#LP A:
<#LP B/C { #>
<#LP D/E { #>
это попадает в D/E
<#LP } E #>
это попадает в D
<#LP } B #>
закрыли остающиеся D и B/C, это попадает в A
#>
<#LP A:
<#LP B/C { #>
<#LP D/E:
это попадает в D/E, но обратите внимание, что "D/E" открыто как не-открытое отступление, и мы всё ещё находимся на его уровне
СЛЕДУЮЩЕЕ - НЕКОРРЕКТНО: <#LP } E #>
Использование "} D", или "} B", или "} C", или "} A" здесь также не разрешается, т. к. они проходят сквозь всё ещё действующий D/E.
#>
<#LP } B #>
<#LP F/G {
То же самое относится в уровню внутри действующего тега, оставляющего открытое отступление,
до того, как он завершится.
То есть, следующее - некорректно: <#LP } F #>
#>
но здесь, поскольку тег, оставивший открытое отступление, закончился, следующее уже приемлемо:
<#LP } F #>
#>
<#LP-TRACE-WHERE [опциональная метка]#>
, который распечатает имя текущего уровеня в том месте, в которое он вставлен,
включая опциональную метку (если она задана) и расшифровку разрешения имени.<#REF item/name#>
. При указании имени действуют правила разрешения имён (
✖ Разрешение имён элементов и тегов FDOM
).
Ссылки поддерживаются на стадии компиляции FDOM как встроенная возможность - пользователю FDOM (напримр, генератору) не требуется изобретать для этого отдельный пользовательский тег.Это <#ref item/name: ссылка на item/name с альт. текстом#>
<# name tag1 tag2 ...: ...контент...#>
), или, если они добавляются к элементу текущего уровня, позднее во вспомогательном под-отступлении (<#. tag1 tag2 ...#>
).
Но возможно сделать и наоборот - добавить элемент текущего уровня как FDOM-тег какому-либо другому элементу: <#LP-TAG-TO other-item-name#>
.<#LP-INCLUDE lp-module-inc.lp-txt#>
или короче...
<#LP-INC lp-module-inc.lp-txt#>
обратите внимание, что здесь даётся только исходное расширение файла, без суффикса .lpinput
.lpinput-inc
вместо .lpinput
, и они не обрабатываются автоматически на стадии компиляции, поскольку
считаются только фрагментами "основных" файлов, которые подключаются вручную. Нельзя подключать другие "основные" файлы (но можно подключить файл-модуль из другого файла-модуля)."lp-extract.gen"
, то для файлов-модулей это может быть
что-нибудь вроде "lp-extract.gen/lp-includes"
. Тому есть причина. Позже, на стадии компиляции, при обработке директив <#LP-inc[lude] имя-файла#>
, имя-файла
будет интерпретироваться следующим образом:.
или ..
, то это путь отнсительно каталога, где находится обрабатываемый входной файл (т. е. содержащий данный <#LP-INCLUDE#>
) - но это достаточно редкий случай,<includes-dir>/file-name[.lpinput-inc]
методом каскадного поиска, то есть, начиная с каталога, где находится обрабатываемый файл, если там нет - то в
../<includes-dir>/file-name[.lpinput-inc]
относительно него, потом ../../<includes-dir>/file-name[.lpinput-inc]
, и т. д., до тех пор, пока подключаемый файл не будет найден, или пока не будет достигнут
каталог <extracted-input-root>
. То есть, по стратегии, аналогичной той, по которой Node.JS ищет файл по require(filename)
. Для организации и использования файлов-модулей рекомендуется именно этот способ.
Например, можно разместить файл-модуль как <extracted-input-root>/<includes-dir>/common.lp-txt[.lpinput-inc]
, а потом подключать его через <#LP-INCLUDE common.lp-txt#>
из любого входного файла
под <extracted-input-root>/**
.outDir
практически как угодно, но следует позаботиться, чтобы они совпадали с каталогами, которые будут <extracted-input-root>
и <includes-dir>
на стадии компиляции
(т. е. будут указаны в
✖ inRootDir
и
✖ lpIncLookupDirName
, соответственно).
Если несколько заданий стадии извлечения рассчитаны на одно и то же целевое задание стадии компиляции, то их outDir
должны быть согласованы с inRootDir
и lpIncLookupDirName
этого задания стадии компиляции.M
для следующего за корневым уровнем имени элемента-домена), или для быстрого перемещения фрагментов FDOM
под другие фактические имена без изменения исходников. Синтаксис для определения алиаса: <#LP-ALIAS new-name: old-name#>
После этого new-name
становится алиасом для old-name
(само по себе old-name
тоже можно продолжать использовать).<#LP-INCLUDE ...#>
, но не на каждый файл-модуль сам по себе, а
по месту конкретного подключения). В FDOM концепция алиасов отсутствует, и в окончательном скомпилированном результате все имена попадают уже разрешёнными.<#LP-ALIAS A/B: C#>
<#LP A/B: это попадает в C: A/B #>
<#LP A/B/D: это попадает в C/D, потому что начальная часть A/B - алиас для C#>
но:
<#LP A/B/../D: это попадает в A/D, потому что A/B/../D разрешается в A/D, а A - не алиас #>
также:
<#LP A: это попадает в A
<#LP ./B: это попадает в C, потому что непосредственно разрешённое имя - A/B, и оно является алиасом#>
#>
<#LP-TRACE-WHERE#>
.
3. Алиас можно переопределить - переопределение вступает в силу в порядке входного потока:<#LP-ALIAS A/B: C#>
<#LP A/B: это попадает в C#>
<#LP-ALIAS A/B: D#>
<#LP A/B: это попадает в D#>
<#LP-ALIAS A: B#>
<#LP-ALIAS A/C: D#> A/C и B/C означают D (здесь A - буквальное имя и алиас, B - настоящее имя без алиаса)
<#LP-ALIAS A/C/E: F#> A/C/E и B/C/E означают D/E
<#LP-ALIAS A: G#>
<#LP-ALIAS A/C: H#> A/C и G/C теперь означают H, A/C/E означает H/E, B/C всё ещё означает D!
Настоящие имена элементов к этому моменту - B, D, D/E, F, G
<#ref ...#>
,<#LP A/B: контент#>
<#LP-ALIAS A/B: C#> это будет пропущено с выдачей предупреждения
<#LP-ALIAS A: D#> и это тоже
ограничение, однако, не распространяется на неиспользованные имена под A/B:
<#LP-ALIAS A/B/C: C#>
и само A/B всё ещё может быть целью алиаса:
<#LP-ALIAS E: A/B#>
<#LP E: это попадает в A/B#>
<#LP-ALIAS E: C#> E может быть переопределено, т. к. оно с самого начала было алиасом
E
как алиаса, больше нельзя использовать или ссылаться на элемент с настоящим именем E
, или какие-либо его подэлементы, в данном
входном файле - E
будет всегда обозначать элемент, на который в данный момент настроен алиас E
. Таким образом, данное ограничение вводится, чтобы предотвратить перемешивание алиасов и настоящих имён
неочевидно трактуемым образом.<#LP-MACRO Mac %tag1 %tag2: контент макроса#>
<#LP test-node Mac: тестовый контент#>
то же самое, что:
<#LP test-node %tag1 %tag2: контент макроса тестовый контент#>
<#LP test-node: тестовый контент <#. Mac#> ещё тестовый контент #>
то же самое, что:
<#LP test-node: тестовый контент <#. %tag1 %tag2: контент макроса#> ещё тестовый контент #>
<#LP test-node: тестовый контент <#Mac#>#>
то же самое, что:
<#LP test-node %tag1 %tag2: контент макроса тестовый контент#>
<#LP-MACRO %is-a-class %class: <#LP-TAG-ON classes#> <#LP ./%title: Заголовок класса: #> #>
...
<#LP ClassA %class: это класс A, мы добавили его в <#ref classes#>, используя макрос
<#./%title: A#> <-#получим %title = "Class title: A"#>
#>
LP-ALIAS
или другой LP-MACRO
, хотя обычно в этом нет большого смысла, и следует быть осторожными с этими вариантами, если всё-таки решитесь их использовать.
Имейте в виду, что уровень имени внутри макроса (т. е., место, которое обозначает <# . #>
, или откуда начинается поиск при разрешении имени) - тот узел, в который идёт вставка, а не сам макрос.<#LP-MACRO where-am-I: <#LP-TRACE-WHERE#>#>
<#LP A where-am-I: внутри A#>
<#LP B where-am-I: внутри B#>
LP-ALIAS
(
✖ Алиасы имён
), нельзя определить макрос под тем фактическим полным именем, которое уже использовалось в текущем входном файле одним из следующих способов:<#ref ...#>
,<#LP-MACRO Mac: контент макроса#>
<#LP Mac/I: может сработать не так, как вы ожидаете#>
[
{ nameAlias: "M", name: "domain.logipard" },
{
docModel: {
{
name: "DocMain",
query: [
...
],
sort: { ... }
}
},
forEachItem: [
...
]
},
{
docModel: {
{
name: "DocReadme",
query: [
...
],
sort: { ... }
}
},
forEachItem: [
...
]
},
...
]
{...запрос}
.{
byMember: "member-name",
keyFormat: "lexical|natural",
order: "asc|desc"
}
{...сортировка}
.Имя
| Описание
|
---|---|
✖ byMember
|
Строка. Задаёт краткое имя члена, который будет использоваться как ключ сортировки. Ключ состоит из контента члена, интерпретируемого как простой текст, с удалением пробелов в начале и конце.
Предполагается, что в контенте члена не будет содержаться вложенной разметки LP, в противном случае фактическое значение ключа не гарантируется. Сравнение ключа чувствительно к регистру.
|
✖ keyFormat
|
Строка, необязательно (по умолчанию
lexical ). Может быть lexical или natural :
|
✖ order
|
Строка, необязательно (по умолчанию
asc ). Может быть asc (для сортировки по возрастанию) или desc (для сортировки по убыванию). |
lexical
). Может быть lexical
или natural
:lexical
: ключи сравниваются как строки (через лексикографическое сравнение строк).natural
: ключи разбиваются (и сравниваются пофрагментно) на последовательности из числовых и нечисловых фрагментов, где пары сегментов числовой/числовой сравниваются как числа,
а нечисловой/числовой и нечисловой/нечисловой сравниваются как строки. Т. е., 1.2.3-a
и 1.10-z
сравниваются как [1, ".", 2, ".", 3, "-a"]
и [1, ".", 10, "-z"]
, первые различающиеся сегменты -
2
и 3
, которые являются случаем числовой/числовой, 2
меньше, поэтому 1.2.3-a
меньше 1.10-z
. Если ключ начинается с +
или -
, за которым следует цифра, этот +
/-
считается частью числа
в первом сегменте, который считается числовым.item1
, item2
, ...item10
, ...,1
, -2
, 3.14
,1.0.3
{ nameAlias: "ItemAliasName", name: "строка-имени"}
, где строка-имени
- полное имя FDOM (возможно начинающееся с определённого ранее алиаса).
Имя алиаса элемента должно быть корректным кратким именем FDOM.{ queryAlias: "QueryAliasName", query: {...запрос}}
.{ conditionAlias: "?CondAliasName", condition: {...условие}}
(использование префикса ?
- необязательная конвенция).{ collectionAlias: "CollAliasName", collection: {...коллекция}}
. Это постоянный алиас, который будет общим для последующих запросов,
в отличие от локального алиаса в пределах запроса, который действует только до окончания текущего запроса (
✖ Задать локальный алиас коллекции ["alias ..."]
).{
docModel: {
name: "DocumentModelName",
rootItems: {
query: {...запрос},
sort: {...сортировка}
},
excludeUnder: {...коллекция}, // необязательно
whitelistUnder: {...коллекция} // необязательно
},
forEachItem: [
... // список инструкций спецификации человекочитаемого контента элемента
]
}
Имя
| Описание
|
---|---|
✖ forEachItem
|
one of listed in
✖ Команды определения контекста
(be sure you don't assign aliases with conflicting names), or one of the instructions
listed in this section.
|
✖ docModel
|
Задать имя модели документа и набор FDOM-элементов для включения в эту модель.
|
"member-field-name"
: любое краткое имя FDOM (обратите внимание, что строки, начинающиеся с %%
и #
, зарезервированы для других инструкций и не попадают в этот вариант).
Предписывает выдать непосредственный контент заданного поля-члена текущего элемента, без (под)членов, или не выдавать ничего, если члена с таким именем нет."#text:...arbitrary string..."
: выдать обычный текст, следующий за префиксом #text:
, со стилем обычного текста."%%title"
: выдать человекочитаемый заголовок текущего элемента (контент его члена %title
, или краткое имя элемента, если %title
отсутствует), со стилем выделенного заголовка
(или как интерактивный элемент-заголовок, если это применимо в рендерере). В общем случае, не требуется давать этой инструкции явно - заголовок выдаётся автоматически,
если у элемента не приватное имя (краткое имя, начинающееся с #
)."%%brief"
: выдать краткую часть непосредственного контента элемента (его первый параграф, если он не блок кода или элемент списка), в стиле общего текста."%%debrief"
: выдать часть непосредственного контента элемента, оставшуюся после %%brief
, в стиле общего текста. Инструкция "%%brief"
и следующая за ней инструкция
"%%debrief"
в сумме выдают полный непосредственный контент элемента."%%refTitle"
: выдать заголовок текущего элемента, в стиле общего текста, завёрнутый во внутристраничную ссылку (Logipard-ссылку, если это применимо в рендерере).
Данная инструкция сама по себе не имеет особого смысла, т. к. ссылки из непосредственного контента элемента на сам этот элемент нерабочие по построению - обычно она используется
в связке с #item
(см. далее)."%%more-start"
: эта инструкция отмечает место, на котором заканчивается контент элемента, показываемый в кратком режиме просмотра. Всё ниже будет должно быть видимо только в режиме
полного просмотра (или после переключения в него). Эта инструкция может быть использована только один раз за всю секцию forEachTeam
."%%mark-for-toc"
: эта инструкция показывает, что данный элемент следует включить в дерево оглавления (если такое применимо в рендерере).
По умолчанию, элемент не отмечается для включения в оглавление, и следует позаботиться о том, чтобы туда включались только достаточно значимые элементы, иначе оглавление получится перегруженным.
Не требуется отмечать каждый уровень в ветке дерева - оно сокращается до только отмеченных элементов (т. е., если отмечены только элемент и его прародительский элемент,
то в оглавлении элемент будет представлен как непосредственный член своего прародителя)."#item:...spec..."
: любой вышеперечисленный вариант, кроме %%more-start
и %%mark-for-toc
, после префикса #item:
- например, #item:%%title
,
#item:%%refTitle
, #item:fieldName
, и т. п. Эту инструкцию не допускается размещать отдельно - только внутри инструкции emitAsItems...
(см. ниже), и она относится
к текущему элементу итерируемой подколлекции.{
on: {...коллекция},
query: {...запрос},
as: "ResultAlias"
}
docModel
.Имя
| Описание
|
---|---|
✖ on
|
Коллекция, с которой начинается запрос (как описано в
✖ Коллекции
). Кроме того, если команда используется внутри
docModel , определён
алиас имени "%%self" , который можно использовать в качестве поля on или внутри запроса - он означает текущий элемент. |
✖ query
|
Запрос для выполнения, над
on в качестве исходной коллекции. |
✖ as
|
Строка. Алиас, задаваемый для полученной коллекции (заменяет одноимённый, определённый ранее, и остаётся в силе на выполняемых далее инструкциях, включая итерации
forEachItem для последующих элементов,
учитывайте это во избежание эффектов, зависимых от порядка выполнения). |
{
ifNotEmpty: {...коллекция},
then: [
...
]
}
docModel
.{
with: {...коллекция},
sort: {...сортировка},
emitAsItemsTable: [
[ "column-header-spec", "column-content-spec" ],
...
]
}
Имя
| Описание
|
---|---|
✖ with
|
Коллекция (как описано в
✖ Коллекции
).
|
✖ sort
|
Необязательно. Спецификация сортировки (как указано в
✖ Спецификация сортировки
) для применения на коллекции
with при её выводе в данную таблицу. |
✖ emitItemsAsTable[]
|
Массив. Спецификация колонок таблицы. Каждый элемент задаёт колонку, в порядке слева направо, как двухэлементный подмассив:
|
Имя
| Описание
|
---|---|
✖ [0]
|
Строка. Заголовок колонки. Интерпретируется как
✖ Строка (текст, ссылки на поля, и т. д.)
.
|
✖ [1]
|
Строка. Содержимое колонки. Интерпретируется как
✖ Строка (текст, ссылки на поля, и т. д.)
, причём элемент для префикса
#item: - это элемент коллекции, предназначенный для данной строки таблицы. |
{
with: {...коллекция},
sort: {...сортировка},
emitAsItemsList: [ "fragment-1-spec" [, "fragment-2-spec", ...] ]
}
Имя
| Описание
|
---|---|
✖ with
|
Коллекция (как описано в
✖ Коллекции
).
|
✖ sort
|
Необязательно. Спецификация сортировки (как описано в
✖ Спецификация сортировки
) для применения на коллекции
with при её выводе в данный список. |
✖ emitItemsAsList[]
|
Массив. Спецификация фрагментов, составляющих строку списка, в перечисленном порядке.
Каждый фрагмент - строка, интерпретируемая как
✖ Строка (текст, ссылки на поля, и т. д.)
, причём элемент для префикса
#item: - это элемент коллекции, предназначенный для данной строки списка. |
{
with: {...коллекция},
sort: {...сортировка},
emitAsOwnItems: "basic|full"
}
lpgwrite-example
, элемент может быть выдан несколько раз в разных местах документа, но только одно из этих мест считается "домашним" местом расположения. Формат документа может полагать его, например,
настоящим местом для размещения информации об элементе, а в других местах просто оставлять ссылку на это место (но может также и игнорировать данную подсказку).emitAsOwnItems
/emitAsExtItems
, lpgwrite-example
выбирает одно из них как домашнее место размещения, при этом места из emitAsOwnItems
имеют больший
приоритет быть выбранными.Имя
| Описание
|
---|---|
✖ with
|
Коллекция (как описано в
✖ Коллекции
).
|
✖ sort
|
Необязательно. Спецификация сортировки (как описано в
✖ Спецификация сортировки
) для применения на коллекции
with при её выводе в данный список. |
✖ emitAsOwnItems
|
Строка. Указывает рекомендуемый информационный режим для элементов, выдаваемых по данной инструкции. Может быть одним из следующих...
|
{
with: {...коллекция},
sort: {...сортировка},
emitAsExtItems: "basic|full"
}
lpgwrite-example
, элемент может быть выдан несколько раз в разных местах документа, но только одно из этих мест считается "домашним" местом расположения. Формат документа может полагать его, например,
настоящим местом для размещения информации об элементе, а в других местах просто оставлять ссылку на это место (но может также и игнорировать данную подсказку).emitAsOwnItems
/emitAsExtItems
, lpgwrite-example
выбирает одно из них как домашнее место размещения, при этом места из emitAsOwnItems
имеют больший
приоритет быть выбранными.Имя
| Описание
|
---|---|
✖ with
|
Коллекция (как описано в
✖ Коллекции
).
|
✖ sort
|
Необязательно. Спецификация сортировки (как описано в
✖ Спецификация сортировки
) для применения на коллекции
with при её выводе в данный список. |
✖ emitAsOwnItems
|
Строка. Указывает рекомендуемый информационный режим для элементов, выдаваемых по данной инструкции. Может быть одним из следующих...
|
Имя
| Описание
|
---|---|
✖ name
|
Строка. Имя модели, которое будет использоваться для ссылки на эту модель в конфигурации рендерера (см.
✖ docModel
).
|
✖ rootItems
|
Начальный срез, с которого начинается включаемый набор. Набор корневых элементов получается через запрос и добавляется в список элементов. Далее набор расширяется, чтобы включать
все элементы, на которые есть ссылка (
✖ Встроенные ссылки
) из (или будут выдаваться в качестве подэлементов) элементов, уже имеющихся в списке - и так далее,
последовательным расширением дерева из охваченных элементов. Далее этот набор можно сократить (см.
✖ excludeUnder
,
✖ whitelistUnder
).
|
✖ excludeUnder
|
Коллекция корневых элементов для рекурсивного исключения из начального набора после
✖ rootItems
. Если задана коллекция
excludeUnder , то
все элементы, находящиеся по FDOM-членству в поддереве любого из этих элементов (включая их самих), исключаются из документа и любых списков/таблиц, основанных на коллекциях, а встроенные ссылки на такие элементы
являются нерабочими. |
✖ whitelistUnder
|
Коллекция корневых элементов для фильтрации по "белому списку" в начальном наборе после
✖ rootItems
. Если задана коллекция
whitelistUnder , то
все элементы, которые не находятся по FDOM-членству в поддереве какого-либо из этих элементов (включая их самих), исключаются из документа и любых списков/таблиц, основанных на коллекциях, а встроенные ссылки на такие элементы
являются нерабочими. |
Имя
| Описание
|
---|---|
✖ query
|
Запрос, выдающий корневые элементы. Исходная текущая коллекция для этого запроса - пустая, поэтому, чтобы он имел смысл, его следует начинать с базового запроса
{ with: ... }
(см.
✖ <QuerySpec>
). |
✖ sort
|
Спецификация сортировки, чтобы определить относительный порядок корневых элементов в результирующем документе. Обратите внимание, что это порядок только для верхнего уровня:
все подэлементы будут выдаваться после содержащего элемента и перед следующим элементом верхнего уровня, а упорядочивание в подэлементах задаётся уже в соответствующимх
инструкциями выдачи.
|
excludeUnder
, то
все элементы, находящиеся по FDOM-членству в поддереве любого из этих элементов (включая их самих), исключаются из документа и любых списков/таблиц, основанных на коллекциях, а встроенные ссылки на такие элементы
являются нерабочими.excludeUnder
является противоположным
✖ whitelistUnder
по смыслу, и обычно их не следует использовать вместе. Однако, если всё же использовать, то excludeUnder
применяется первым.whitelistUnder
, то
все элементы, которые не находятся по FDOM-членству в поддереве какого-либо из этих элементов (включая их самих), исключаются из документа и любых списков/таблиц, основанных на коллекциях, а встроенные ссылки на такие элементы
являются нерабочими.whitelistUnder
является противоположным
✖ excludeUnder
по смыслу, и обычно их не следует использовать вместе. Однако, если всё же использовать, то excludeUnder
применяется первым.file
(см.
✖ file(...): вставка значения из файла JSON/LPSON
) с некоторыми добавленными параметрами, как показано ниже; она определяет модель документа с именем DocMain
: ...
lpgwrite-example: {
...,
program: file("${LP_HOME}/lpgwrite-example-docprg.lpson" $, {
docprgPrologue: [ ... ], // инструкции для вставки в начало
docRootItems: {...запрос},
LS_EXTENDS: "Расширяет (является)",
LS_MEMBERS: "Члены",
LS_NAME: "Имя",
LS_DESCRIPTION: "Описание",
LS_MEMBERS_FROM_EXTENTS: "Члены из расширеяемых",
LS_ARGUMENTS: "Параметры",
LS_RETURNS: "Возвращает:",
LS_ERRORS: "Ошибки:",
LS_MEMBERS_DETAILED: "Члены (подробно)",
LS_MEMBERS_FROM_EXTENTS_DETAILED: "Члены из расширяемых (подробно)",
LS_ARGUMENTS_DETAILED: "Параметры (подробно)",
LS_NOTES: "Примечания",
LS_PROPERTIES: "Свойства",
LS_PROPERTIES_FROM_EXTENTS: "Свойства из расширяемых",
LS_METHODS: "Методы",
LS_METHODS_FROM_EXTENTS: "Методы из расширяемых"
}),
renders: [
{
docModel: "DocMain",
renderer: "${LP_HOME}/lpgwrite-example-render-html" $,
...
},
{
docModel: "DocMain",
renderer: "${LP_HOME}/lpgwrite-example-render-md" $,
...
},
...
]
}
%extends
или %.extends
(сам этот член не должен содержать никакого контента, только эти добавляемые теги).%member
или %.member
. Возможно совмещать его с тегами %property
/%.property
или %method
/%.method
.%arg
или %.arg
.%return
предполагается описанием этого значения и добавляется в отведённую для этого секцию документации. Член %return
не должен иметь заголовка.%errors
предполагается описанием этих ошибок и добавляется в отведённую для этого секцию документации. Член %errors
не должен иметь заголовка.%errors
может иметь подчлены, даже помеченные тегами %member
.lpgwrite-example-docprg
включает в оглавление только элементы, которые не помечены %arg
, %member
, %method
или %property
, и имеют "публичные" краткие имена (не начинающиеся с %
или #
).
Если у вас есть такой элемент, но вы хотите всё равно включить его в оглавление, добавьте ему тег %for-toc
.%method
или %.method
. Возможно совмещать его с тегами %member
/%.member
или %property
/%.property
.%title
для указания полного человекочитаемого имени.%property
или %.property
. Возможно совмещать его с тегами %member
/%.member
или %method
/%.method
.элемент/%note
следующим образом:#LP main-item {
Контент основного элемента
#LP ./%note/~ {
#LP контент примечания 1 (внутри элемента), с членом
#LP ./note-1-member: член примечания 1
#LP }
<#LP ./%note/~: контент примечания 2 (внутри элемента), простого#>
Ещё контент основного элемента
#LP }
#LP main-item/%note/~: контент примечания 3 (снаружи элемента)
%notes
- появляется больше вариантов для контроля за порядком их следования в документе. Добавление непосредственно к контенту
элемента из разных исходников не гарантирует, в каком порядке в итоге будут следовать фрагменты, и могут даже нарушиться ваши предположения о том, что будет считаться краткой частью информации для данного
элемента. С другой стороны, место расположения секции 'Примечания' чётко определено, и на члены %notes
под ней действуют подсказки %order
(
✖ %order: контроль порядка следования
).%note
и его неанонимные члены не используются. В целях разделения обязанностей, для такого есть отдельная возможность с несколько другим предполагаемым применением -
см.
✖ extra
.%extra
можно указать контент, который будет показан после основного контента элемента внутри текста, так же, как если бы он был написан в конце самого контента элемента.
Конкретнее говоря, он ведёт себя как дополнительный подэлемент без заголовка, следующий перед секцией с подробным описанием членов элемента (и перед секцией 'Примечания', если таковая имеется), что
выглядит как продолжение контента основного элемента. Предполагаемое использование - через добавление контента и членов к item/%extra
следующим образом:#LP main-item {
Контент основного элемента
#LP ./%extra: непосредственный контент extra для основного элемента
#LP ./%extra/A %member: член A в extra основного элемента (внутри элемента)
Ещё контент основного элемента
#LP ./ownMember %member: собственный член основного элемента
#LP }
#LP main-item/%extra/B %member {
член B extra основного элемента (снаружи элемента)
#LP ./extra-B-member: дополнительный член элемента B
#LP }
main-item
:# main-item
Контент основного элемента
Члены:
ownMember | собственный член основного элемента
// конец краткого описания main-item
Ещё контент основного элемента
// здесь начинаются данные из %extra
непосредственный контент extra для основного элемента
Члены: // от %extra
A | член A в extra основного элемента (внутри элемента)
B | член B extra основного элемента (снаружи элемента)
Члены (подробно): // of %extra
# A
член A в extra основного элемента (внутри элемента)
# B
член B extra основного элемента (снаружи элемента)
# extra-B-member
дополнительный член элемента B
// здесь заканчиваются данные из %extra
Члены (подробно): // от main-item
# ownMember
собственный член основного элемента
%extra
- контроль порядка показа членов в пограничных случаях.lpgwrite-example-docprg
размещает непосредственные члены элемента, помеченные тегами %member
, %arg
, %return
, %errors
, и аналогичные из расширяемых элементов,
в конце краткого описания элемента, но перед остальной частью контента. Иногда это может нарушить связность информации (например, фрагмент кода с ограничителями, представляющий общий вид элемента,
которые лучше разместить перед списком членов). Чтобы обойти это неудобство, можно переместить список членов, аргументов и т. д. из самого элемента в члены его элемента ./%extra
. Они всё ещё
будут выглядеть "встроенными" в общий текст, но логически уже будут частью отдельного элемента, и не будут участвовать в общем порядке показа частей основного элемента.lpgwrite-example-docprg
задаются через добавленные контекстные переменные в LPSON-операторе file
:Имя
| Описание
|
---|---|
✖ docprgPrologue
|
Массив инструкций для добавления в самом начале документационной программы. Обычно - определения алиасов для применения в
✖ docRootItems
.
|
✖ docRootItems
|
Секция
rootItems (see
✖ rootItems
) генерируемой модели (DocMain ). Этот объект будет задан как rootItems
полностью, как написан, без дополнений и обёрток, так что пользователь не должен полагаться здесь на какие-либо умолчания. |
✖ localization
|
Группа предопределённых заголовков, используемых в сгенерированной странице. Вынесены в параметры, чтобы сделать их локализуемыми в этой точке. Этот объект - словарь строк, с именами членов, означающих смысл
каждой строки:
|
%extends
list (т. е. добавленных тегами к члену %extends
)%member
%member
внутри элементов из списка %extends
, на всю глубину дерева "расширяемых"%property
%property
внутри элементов из списка %extends
, на всю глубину дерева "расширяемых"%method
%method
внутри элементов из списка %extends
list, на всю глубину дерева "расширяемых"%arg
%return
%error
%member
%member
, определёнными в элементах из списка %extends
, на всю глубину дерева "расширяемых"%arg
%notes
[D]
для указания, что это заменители по умолчанию, которые лучше исправить.null
-значением).
Если элемент в движке данного считывателя - это ресурс, который требуется явным образом освободить после использования, документация по реализации должна подчеркнуть это, указать время жизни для объекта,
и должен быть предоставлен способ освобождения.Имя
| Описание
|
---|---|
✖ .content
|
Свойство только для чтения. Контент элемента (текст, внутритекстовые ссылки, и всё прочее, что поддерживает модель, обслуживаемая данным считывателем.)
|
✖ .name
|
Свойство только для чтения, строка. Полный путь элемента (без именных алиасов).
|
✖ .shortName
|
Свойство только для чтения, строка. Краткое имя элемента (последний сегмент полного имени-пути).
|
✖ .tags
|
Свойство только для чтения. Коллекция тегов элемента.
|
✖ .members
|
Свойство только для чтения. Коллекция членов элемента.
|
✖ .isNull
|
Свойство только для чтения, булевое. Проверка, что элемент пустой (true) или нет (false).
|
✖ .parent
|
Свойство только для чтения,
✖ <Item>
. Возвращает родительский элемент (тот, членом которого является данный). Для корневого элемента
возвращает null (именно null-значение, а не нулевой элемент).
|
✖ .isConditionTrue(lpqCtx, condSpec)
|
Проверить, что элемент удовлетворяет некоторому условию; проверка должна делаться в рамках заданного контекста запроса (чтобы иметь возможность использовать алиасы условий и коллекций).
|
{ ref: <Item>, text: string }
(ref - это значение типа
✖ <Item>
), для внутритекстовой ссылки на другой элемент. Текст - это альтернативный видимый текст для данной ссылки, если он не пустой, то
рекомендуется использовать его, а не заголовок ссылаемого элемента по умолчанию.Имя
| Описание
|
---|---|
✖ .nameAlias(aliasName, item)
|
Задать алиас имени элемента (который должен быть корректным кратким именем), могущий впоследствии использоваться как самостоятельное имя элемента, или как начальный сегмент для другого имени элемента в пределах данного
✖ <QueryContext>
.
Поведение в случае уже существующего алиаса с таким же именем зависит от реализации.
|
✖ .collectionAlias(aliasName, ...collectionSpecs)
|
Задать именованный алиас коллекции, который может быть использован позднее для ссылки на эту коллекцию в пределах данного контекста (
✖ <CollectionSpec>
). Коллекция строится из коллекций, соответствующих каждому элементу
списка спецификаций. Алиас - постоянный в пределах данного контекста, в отличие от локального алиаса (
✖ Задать локальный алиас коллекции ["alias ..."]
).
|
✖ .queryAlias(aliasName, ...querySpecs)
|
Задать именованный алиас запроса, который может быть использован позднее для ссылки на этот запрос в пределах данного контекста (
✖ <QuerySpec>
). Список трактуется как составной запрос.
|
✖ .conditionAlias(aliasName, condSpec)
|
Задать именованный алиас условия, который может быть использован позднее для ссылки на это условие в пределах данного контекста (
✖ <Condition>
).
|
✖ .item([baseItem ,] name)
|
Вернуть элемент по заданному имени-пути, полному или относительно заданного базового элемнета. Первый краткоимённый сегмент полного имени может быть алиасом имени, определённым в данном
✖ <QueryContext>
.
|
✖ .collection(...collectionSpecs)
|
Возвращает коллекцию, заданную списком спецификаций элементов коллекции. Каждый элемент списка -
✖ <CollectionSpec>
.
|
✖ .with(...collectionSpecs)
|
Задать текущую коллекцию для последующего запроса (вызова
✖ .query(...querySpecs)
). Коллекция строится из коллекций, соответствующих каждому элементу списка.
.with , по сути, инициирует цепочку запроса, но может быть использован и в её середине, чтобы заменить текущую коллекцию на некотором шаге. |
✖ .query(...querySpecs)
|
Выполнить запрос, или список запросов, трактуемый как составной запрос, с учётом того, что текущая коллекция задана предыдущим
✖ .with(...collectionSpecs)
или получена от предыдущих вызовов
.query .
Обратите внимание, что полученная коллекция не возвращается немедленно, а становится новой текущей коллекцией. |
✖ .teardownCollection()
|
Завершить запрос и вернуть результат (текущую коллекцию на момент вызова). Сама текущая коллекция сбрасывается, так что следующий запрос должен быть инициализирован повторно, начиная с
✖ .with(...collectionSpecs)
.
|
✖ .currentCollectionAlias(aliasName)
|
Задать именованный алиас коллекции для текущей коллекции, который может быть использован позже для ссылки на эту коллекцию в пределах данного контекста (
✖ <CollectionSpec>
). Может быть использован только
в процессе запроса (пока имеет значение текущая коллекция), в противном вызов данного метода - ошибка. Это локальный алиас запроса, в отличие от постоянного (
✖ Задать локальный алиас коллекции ["alias ..."]
).
|
✖ .compileQuery(...querySpecs)
|
Скомпилировать запрос в объект-дескриптор, который может быть использован позже для ссылки на этот запрос в пределах данного контекста (
✖ <QuerySpec>
). Список трактуется как составной запрос.
|
Имя
| Описание
|
---|---|
✖ aliasName
| Имя алиаса, строка.
|
✖ item
|
Алиасируемый элемент. Строка (полное имя-путь, возможно с участием другого алиаса) или
✖ <Item>
.
|
Имя
| Описание
|
---|---|
✖ baseItem
|
Необязательный параметр. Базовый элемент, от которого строится путь
✖ name
.
✖ <Item>
, строка или массив строк.
|
✖ name
|
Путь к элементу. Строка или массив строк. Может начинаться с алиаса имени, определённого в данном
✖ <QueryContext>
.
|
some/path
, а заданный baseItem
имеет путь base/item
, то полученный элемент ищется по пути base/item/some/path
..query
.
Обратите внимание, что полученная коллекция не возвращается немедленно, а становится новой текущей коллекцией.Имя
| Описание
|
---|---|
✖ .size
|
Свойство только для чтения, число. Размер коллекции (сколько в ней элементов).
|
✖ .contains(item)
|
Проверить, содержит ли коллекция данный элемент. Должен быть false для любого нулевого (с
✖ .isNull
= true) элемента.
|
✖ [Symbol.iterator]
|
Коллекция должна быть JS-перечисляемым объектом, выдавая содержащиеся
✖ <Item>
в некотором порядке (
for (var item of collection) ).
Рекомендуется, чтобы реализация сохраняла элементы, объявленные в одном и том же исходнике, в том же порядке, в каком они объявлены, но пользователю не рекомендуется полагаться на это допущение. |
for (var item of collection)
).
Рекомендуется, чтобы реализация сохраняла элементы, объявленные в одном и том же исходнике, в том же порядке, в каком они объявлены, но пользователю не рекомендуется полагаться на это допущение.{ isAnyOf: <CollectionSpec> }
: условие типа isAnyOf
{ hasMembersNamed: <string | RegExp> }
: условие типа hasMembersNamed
, регулярное выражение может быть задано как RegExp
-объект JS (не следует использовать флагов, кроме i
) или как исходная строка с регулярным выражением (с предполагаемым отсутствием флагов){ hasMembersThat: <Condition> }
: условие типа hasMembersThat
{ hasAnyOfTags: <CollectionSpec> }
: условие типа hasAnyOfTags
{ hasAllOfTags: <CollectionSpec> }
: условие типа hasAllOfTags
{ hasParentThat: <Condition> }
: условие типа hasParentThat
{ named: <string | RegExp> }
: условие типа named
, регулярное выражение может быть задано как RegExp
-объект JS (не следует использовать флагов, кроме i
) или как исходная строка с регулярным выражением (с предполагаемым отсутствием флагов){ and: [ ...<Condition> ] }
: условие типа and
, аргумент - массив объектов <Condition>
{ or: [ ...<Condition> ] }
: условие типа or
, аргумент - массив объектов <Condition>
{ not: <Condition> }
: условие типа not
{ union: [nestedCollectionSpecs] }
: объединение коллекций, заданное массивом элементов, каждый из которых - тоже элемент спецификации коллекции (возможна вложенность произвольного уровня, но
обратите внимание, что элементы на первом уровне списка union
трактуются как операнды для операции объединения, а не составные элементы списка){ intersect: [nestedCollectionSpecs] }
: пересечение коллекций, заданное массивом элементов, каждый из которых - тоже элемент спецификации коллекции (возможна вложенность произвольного уровня, но
обратите внимание, что элементы на первом уровне списка intersect
трактуются как операнды для операции пересечения, а не составные элементы списка){ subtract: [nestedCollectionSpecs] }
: разность коллекций, заданная массивом элементов (вычитание 2-го и последующиъ элементов из 1-го элемента), каждый из которых - тоже элемент спецификации коллекции
(возможна вложенность произвольного уровня, но что элементы на первом уровне списка subtract
трактуются как операнды для операции вычитания, а не составные элементы списка)[ ...<QuerySpec> ]
: массив спецификаций запроса, составной запрос, где компоненты применяются в порядке перечисления{ alias: string }
:
✖ Задать локальный алиас коллекции ["alias ..."]
, имя задаваемого алиаса задаётся строкой{ with: <CollectionSpec> }
:
✖ Заменить текущую коллекцию ["with ..."]
{ membersThat: <Condition>, on?: <CollectionSpec>, recursive?: boolean }
:
✖ Выбрать члены, удовлетворяющие условию ["membersThat ..."]
{ tagsThat: <Condition>, on?: <CollectionSpec>, recursive?: boolean }
:
✖
Выбрать теги с элементов коллекций, удовлетворяющие условию ["tagsThat ..."]
{ inMembersThat: <Condition>, query: [ ...<QuerySpec> ], on?: <CollectionSpec>, recursive?: boolean }
:
✖
Выполнить подзапрос на тех членах элементов коллекции, которые удовлетворяют условию ["inMembersThat ..."]
{ inTagsThat: <Condition>, query: [ ...<QuerySpec> ], on?: <CollectionSpec>, recursive?: boolean }
:
✖
Выполнить подзапрос на тех тегах от элементов коллекции, которые удовлетворяют условию ["inTagsThat ..."]
{ inItemsThat: <Condition>, query: [ ...<QuerySpec> ], on?: <CollectionSpec>, recursive?: boolean }
:
✖
Выполнить подзапрос на элементах коллекции, удовлетворяющих условию ["inItemsThat ..."]
{ subtractQuery: [ ...<QuerySpec> ], on?: <CollectionSpec> }
:
✖
Вычитание результата подзапроса из текущей коллекции ["subtractQuery ..."]
{ unionQuery: [ ...<QuerySpec> ], on?: <CollectionSpec> }
:
✖
Объединение результата подзапроса с текущей коллекцией ["unionQuery ..."]
{ intersectQuery: [ ...<QuerySpec> ], on?: <CollectionSpec> }
:
✖
Пересечение результата подзапроса с текущей коллекцией ["intersectQuery ..."]
{ sideQuery: [ ...<QuerySpec> ], on?: <CollectionSpec> }
:
✖ Выполнить подзапрос, не меняя текущую коллекцию ["sideQuery ..."]
true
, false
, и null
.
Всё это возможно также и в LPSON (плюс некоторые расширенные возможности для списков и строк), плюс ещё некоторые варианты:vars
: результат - словарь переменныхfile(...)
: результат - значение, распарсенное из заданного файла JSON/LPSON.fieldName
, или ."fieldName"
, или .(field-name-expr)
, результат - значение заданного поля из предшествующего значения$
, результат - предыдущее значение-строка, в котором фрагменты вида ${var-name}
заменяются значениями соответствующих
контекстных переменных{ ... }
), результат - этот словарь с добавленным ключом "@type"
и предыдущим значением в качестве значения
для этого ключаvars.objectField1."objectField2".("objectField${THREE}" $).stringField { value: 123 }
). Они вычисляются слева направо с одним и тем же приоритетом.file(name-value)
или file(name-value, extra-vars-dictionary-value)
. Оно распарсивает и вычисляет LPSON-значение
из заданного файла, добавляя/заменяя в словаре контекста заданный набор допонительных переменных. Модифицированный словарь будет действовать только
для выражений (
✖ 'vars': словарь контекстных переменных
) в дочернем контексте внутри вставляемого файла; контекстные переменные самого текущего файла не меняются.Имя
| Описание
|
---|---|
✖ name-value
|
значение, результат которого - имя файла. Относительные имена задаются относительно каталога с текущим файлом
(т. е.,
file("xxx.lpson") изнутри yyy/zzz.lpson означает файл yyy/xxx.lpson ). |
✖ extra-vars-dictionary-value
|
значение, результат которого - словарь. Ключи - имена контекстных переменных для добавления/замены в дочернем контексте,
значения - соответствующие значения для этих переменных.
|
"abc"
, JSON-совместимая форма), в одинарных кавычках ('abc'
), или с ограничителями из обратных кавычек (`...`abc`...`
).\n
, \"
, \\
, \n
, \r
, \t
, \uXXXX
и т. п.). Кавычки неподходящего типа
('
в "..."
и "
в '...'
) можно оставить неэкранированными. Прямые переводы строки (непосредственные или экранированные) в строках с кавычками не допускаются.перед, `a`, после
// то же самое, что
перед, ``
a
``, после
LP_HOME
: каталог с инсталляцией работающего в данный момент исполнителя конвейера Logipard, с помощью этой переменной можно ссылаться на встроенные модули LPLP_PROJECT_ROOT
: корневой каталог проекта, используйте для конструкции строк, которые должны означать имена файлов относительно корня проекта (не считая имён файлов в операторе file(...)
, там
это делается автоматически)THISFILE
: путь к текущему LPSON-файлу, может быть полезно для построения имён разных элементов относительно размещения файла (например, "${vars.THISFILE}/../item_in_the_same_dir.png" $
){ "key1": value1, "key2": value2, ... }
, но имеет некоторые дополнительные возможности:A-Z
, a-z
, 0-9
(кроме первого символа) и _
, как в JS, но также +
, -
, *
, /
(кроме //
и /*
, которые считаются
началом комментариев), $
и =
. Пример:{
"jsonStyleKey": 0,
LP-style-key: 1,
/this-is+allowed=too*$: 2,
jsonStyleKey: 3 // то же самое, что "jsonStyleKey"
}
{
a: 1,
...({ b: 2, c: 3 }),
d: 4
}
{
(vars.KEY_NAME): "value"
}
"@type"
.
Пример:"string" { value: "123" }
```<lp-src file="lp-input.lp-txt"></lp-src>то же самое, что:<lp-src file="lpson.js"></lp-src>
Значение выражения может быть не только атомарным значением:
Префиксация типом работает только с литералами-словарями с правой стороны. Не разрешается ставить туда выражение (даже если это литерал-словарь в скобках).
На самом деле, этот словарь считается постфиксным оператором.
**Запятая после завершающего элемента**
Аналогично JS и большинству прочих C-подобных синтаксисов, LPSON позволяет ставить запятую после заключительного элемента словаря:
[ value1, value2, ... ]
, но с некоторыми дополнительными возможностями:[
1,
...([2, 3]),
4
}
[
"is",
"legit",
]
{ a: "value" }.a
{ a: "value" }."a"
{ a: "value" }.("a")
// результат всех этих выражений - "value"
(= выражение)
после ключа или ключа-выражения:{ a: "value" }.a (= "default value") // результат - "value"
{ b: "value" }.a (= "default value") // результат - "default value", т. к. .a не определено
null
или любое false-значение, то поле считается установленным на это значение.${varName}
- подставить значение контекстной переменной varName
, при условии, что она имеет значением строку, число или булевое.\$
(\\$
в строке с двойными кавычками) - буквальный символ $
"Program version ${version}, and this is not a \\${placeholder}" $
// если vars.version "1.0.0", то результат идентичен следующей константе: "Program version 1.0.0, and this is not a ${placeholder}"
(= выражение-словарь)
// vars.a - "value-of-a", vars.b не определено
"a is ${a}, b is ${b}" $(= { a: 1, b: 2, c: 3 })
// то же, что "a is value-of-a, b is 2"
<валидный-LP-идентификатор выражение-параметр>
.<+vars { a: 10 }>
{
innerA: "${a}",
innerA1: <+vars { a: 11 }> "${a}",
<+vars { a: 12 }>
...{ innerA3: vars.a, innerA4: "${a}" }
}
+vars
подряд, то они применяются в порядке следования, и более поздние применяются поверх более ранних:<+vars { a: 1 }>
<+vars { a: 2, b: 3 }>
"${a} ${b}" $ // "2 3"
<trace vars.a>
{
innerA: "${a}"
}
+vars
, то используемые переменные - те, что действуют после всех +vars
, предшествующих
trace
:<+vars { a: 10 }>
<trace vars.a> // 10
<+vars { a: 20 }>
vars.a // 20
WHITESPACE ::= \s+
CHARSET_COMMENT ::= //[^\S\r\n]*#charset[^\S\r\n]+([-A-Za-z0-9_]*).*
COMMENT ::= //.*
MULTILINE_COMMENT ::= /\*[\S\s].*?\*/
// пробелы и комментарии исключаются из строки токенов
// токен '#charset' чувствителен к регистру, но имя самой кодировки - нет, и дефисы в ней игнорируются,
// например, можно написать: #charset utf8, #charset UTF-8, or #charset Utf8-, и т. п.
SINGLE_QUOTE_STRING ::= '(?:\\.|[^'])*'
DOUBLE_QUOTE_STRING ::= "(?:\\.|[^"])*"
// аналогично обычному JSON, строки в кавычках не могут занимать несколько строк
FENCED_STRING ::= (?<fence>`+)(?:\s*?\n)?([\S\s]*?)(?:(?<=\n)[^\S\r\n]*)?\k<fence>
// строка с ограничителем ограничена последовательностями обратных апострофов одной и той же длины
// (начиная с одного), и могут занимать несколько строк
// все символы между ограничителями берутся как есть, включая пробелы и переводы строк,
// кроме замыкающих пробелов на строке с открывающим ограничителем и/или начальных пробелов
// на строке с закрывающим ограничителем, в случае, если ограничители - соответственно, последние/первые символы-не пробелы
// на своих строках; такие пробелы, включая переводы строк, не включаются в строку.
NUMBER ::= [-+]?(?:\d+(?:\.\d+)?(?:[eE]\d+)?|0[Xx][0-9A-Fa-f]+)
// в отличие от обычного JSON, в LPSON разрешается указывать знак '+' и использовать целочисленные шестнадцатеричные числа
PUNCTUATION ::= \.\.\.|[\(\)\[\]\{\}<>.,:]
// распознаваемые символы пунктуации LPSON: '[' ']' '{' '}' '(' ')' '<' '>' ',' ':' '.' '...'
IDENTIFIER ::= (?![0-9])(?:[-+A-Za-z_$*=]|\/(?!\/))+
// помимо цифр, букв и подчёркивания, LPSON позволяет содержание в идентификаторе также $, -, +, *, / (кроме двух последовательно идущих
// '/', которые считаются началом комментария), и =
// идентификатор не должен начинаться с цифры, или с плюса или минуса, за которым идёт цифра
ANNOTATION ::= '<' NOISE '>'
SPREAD ::= '...' NOISE
SUBEXPR ::= '(' (NOISE ',')* NOISE? ')'
LIST.ITEM ::= ANNOTATION* (SPREAD | NOISE)
LIST ::= '[' (LIST.ITEM ',')* LIST.ITEM? ']'
KEY_VALUE ::= NOISE ':' NOISE
DICTIONARY.ITEM ::= ANNOTATION* (SPREAD | KEY_VALUE)
DICTIONARY ::= '{' (DICTIONARY.ITEM ',')* DICTIONARY.ITEM? '}'
ATOMIC_VALUE ::= NUMBER | STRING | IDENTIFIER
NOISE.ITEM ::= SUBEXPR | LIST | DICTIONARY | ATOMIC_VALUE | '.'
NOISE ::= NOISE.ITEM+
// NOISE - упрощённо говоря, некоторое выражение, из которого вычисляется JSON-значение, но его дальнейшая структура не является предметом L1
LPSON_FILE ::= ANNOTATION* NOISE
// файл LPSON содержит ровно один, возможно аннотированный, символ NOISE
const { loadLpsonFile } = require('logipard/lpson');
...
var [parsedObject, errors] = await loadLpsonFile('path-to-file.lpson', { varA: "A" }); // 2й параметр - словарь переменных
if (errors.length > 0) {
console.log("Ошибки парсинга:");
console.dir(errors);
} else {
console.log("Объект распарсен успешно, обратная сериализация в JSON:", JSON.stringify(parsedObject));
}
THISFILE
.