diff --git a/.gitignore b/.gitignore index 08b2e29..313bf3f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,15 +1,9 @@ -# Compiled class file -*.class - # Log file *.log # BlueJ files *.ctxt -# Mobile Tools for Java (J2ME) -.mtj.tmp/ - # Package Files # *.jar *.war @@ -23,6 +17,10 @@ hs_err_pid* .gradle/ -.idea/ build/ gradle.properties +*.orig +/.idea/compiler.xml +/.idea/misc.xml +/.idea/vcs.xml +/.idea/workspace.xml diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..f9163b4 --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,15 @@ + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..7cdb0ad --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/LICENSE.md b/LICENSE.md index 858cc21..f60438a 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,4 +1,3 @@ - The MIT License (MIT) Copyright (c) 2023 alkoleft diff --git a/README.md b/README.md index c1533d8..253bd26 100644 --- a/README.md +++ b/README.md @@ -1 +1,95 @@ -# bsldoc +# BSL-Doc + +BSL-Doc — это инструмент для автоматической генерации документации на основе исходных кодов конфигурации 1С:Предприятие. +Он поддерживает работу с исходниками как в формате **конфигуратора**, так и в формате **EDT**. +Для анализа исходных кодов используются компоненты [bsl-language-server](https://github.com/1c-syntax/bsl-language-server), включая [bsl-parser](https://github.com/1c-syntax/bsl-parser) для работы с модулями и [mdclasses](https://github.com/1c-syntax/mdclasses) для чтения дерева метаданных. + +## Возможности + +- Генерация документации в форматах Markdown, ConfluenceMarkdown и Docusaurus. +- Поддержка фильтрации по подсистемам и регионам. +- Возможность объединения с ручной документацией. +- Гибкая настройка иерархии и структуры выходной документации. + +## Требования + +* java version 17 + +## Установка + +* Скачайте jar файл со страницы [релизов](https://github.com/1CDevFlow/BSL-Doc/releases) + +## Запуск + +### Параметры запуска + +```bash +bsldoc --format= --only-subsystems= --regions= source destination +``` + +| Параметр | Описание | Доступные значения | Значение по умолчанию | Файл | Cli | +|-----------------|------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------|------------------------------|---------------|-------------------| +| format | Формат генерируемой документации | Markdown, ConfluenceMarkdown, Docusaurus | Markdown | format | --format | +| hierarchy | Формат иерархии генерируемой документации — плоский список или иерархия по подсистемам | FLAT, SUBSYSTEM | FLAT | hierarchy | --hierarchy | +| subsystems | Подсистемы верхнего уровня, по которым строится документация. Если не указано, то по всем. | | | subsystems | --only-subsystems | +| regions | Список областей модулей, по которым строится документация. Если не указано, то по всем экспортным методам | | ПрограммныйИнтерфейс, Public | regions | --regions | +| manualDocs | Путь к каталогу с ручной документацией | | | manualDocs | --manual-docs | +| mergeStrategy | Способ объединения ручной и сгенерированной документации | APPEND, REPLACE, MERGE | APPEND | mergeStrategy | --merge-strategy | +| childLayout | Расположение файла описания групп | SUB_DIRECTORY, SAME_DIRECTORY | SAME_DIRECTORY | childLayout | --child-layout | +| header | Шаблон, добавляется в начало каждого сгенерированного файла `{path: '', content: ''}`. Либо сам шаблон, либо путь к файлу шаблона. | | | header | | +| footer | Шаблон, добавляется в конец каждого сгенерированного файла `{path: '', content: ''}`. Либо сам шаблон, либо путь к файлу шаблона. | | | footer | | + +### Пример запуска + +```bash +bsldoc --format=Markdown --only-subsystems="ОбщегоНазначения, Администрирование" --regions="ПрограммныйИнтерфейс" source_directory output_directory +``` + +## Настройка `childLayout` + +Определяет расположение описаний групп (подсистем). + +### Пример структуры файлов + +Предположим, конфигурация имеет общий модуль `СтроковыеФункции`, который относится к подсистеме `ОбщегоНазначения`. + +#### Для настройки `SAME_DIRECTORY` + +```tree +└── ОбщегоНазначения // Каталог с описанием элементов подсистемы + ├── index.md // Описание подсистемы + └── СтроковыеФункции.md // Описание общего модуля +``` + +#### Для настройки `SUB_DIRECTORY` + +```tree +├── ОбщегоНазначения.md // Описание подсистемы +└── ОбщегоНазначения // Каталог с описанием элементов подсистемы + └── СтроковыеФункции.md // Описание общего модуля +``` + +## Ручная документация + +BSL-Doc поддерживает работу с ручной документацией, что позволяет: + +- Совмещать ручные и сгенерированные статьи. +- Окружать сгенерированную документацию ручной в одной статье. +- Заменять сгенерированные страницы ручным вариантом. + +### Процесс публикации документации + +1. В каталог назначения копируется ручная документация. +2. На основании исходников генерируется документация в тот же каталог назначения. +3. При генерации проверяется существование "ручной" статьи. Если она найдена, то в зависимости от настройки `mergeStrategy`: + - `APPEND` — соответствующее описание не генерируется, существующая страница остается неизменной. + - `REPLACE` — страница заменяется сгенерированной. + - `MERGE` — описания объединяются. Страница с ручной документацией может содержать маркер `generated_content`, который заменяется сгенерированным описанием. При отсутствии маркера страница остается неизменной. + +## Лицензия + +BSL-Doc распространяется под лицензией MIT. Подробности смотрите в файле [LICENSE](LICENSE). + +## Поддержка и обратная связь + +Если у вас есть вопросы, предложения или вы нашли ошибку, пожалуйста, создайте issue на [GitHub](https://github.com/1CDevFlow/BSL-Doc). diff --git a/build.gradle.kts b/build.gradle.kts index 0c995c9..4723dca 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,62 +1,74 @@ plugins { java application - id("io.freefair.lombok") version "6.6.1" + id("me.qoomon.git-versioning") version "6.4.3" + id("com.gorylenko.gradle-git-properties") version "2.4.1" + id("io.freefair.lombok") version "8.4" id("maven-publish") id("org.springframework.boot") version "2.7.10" id("io.spring.dependency-management") version "1.1.0" + id("org.sonarqube") version "6.1.0.5360" } group = "ru.alkoleft.bsl.doc" -version = "0.1.0-SNAPSHOT" +gitVersioning.apply { + refs { + considerTagsOnBranches = true + tag("v(?[0-9].*)") { + version = "\${ref.tagVersion}\${dirty}" + } + branch(".+") { + version = "\${ref}-\${commit.short}\${dirty}" + } + } + + rev { + version = "\${commit.short}\${dirty}" + } +} -val JUINT_VERSION = "5.8.2" +val junitVersion = "5.8.2" +val jacksonVersion = "2.19.0" repositories { mavenLocal() mavenCentral() - maven(url = "https://jitpack.io") + maven("https://central.sonatype.com/repository/maven-snapshots") } java { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 -} - -java.sourceSets["main"].java { - srcDir("src/main/java") + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 } dependencies { - // spring - implementation("org.springframework.boot:spring-boot-starter") - implementation("org.springframework.boot:spring-boot-starter-websocket") - implementation("info.picocli:picocli-spring-boot-starter:4.7.1") - implementation("com.github.1c-syntax", "bsl-language-server", "0.20.0") + // CLI + implementation("info.picocli", "picocli", "4.7.5") - implementation("com.github.1c-syntax", "mdclasses", "0.10.3") - implementation("io.github.1c-syntax", "bsl-common-library", "f6714e4e") - implementation("io.github.1c-syntax", "supportconf", "0.1.1") { - exclude("io.github.1c-syntax", "bsl-common-library") - } - implementation("com.github.1c-syntax", "bsl-parser", "167aaad827322e09ccde4658a71152dad234de4b") { - exclude("com.tunnelvisionlabs", "antlr4-annotations") - exclude("com.ibm.icu", "*") - exclude("org.antlr", "ST4") - exclude("org.abego.treelayout", "org.abego.treelayout.core") - exclude("org.antlr", "antlr-runtime") - exclude("org.glassfish", "javax.json") - } + // 1c-syntax + implementation("io.github.1c-syntax", "mdclasses", "0.17.3") + implementation("io.github.1c-syntax", "utils", "0.6.8") + implementation("io.github.1c-syntax", "bsl-common-library", "0.9.2") + implementation("io.github.1c-syntax", "bsl-parser", "0.30.0") + // logging + implementation("org.slf4j", "slf4j-api", "2.1.0-alpha1") + + // config load + implementation("com.fasterxml.jackson.core:jackson-core:$jacksonVersion") + implementation("com.fasterxml.jackson.core:jackson-databind:$jacksonVersion") + implementation("com.fasterxml.jackson.core:jackson-annotations:$jacksonVersion") // template engine implementation("com.github.jknack:handlebars:4.3.1") - implementation("org.apache.velocity:velocity-engine-core:2.3") - implementation("org.apache.velocity:velocity-tools:2.0") - testImplementation("org.junit.jupiter:junit-jupiter:$JUINT_VERSION") - testImplementation("org.junit.jupiter:junit-jupiter-api:$JUINT_VERSION") - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:$JUINT_VERSION") + implementation("commons-io", "commons-io", "2.18.0") + + // tests + testImplementation("org.slf4j", "slf4j-reload4j", "2.1.0-alpha1") + testImplementation("org.junit.jupiter:junit-jupiter:$junitVersion") + testImplementation("org.junit.jupiter:junit-jupiter-api:$junitVersion") + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:$junitVersion") } tasks.test { @@ -74,41 +86,14 @@ tasks.withType { } tasks.jar { -// manifest { -// attributes["Main-Class"] = "ru.alkoleft.bsl.doc.Main" -// } enabled = true archiveClassifier.set("") } tasks.bootJar { -// manifest { -// attributes["Main-Class"] = "ru.alkoleft.bsl.doc.Main" -// } archiveClassifier.set("exec") } -tasks { - val fatJar = register("fatJar") { - dependsOn.addAll(listOf("compileJava", "processResources")) // We need this for Gradle optimization to work - archiveClassifier.set("standalone") // Naming the jar - duplicatesStrategy = DuplicatesStrategy.EXCLUDE - manifest { attributes(mapOf("Main-Class" to application.mainClass)) } // Provided we set it up in the application plugin configuration - val sourcesMain = sourceSets.main.get() - val contents = configurations.runtimeClasspath.get() - .map { if (it.isDirectory) it else zipTree(it) } + - sourcesMain.output - from(contents) - } - build { - dependsOn(fatJar) // Trigger fat jar creation during build - } - - group = "build" -} - - - publishing { repositories { maven { @@ -125,4 +110,4 @@ publishing { from(components["java"]) } } -} \ No newline at end of file +} diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 05679dc..d4081da 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.1.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip +networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 744e882..23d15a9 100755 --- a/gradlew +++ b/gradlew @@ -1,7 +1,7 @@ -#!/usr/bin/env sh +#!/bin/sh # -# Copyright 2015 the original author or authors. +# Copyright © 2015-2021 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,81 +15,115 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## -## -## Gradle start up script for UN*X -## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## # Attempt to set APP_HOME + # Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum warn () { echo "$*" -} +} >&2 die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MSYS* | MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar +CLASSPATH="\\\"\\\"" # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -98,88 +132,120 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac fi -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. # For Cygwin or MSYS, switch paths to Windows format before running java -if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) fi - i=`expr $i + 1` + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg done - case $i in - 0) set -- ;; - 1) set -- "$args0" ;; - 2) set -- "$args0" "$args1" ;; - 3) set -- "$args0" "$args1" "$args2" ;; - 4) set -- "$args0" "$args1" "$args2" "$args3" ;; - 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac fi -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=`save "$@"` -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index ac1b06f..5eed7ee 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -13,8 +13,10 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,7 +27,8 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -40,13 +43,13 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute +if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -56,32 +59,34 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail :execute @rem Setup the command line -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar +set CLASSPATH= @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal diff --git a/lombok.config b/lombok.config new file mode 100644 index 0000000..911d2ae --- /dev/null +++ b/lombok.config @@ -0,0 +1,4 @@ +lombok.anyConstructor.addConstructorProperties=true +lombok.addLombokGeneratedAnnotation=true +lombok.log.fieldName=log +lombok.extern.findbugs.addSuppressFBWarnings=false diff --git a/settings.gradle b/settings.gradle deleted file mode 100644 index 4d6d84d..0000000 --- a/settings.gradle +++ /dev/null @@ -1,2 +0,0 @@ -rootProject.name = 'bsldoc' - diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 0000000..ef1a73a --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,2 @@ +rootProject.name = "bsldoc" + diff --git a/src/main/java/ru/alkoleft/bsl/doc/AutodocManager.java b/src/main/java/ru/alkoleft/bsl/doc/AutodocManager.java new file mode 100644 index 0000000..9ce9c39 --- /dev/null +++ b/src/main/java/ru/alkoleft/bsl/doc/AutodocManager.java @@ -0,0 +1,93 @@ +package ru.alkoleft.bsl.doc; + +import lombok.Builder; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.io.file.PathUtils; +import ru.alkoleft.bsl.doc.bsl.BslContext; +import ru.alkoleft.bsl.doc.bsl.Filter; +import ru.alkoleft.bsl.doc.content.processor.TitleProcessor; +import ru.alkoleft.bsl.doc.manual.ManualContent; +import ru.alkoleft.bsl.doc.model.ContentModel; +import ru.alkoleft.bsl.doc.options.ManualMergeStrategy; +import ru.alkoleft.bsl.doc.options.OutputOptions; +import ru.alkoleft.bsl.doc.render.BaseRender; +import ru.alkoleft.bsl.doc.render.StructureRender; +import ru.alkoleft.bsl.doc.render.TemplatesDefinition; +import ru.alkoleft.bsl.doc.render.handlebars.RenderContext; +import ru.alkoleft.bsl.doc.render.output.OutputStrategy; +import ru.alkoleft.bsl.doc.structure.StructureBuilder; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Objects; + +@Slf4j +public class AutodocManager { + + private final OutputOptions outputOptions; + private final Path destination; + private final ManualMergeStrategy manualMergeStrategy; + private final ManualContent manualContent; + private final BslContext bslContext; + private final TemplatesDefinition templatesDefinition; + + @Builder + public AutodocManager(Filter filter, + OutputOptions outputOptions, + Path sources, + Path destination, + Path manualDocumentation, + ManualMergeStrategy manualMergeStrategy, + String header, + String footer) { + this.destination = destination; + this.outputOptions = outputOptions; + this.manualMergeStrategy = manualMergeStrategy; + this.templatesDefinition = new TemplatesDefinition(outputOptions.getOutputFormat().getPath(), header, footer); + this.manualContent = new ManualContent(manualDocumentation, destination); + this.bslContext = new BslContext(sources, filter); + + TitleProcessor.Factory.create(outputOptions.getOutputFormat()); + } + + public void loadData() { + manualContent.buildModel(outputOptions.getOutputFormat()); + } + + public void generateDocumentation() { + manualContent.copy(); + + var structure = StructureBuilder.builder(outputOptions).build(bslContext); + StructureBuilder.print(structure); + + var processor = OutputStrategy.create(manualMergeStrategy); + var contentModel = Objects.requireNonNullElseGet(manualContent.getContentModel(), ContentModel::new); + processor.init(outputOptions.getOutputFormat(), manualContent, contentModel); + var render = new StructureRender(outputOptions, processor, contentModel); + + BaseRender.setContext(RenderContext.Factory.create(templatesDefinition)); + render.render(structure, destination); + } + + @SneakyThrows + public void clearOutput() { + if (Files.exists(destination)) { + try (var stream = Files.walk(destination)) { + stream.filter(it -> !it.equals(destination)) + .forEach(this::deletePath); + } catch (IOException e) { + log.error("Error cleaning", e); + } + } + } + + private void deletePath(Path path) { + try { + PathUtils.delete(path); + } catch (IOException e) { + log.error("Error delete", e); + } + } +} diff --git a/src/main/java/ru/alkoleft/bsl/doc/bsl/BslContext.java b/src/main/java/ru/alkoleft/bsl/doc/bsl/BslContext.java index 9d42d1d..205e30e 100644 --- a/src/main/java/ru/alkoleft/bsl/doc/bsl/BslContext.java +++ b/src/main/java/ru/alkoleft/bsl/doc/bsl/BslContext.java @@ -1,194 +1,110 @@ package ru.alkoleft.bsl.doc.bsl; -import com.github._1c_syntax.bsl.languageserver.utils.Trees; -import com.github._1c_syntax.bsl.parser.BSLParser; -import com.github._1c_syntax.bsl.parser.BSLTokenizer; -import com.github._1c_syntax.bsl.types.MDOType; -import com.github._1c_syntax.mdclasses.Configuration; -import com.github._1c_syntax.mdclasses.mdo.MDCommonModule; -import org.antlr.v4.runtime.Token; -import ru.alkoleft.bsl.doc.bsl.symbols.MethodSymbol; -import ru.alkoleft.bsl.doc.bsl.symbols.MethodSymbolComputer; - -import java.nio.file.Files; +import com.github._1c_syntax.bsl.mdclasses.CF; +import com.github._1c_syntax.bsl.mdclasses.MDClasses; +import com.github._1c_syntax.bsl.mdo.MD; +import com.github._1c_syntax.bsl.mdo.Module; +import com.github._1c_syntax.bsl.mdo.ModuleOwner; +import com.github._1c_syntax.bsl.mdo.Subsystem; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import ru.alkoleft.bsl.doc.bsl.helpers.BslFilter; +import ru.alkoleft.bsl.doc.bsl.helpers.ModuleContextBuilder; + import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.List; -import java.util.Set; +import java.util.Optional; import java.util.stream.Collectors; import java.util.stream.Stream; +@Slf4j public class BslContext { - Configuration configuration; - Filter filter; - - List modules = Collections.emptyList(); - - private static final Set VALID_TOKEN_TYPES_FOR_COMMENTS_SEARCH = Set.of( - BSLParser.LINE_COMMENT, - BSLParser.WHITE_SPACE, - BSLParser.AMPERSAND - ); + @Getter + private static BslContext current; + @Getter + private final CF configuration; + private final ModuleContextBuilder builder; + private List modules = Collections.emptyList(); public BslContext(Path path, Filter filter) { - this.filter = filter; - configuration = Configuration.create(path); + BslFilter.setFilter(filter); + builder = new ModuleContextBuilder(); + configuration = (CF) MDClasses.createConfiguration(path); + current = this; } - public Stream getModules() { - return modules - .stream() - .map(this::buildModuleContext) - .filter(ModuleContext::isNotEmpty); + public Stream getModules() { + return configuration.getChildren().stream() + .filter(ModuleOwner.class::isInstance) + .map(ModuleOwner.class::cast) + .flatMap(it -> it.getModules().stream()) + .filter(BslFilter::checkModule); } - public boolean contains(String name) { - return modules.stream().anyMatch(it -> it.getName().equalsIgnoreCase(name)); + public ModuleInfo getModuleContext(Module module) { + return builder.buildFilteredModuleContext(module); } - public void load() { - modules = configuration.getOrderedTopMDObjects().get(MDOType.COMMON_MODULE).stream() - .map(MDCommonModule.class::cast) - .map(this::buildModuleContext) - .collect(Collectors.toList()); - } + public Stream getRootSubsystems(boolean filtered) { + var stream = configuration.getSubsystems().stream(); - private ModuleContext buildModuleContext(MDCommonModule module) { - var bslModules = module.getModules(); - var srcPath = Path.of(bslModules.get(0).getUri()); - List methods; - String description; - try { - var content = Files.readString(srcPath); - MethodSymbolComputer computer = new MethodSymbolComputer(); - var tokenizer = new BSLTokenizer(content); - methods = computer.compute(tokenizer); - description = computeModuleDescription(tokenizer); - } catch (Exception e) { - throw new RuntimeException(module.getMdoReference().getMdoRef() + ". Module parsing error", e); + if (filtered) { + stream = stream.filter(BslFilter::checkRootSubsystem); } - - return ModuleContext.builder() - .owner(module) - .methods(methods) - .description(description) - .build(); + return stream; } - private String computeModuleDescription(BSLTokenizer tokenizer) { - var token = tokenizer.getAst().getStart(); - - var isDescription = false; - List comments = null; - while (token != null && !isDescription) { - comments = getComments(tokenizer.getTokens(), token); - isDescription = !comments.isEmpty() && comments.stream().map(Token::getText).map(it -> it.substring(2)).map(String::trim).noneMatch(it -> it.contains("Copyright") || it.contains("Экспортные")); - token = comments.isEmpty() ? null : comments.get(0); - } - if (isDescription) { - var lines = comments.stream() - .map(Token::getText) - .map(it -> it.substring(2)) - .filter(it -> it.isEmpty() || !it.matches("/+")) - .collect(Collectors.toList()); - - var chars = lines.get(0).toCharArray(); - - for (int i = 1; i < lines.size(); i++) { - var line = lines.get(i); - - for (int j = 0; j < chars.length; j++) { - if (line.length() <= j || line.charAt(j) != chars[j]) { - chars = Arrays.copyOf(chars, j); - break; - } - } - if (chars.length == 0) { - break; - } - } - - if (chars.length > 0) { - char[] finalChars = chars; - return lines.stream() - .map(it -> it.substring(finalChars.length)) - .collect(Collectors.joining("\n")); - } else { - return String.join("\n", lines); - } - } else { - return ""; - } + public Stream getChildrenSubsystems(Subsystem parent) { + return parent.getSubsystems().stream(); } - private ModuleContext buildModuleContext(ModuleContext module) { - - var stream = module.getMethods().stream(); - - if (filter.isExport()) { - stream = stream.filter(MethodSymbol::isExport); - } - - if (!filter.getRegions().isEmpty()) { - stream = stream.filter(this::regionFilter); - } - - return ModuleContext.builder() - .owner(module.getOwner()) - .methods(stream.collect(Collectors.toList())) - .description(module.getDescription()) - .build(); + public boolean contains(String name) { + return modules.stream().anyMatch(it -> it.getName().equalsIgnoreCase(name)); } - private boolean regionFilter(MethodSymbol m) { - var region = m.getRegion(); - - while (region != null) { - if (filter.getRegions().contains(region.getName())) { - return true; - } - region = region.getParent(); - } - return false; + public Optional getModule(String name) { + return modules.stream().filter(it -> it.getName().equalsIgnoreCase(name)).findAny(); } - public static List getComments(List tokens, Token token) { - List comments = new ArrayList<>(); - fillCommentsCollection(tokens, token, comments); - return comments; + public Stream getSubsystemObjects(Subsystem subsystem) { + return subsystem.getContent().stream() + .map(configuration::findChild) + .filter(Optional::isPresent) + .map(Optional::get); } - private static void fillCommentsCollection(List tokens, Token currentToken, List lines) { - - int index = currentToken.getTokenIndex(); + public void load() { + modules = getModules() + .map(builder::buildModuleContext) + .collect(Collectors.toList()); + } - if (index == 0) { - return; + public MethodInfo getMethodInfo(String link) { + if (link == null || link.isEmpty()) { + return null; } + var linkInfo = Links.parseLink(link, false); + return getMethodInfo(linkInfo); + } - var previousToken = tokens.get(index - 1); + public MethodInfo getMethodInfo(Links.Link link) { - if (abortSearchComments(previousToken, currentToken)) { - return; + if (link == null || link.ownerName() == null || link.methodName() == null) { + return null; } - fillCommentsCollection(tokens, previousToken, lines); - int type = previousToken.getType(); - if (type == BSLParser.LINE_COMMENT) { - lines.add(previousToken); - } - } + var module = getModule(link.ownerName()); + var method = module.flatMap(it -> it.getMethod(link.methodName())); - private static boolean abortSearchComments(Token previousToken, Token currentToken) { - int type = previousToken.getType(); - return !VALID_TOKEN_TYPES_FOR_COMMENTS_SEARCH.contains(type) || isBlankLine(previousToken, currentToken); + return MethodInfo.builder() + .module(module.orElse(null)) + .method(method.orElse(null)) + .publishing(module.isPresent() + && method.isPresent() + && BslFilter.checkModule(module.get()) + && BslFilter.checkMethod(method.get())) + .build(); } - private static boolean isBlankLine(Token previousToken, Token currentToken) { - return previousToken.getType() == BSLParser.WHITE_SPACE - && (previousToken.getTokenIndex() == 0 - || (previousToken.getLine() + 1) != currentToken.getLine()); - } } diff --git a/src/main/java/ru/alkoleft/bsl/doc/bsl/Filter.java b/src/main/java/ru/alkoleft/bsl/doc/bsl/Filter.java index 8341efd..1e8cf77 100644 --- a/src/main/java/ru/alkoleft/bsl/doc/bsl/Filter.java +++ b/src/main/java/ru/alkoleft/bsl/doc/bsl/Filter.java @@ -10,6 +10,12 @@ @Builder public class Filter { boolean isExport; + boolean onlyCommonAndManagerModules = false; @Singular List regions; + @Singular + List rootSubsystems; + @Singular + List modules; + } diff --git a/src/main/java/ru/alkoleft/bsl/doc/bsl/Links.java b/src/main/java/ru/alkoleft/bsl/doc/bsl/Links.java new file mode 100644 index 0000000..12fe025 --- /dev/null +++ b/src/main/java/ru/alkoleft/bsl/doc/bsl/Links.java @@ -0,0 +1,45 @@ +package ru.alkoleft.bsl.doc.bsl; + +import lombok.experimental.UtilityClass; +import ru.alkoleft.bsl.doc.bsl.helpers.Strings; + +import java.util.regex.Pattern; + +@UtilityClass +public class Links { + private static final Pattern PATTERN_WITH_SEE = + Pattern.compile("^см\\. (([\\wА-Яа-я\\.\\d]+)\\.)*([\\wА-Яа-я\\d]+)$"); + private static final Pattern PATTERN = Pattern.compile("^(([\\wА-Яа-я\\.\\d]+)\\.)*([\\wА-Яа-я\\d]+)$"); + + public Link parseLink(String link, boolean withSee) { + var matcher = (withSee ? PATTERN_WITH_SEE : PATTERN).matcher(link); + if (matcher.find()) { + return createLink(matcher.group(2), matcher.group(3)); + } + return null; + } + + public Link createLink(String owner, String method) { + if (owner != null && method != null) { + return new Link(owner, method); + } else if (BslContext.getCurrent().contains(method)) { + return new Link(method, null); + } else if (method != null) { + return new Link(null, method); + } else { + return null; + } + } + + public record Link(String ownerName, String methodName) { + public String getFullName() { + if (Strings.isNullOrEmpty(ownerName)) { + return methodName; + } else if (Strings.isNullOrEmpty(methodName)) { + return ownerName; + } else { + return ownerName + "." + methodName; + } + } + } +} diff --git a/src/main/java/ru/alkoleft/bsl/doc/bsl/MethodInfo.java b/src/main/java/ru/alkoleft/bsl/doc/bsl/MethodInfo.java new file mode 100644 index 0000000..a853db0 --- /dev/null +++ b/src/main/java/ru/alkoleft/bsl/doc/bsl/MethodInfo.java @@ -0,0 +1,13 @@ +package ru.alkoleft.bsl.doc.bsl; + +import lombok.Builder; +import lombok.Value; +import ru.alkoleft.bsl.doc.bsl.symbols.MethodSymbol; + +@Value +@Builder +public class MethodInfo { + MethodSymbol method; + ModuleInfo module; + boolean publishing; +} diff --git a/src/main/java/ru/alkoleft/bsl/doc/bsl/ModuleContext.java b/src/main/java/ru/alkoleft/bsl/doc/bsl/ModuleContext.java deleted file mode 100644 index 219270a..0000000 --- a/src/main/java/ru/alkoleft/bsl/doc/bsl/ModuleContext.java +++ /dev/null @@ -1,25 +0,0 @@ -package ru.alkoleft.bsl.doc.bsl; - -import com.github._1c_syntax.mdclasses.mdo.AbstractMDObjectBSL; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Value; -import ru.alkoleft.bsl.doc.bsl.symbols.MethodSymbol; - -import java.util.List; - -@Builder -@Value -public class ModuleContext { - AbstractMDObjectBSL owner; - List methods; - String description; - - public String getName() { - return owner.getName(); - } - - public boolean isNotEmpty() { - return methods != null && !methods.isEmpty(); - } -} diff --git a/src/main/java/ru/alkoleft/bsl/doc/bsl/ModuleInfo.java b/src/main/java/ru/alkoleft/bsl/doc/bsl/ModuleInfo.java new file mode 100644 index 0000000..5d85571 --- /dev/null +++ b/src/main/java/ru/alkoleft/bsl/doc/bsl/ModuleInfo.java @@ -0,0 +1,35 @@ +package ru.alkoleft.bsl.doc.bsl; + +import com.github._1c_syntax.bsl.mdo.MD; +import com.github._1c_syntax.bsl.mdo.Module; +import lombok.Builder; +import lombok.Value; +import ru.alkoleft.bsl.doc.bsl.symbols.MethodSymbol; + +import java.util.List; +import java.util.Optional; + +@Builder +@Value +public class ModuleInfo { + MD owner; + Module module; + List methods; + String description; + + public String getName() { + return owner.getName(); + } + + public boolean isNotEmpty() { + return !isEmpty(); + } + + public boolean isEmpty() { + return methods == null || methods.isEmpty(); + } + + public Optional getMethod(String name) { + return getMethods().stream().filter(it -> name.equalsIgnoreCase(it.getName())).findAny(); + } +} diff --git a/src/main/java/ru/alkoleft/bsl/doc/bsl/helpers/BslFilter.java b/src/main/java/ru/alkoleft/bsl/doc/bsl/helpers/BslFilter.java new file mode 100644 index 0000000..91eeb67 --- /dev/null +++ b/src/main/java/ru/alkoleft/bsl/doc/bsl/helpers/BslFilter.java @@ -0,0 +1,75 @@ +package ru.alkoleft.bsl.doc.bsl.helpers; + +import com.github._1c_syntax.bsl.mdo.Module; +import com.github._1c_syntax.bsl.mdo.Subsystem; +import com.github._1c_syntax.bsl.types.ModuleType; +import lombok.Getter; +import lombok.experimental.UtilityClass; +import ru.alkoleft.bsl.doc.bsl.Filter; +import ru.alkoleft.bsl.doc.bsl.ModuleInfo; +import ru.alkoleft.bsl.doc.bsl.symbols.MethodSymbol; + +import java.util.Set; +import java.util.stream.Stream; + +@UtilityClass +public class BslFilter { + + private final Set moduleTypes = Set.of( + ModuleType.CommonModule, + ModuleType.ValueManagerModule, + ModuleType.ManagerModule + ); + + @Getter + private Filter filter; + + public static void setFilter(Filter filter) { + BslFilter.filter = filter; + } + + public boolean checkMethod(MethodSymbol method) { + return (!filter.isExport() || method.isExport()) + && (filter.getRegions().isEmpty() || checkRegion(method)); + } + + public Stream setFilter(Stream stream) { + if (filter.isExport()) { + stream = stream.filter(MethodSymbol::isExport); + } + + if (!filter.getRegions().isEmpty()) { + stream = stream.filter(BslFilter::checkRegion); + } + + return stream; + } + + public boolean checkModule(ModuleInfo module) { + return true; + } + + public boolean checkModule(Module module) { + return !filter.isOnlyCommonAndManagerModules() || moduleTypes.contains(module.getModuleType()); + } + + public boolean checkRegion(MethodSymbol m) { + var region = m.getRegion(); + + while (region != null) { + if (filter.getRegions().contains(region.name())) { + return true; + } + region = region.parent(); + } + return false; + } + + public boolean checkRootSubsystem(Subsystem subsystem) { + if (filter.getRootSubsystems().isEmpty()) { + return true; + } else { + return filter.getRootSubsystems().contains(subsystem.getName()); + } + } +} diff --git a/src/main/java/ru/alkoleft/bsl/doc/bsl/helpers/MDOHelper.java b/src/main/java/ru/alkoleft/bsl/doc/bsl/helpers/MDOHelper.java new file mode 100644 index 0000000..08b94be --- /dev/null +++ b/src/main/java/ru/alkoleft/bsl/doc/bsl/helpers/MDOHelper.java @@ -0,0 +1,33 @@ +package ru.alkoleft.bsl.doc.bsl.helpers; + +import com.github._1c_syntax.bsl.mdo.MD; +import com.github._1c_syntax.bsl.mdo.Module; +import com.github._1c_syntax.bsl.types.ModuleType; +import lombok.experimental.UtilityClass; +import ru.alkoleft.bsl.doc.bsl.BslContext; + +@UtilityClass +public class MDOHelper { + + public MD getOwner(Module module) { + return BslContext.getCurrent().getConfiguration().findChild(module.getOwner()).orElse(null); + } + + public String getPresent(MD object) { + return object.getDescription(); + } + + public String getPresent(ModuleType moduleType) { + return switch (moduleType) { + case ManagerModule -> "Модуль менеджера"; + case BotModule, ObjectModule -> "Модуль объекта"; + case HTTPServiceModule -> "Модуль http-сервиса"; + case WEBServiceModule -> "Модуль web-сервиса"; + case CommonModule -> "Модуль"; + case CommandModule -> "Модуль команды"; + case RecordSetModule -> "Модуль набора записей"; + case ValueManagerModule -> "Модуль менеджера значений"; + default -> moduleType.name(); + }; + } +} diff --git a/src/main/java/ru/alkoleft/bsl/doc/bsl/helpers/ModuleComputer.java b/src/main/java/ru/alkoleft/bsl/doc/bsl/helpers/ModuleComputer.java new file mode 100644 index 0000000..75b7f89 --- /dev/null +++ b/src/main/java/ru/alkoleft/bsl/doc/bsl/helpers/ModuleComputer.java @@ -0,0 +1,65 @@ +package ru.alkoleft.bsl.doc.bsl.helpers; + +import com.github._1c_syntax.bsl.parser.BSLTokenizer; +import lombok.experimental.UtilityClass; +import org.antlr.v4.runtime.Token; +import ru.alkoleft.bsl.doc.bsl.symbols.Trees; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +@UtilityClass +public class ModuleComputer { + + public String computeModuleDescription(BSLTokenizer tokenizer) { + var token = tokenizer.getAst().getStart(); + + var isDescription = false; + List comments = null; + while (token != null && !isDescription) { + comments = Trees.getComments(tokenizer.getTokens(), token); + isDescription = !comments.isEmpty() + && comments.stream() + .map(Token::getText) + .map(it -> it.substring(2)) + .map(String::trim) + .noneMatch(it -> it.contains("Copyright") || it.contains("Экспортные")); + token = comments.isEmpty() ? null : comments.get(0); + } + if (isDescription) { + var lines = comments.stream() + .map(Token::getText) + .map(it -> it.substring(2)) + .filter(it -> it.isEmpty() || !it.matches("/+")) + .collect(Collectors.toList()); + + var chars = lines.get(0).toCharArray(); + + for (int i = 1; i < lines.size(); i++) { + var line = lines.get(i); + + for (int j = 0; j < chars.length; j++) { + if (line.length() <= j || line.charAt(j) != chars[j]) { + chars = Arrays.copyOf(chars, j); + break; + } + } + if (chars.length == 0) { + break; + } + } + + if (chars.length > 0) { + char[] finalChars = chars; + return lines.stream() + .map(it -> it.substring(finalChars.length)) + .collect(Collectors.joining("\n")); + } else { + return String.join("\n", lines); + } + } else { + return ""; + } + } +} diff --git a/src/main/java/ru/alkoleft/bsl/doc/bsl/helpers/ModuleContextBuilder.java b/src/main/java/ru/alkoleft/bsl/doc/bsl/helpers/ModuleContextBuilder.java new file mode 100644 index 0000000..c8dbdbf --- /dev/null +++ b/src/main/java/ru/alkoleft/bsl/doc/bsl/helpers/ModuleContextBuilder.java @@ -0,0 +1,57 @@ +package ru.alkoleft.bsl.doc.bsl.helpers; + +import com.github._1c_syntax.bsl.mdo.Module; +import com.github._1c_syntax.bsl.parser.BSLTokenizer; +import lombok.extern.slf4j.Slf4j; +import ru.alkoleft.bsl.doc.bsl.ModuleInfo; +import ru.alkoleft.bsl.doc.bsl.symbols.MethodSymbol; +import ru.alkoleft.bsl.doc.bsl.symbols.MethodSymbolComputer; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.stream.Collectors; + +@Slf4j +public class ModuleContextBuilder { + public ModuleInfo buildModuleContext(Module bslModule) { + var owner = MDOHelper.getOwner(bslModule); + log.debug("Parse module: {}.{}", owner.getName(), bslModule.getModuleType()); + var srcPath = Path.of(bslModule.getUri()); + List methods; + String description; + try { + var content = Files.readString(srcPath); + MethodSymbolComputer computer = new MethodSymbolComputer(); + var tokenizer = new BSLTokenizer(content); + methods = computer.compute(tokenizer); + description = ModuleComputer.computeModuleDescription(tokenizer); + } catch (Exception e) { + throw new RuntimeException(owner.getMdoReference().getMdoRef() + ". Module parsing error", e); + } + + return ModuleInfo.builder() + .owner(owner) + .module(bslModule) + .methods(methods) + .description(description) + .build(); + } + + public ModuleInfo buildFilteredModuleContext(Module bslModule) { + return buildModuleContext(buildModuleContext(bslModule)); + } + + public ModuleInfo buildModuleContext(ModuleInfo module) { + + var stream = BslFilter.setFilter(module.getMethods().stream()); + + return ModuleInfo.builder() + .module(module.getModule()) + .owner(module.getOwner()) + .description(module.getDescription()) + .methods(stream.collect(Collectors.toList())) + .build(); + } + +} diff --git a/src/main/java/ru/alkoleft/bsl/doc/bsl/helpers/Strings.java b/src/main/java/ru/alkoleft/bsl/doc/bsl/helpers/Strings.java new file mode 100644 index 0000000..1cc7870 --- /dev/null +++ b/src/main/java/ru/alkoleft/bsl/doc/bsl/helpers/Strings.java @@ -0,0 +1,10 @@ +package ru.alkoleft.bsl.doc.bsl.helpers; + +import lombok.experimental.UtilityClass; + +@UtilityClass +public class Strings { + public boolean isNullOrEmpty(String value) { + return value == null || value.isEmpty(); + } +} diff --git a/src/main/java/ru/alkoleft/bsl/doc/bsl/symbols/DocumentationLink.java b/src/main/java/ru/alkoleft/bsl/doc/bsl/symbols/DocumentationLink.java deleted file mode 100644 index b5bd4e4..0000000 --- a/src/main/java/ru/alkoleft/bsl/doc/bsl/symbols/DocumentationLink.java +++ /dev/null @@ -1,4 +0,0 @@ -package ru.alkoleft.bsl.doc.bsl.symbols; - -public class DocumentationLink { -} diff --git a/src/main/java/ru/alkoleft/bsl/doc/bsl/symbols/MethodSymbol.java b/src/main/java/ru/alkoleft/bsl/doc/bsl/symbols/MethodSymbol.java index 6e033cb..038c0ed 100644 --- a/src/main/java/ru/alkoleft/bsl/doc/bsl/symbols/MethodSymbol.java +++ b/src/main/java/ru/alkoleft/bsl/doc/bsl/symbols/MethodSymbol.java @@ -1,40 +1,110 @@ package ru.alkoleft.bsl.doc.bsl.symbols; -import com.github._1c_syntax.bsl.languageserver.context.symbol.description.MethodDescription; -import com.google.common.base.Strings; +import com.github._1c_syntax.bsl.parser.description.HyperlinkTypeDescription; +import com.github._1c_syntax.bsl.parser.description.MethodDescription; +import com.github._1c_syntax.bsl.parser.description.TypeDescription; +import com.github._1c_syntax.bsl.parser.description.support.SimpleRange; import lombok.Builder; +import lombok.Setter; import lombok.Value; +import lombok.experimental.NonFinal; +import lombok.extern.slf4j.Slf4j; +import ru.alkoleft.bsl.doc.bsl.BslContext; +import ru.alkoleft.bsl.doc.bsl.helpers.Strings; +import ru.alkoleft.bsl.doc.render.UsedInHandlebars; -import java.util.Collections; import java.util.List; import java.util.Optional; @Builder @Value +@Slf4j public class MethodSymbol { boolean deprecated; boolean function; boolean export; String name; Optional fullDescription; + List parameters; + SimpleRange range; + @Setter + @NonFinal RegionSymbol region; public String getDescription() { return fullDescription.map(MethodDescription::getPurposeDescription).orElse(""); } - public List getExamples() { - return fullDescription.map(MethodDescription::getExamples).orElse(Collections.emptyList()); + @UsedInHandlebars("module.hbs") + public String getExamples() { + return fullDescription.map(MethodDescription::getExamples).orElse(""); } public TypeDescription getReturnedValue() { - var result = fullDescription.map(MethodDescription::getReturnedValue).filter(it -> !it.isEmpty()).map(it -> it.get(0)).orElse(null); + var result = fullDescription + .map(MethodDescription::getReturnedValue) + .filter(it -> !it.isEmpty()) + .map(it -> it.get(0)) + .orElse(null); if (result != null) { - return new TypeDescription(result.getName(), result.getDescription(), result.getParameters(), Strings.isNullOrEmpty(result.getLink()) ? null : "см. " + result.getLink(), result.isHyperlink()); + return createTypeDescription(result); } else { return null; } } + + private TypeDescription createTypeDescription(TypeDescription baseDescription) { + TypeDescription result = null; + HyperlinkTypeDescription hyperlinkTypeDescription = null; + if (baseDescription instanceof HyperlinkTypeDescription typeDescription) { + hyperlinkTypeDescription = typeDescription; + } + if (hyperlinkTypeDescription != null && !Strings.isNullOrEmpty(hyperlinkTypeDescription.hyperlink().link())) { + var methodInfo = BslContext.getCurrent().getMethodInfo(hyperlinkTypeDescription.hyperlink().link()); + + if (methodInfo != null && methodInfo.getMethod() != null) { + result = methodInfo.getMethod().getReturnedValue(); + } else { + log.warn("Bad link: {}", hyperlinkTypeDescription.hyperlink().link()); + } + } + if (result == null) { + result = baseDescription; + } + return result; + } + + @UsedInHandlebars("module.hbs") + public String callExample() { + var builder = new StringBuilder(); + if (function) { + builder.append("Функция"); + } else { + builder.append("Процедура"); + } + builder.append(" ").append(name).append("("); + boolean firstParameter = true; + for (var parameter : parameters) { + if (firstParameter) { + firstParameter = false; + } else { + builder.append(", "); + } + if (parameter.isByValue()) { + builder.append("Знач "); + } + builder.append(parameter.getName()); + if (parameter.getDefaultValue().type() != ParameterDefinition.ParameterType.EMPTY) { + builder.append(" = ").append(parameter.getDefaultValue().value()); + } + } + builder.append(")"); + if (isExport()) { + builder.append(" Экспорт"); + } + + return builder.toString(); + } } diff --git a/src/main/java/ru/alkoleft/bsl/doc/bsl/symbols/MethodSymbolComputer.java b/src/main/java/ru/alkoleft/bsl/doc/bsl/symbols/MethodSymbolComputer.java index e9e14ee..ef7eec5 100644 --- a/src/main/java/ru/alkoleft/bsl/doc/bsl/symbols/MethodSymbolComputer.java +++ b/src/main/java/ru/alkoleft/bsl/doc/bsl/symbols/MethodSymbolComputer.java @@ -1,109 +1,48 @@ package ru.alkoleft.bsl.doc.bsl.symbols; -import com.github._1c_syntax.bsl.languageserver.context.symbol.description.MethodDescription; -import com.github._1c_syntax.bsl.languageserver.utils.Ranges; -import com.github._1c_syntax.bsl.languageserver.utils.Trees; import com.github._1c_syntax.bsl.parser.BSLParser; import com.github._1c_syntax.bsl.parser.BSLParserBaseVisitor; import com.github._1c_syntax.bsl.parser.BSLTokenizer; +import com.github._1c_syntax.bsl.parser.description.MethodDescription; +import com.github._1c_syntax.bsl.parser.description.ParameterDescription; +import com.github._1c_syntax.bsl.parser.description.support.SimpleRange; +import lombok.extern.slf4j.Slf4j; import org.antlr.v4.runtime.Token; import org.antlr.v4.runtime.tree.ParseTree; import org.antlr.v4.runtime.tree.TerminalNode; -import org.eclipse.lsp4j.Range; +import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.Deque; import java.util.List; import java.util.Optional; -import java.util.Stack; import java.util.stream.Collectors; +@Slf4j public class MethodSymbolComputer extends BSLParserBaseVisitor { private final List methods = new ArrayList<>(); + private final List regionData = new ArrayList<>(); + private final Deque regions = new ArrayDeque<>(); private BSLTokenizer tokenizer; - Stack regions = new Stack<>(); - - public List compute(BSLTokenizer tokenizer) { - this.tokenizer = tokenizer; - methods.clear(); - visitFile(tokenizer.getAst()); - return methods; - } - - @Override - public ParseTree visitRegionStart(BSLParser.RegionStartContext ctx) { - var name = ctx.regionName().getText(); - - RegionSymbol region; - - if (regions.isEmpty()) { - region = new RegionSymbol(name, null); - } else { - region = new RegionSymbol(name, regions.peek()); - } - regions.push(region); - return super.visitRegionStart(ctx); - } - - @Override - public ParseTree visitRegionEnd(BSLParser.RegionEndContext ctx) { - regions.pop(); - return super.visitRegionEnd(ctx); - } - - @Override - public ParseTree visitFunction(BSLParser.FunctionContext ctx) { - BSLParser.FuncDeclarationContext declaration = ctx.funcDeclaration(); - - TerminalNode startNode = declaration.FUNCTION_KEYWORD(); - handleMethod(startNode, declaration.subName().getStart(), declaration.paramList(), true, declaration.EXPORT_KEYWORD() != null); - return super.visitFunction(ctx); - } - - @Override - public ParseTree visitProcedure(BSLParser.ProcedureContext ctx) { - BSLParser.ProcDeclarationContext declaration = ctx.procDeclaration(); - - TerminalNode startNode = declaration.PROCEDURE_KEYWORD(); - handleMethod(startNode, declaration.subName().getStart(), declaration.paramList(), false, declaration.EXPORT_KEYWORD() != null); - return super.visitProcedure(ctx); - } - - private void handleMethod(TerminalNode startNode, Token subName, BSLParser.ParamListContext paramList, boolean function, boolean export) { - Optional description = createDescription(startNode.getSymbol()); - boolean deprecated = description - .map(MethodDescription::isDeprecated) - .orElse(false); - - var method = MethodSymbol.builder() - .name(subName.getText().intern()) - .function(function) - .export(export) - .fullDescription(description) - .deprecated(deprecated) - .parameters(createParameters(paramList, description)) - .region(regions.peek()) - .build(); - methods.add(method); - } - - private static List createParameters(BSLParser.ParamListContext paramList, Optional description) { + private static List createParameters(BSLParser.ParamListContext paramList, + Optional description) { if (paramList == null) { return Collections.emptyList(); } return paramList.param().stream() - .map((BSLParser.ParamContext param) -> { - String parameterName = getParameterName(param.IDENTIFIER()); - return ParameterDefinition.builder() - .name(parameterName) - .byValue(param.VAL_KEYWORD() != null) - .defaultValue(getDefaultValue(param)) - .description(getParameterDescription(parameterName, description)) - .build(); - }).collect(Collectors.toList()); + .map((BSLParser.ParamContext param) -> { + String parameterName = getParameterName(param.IDENTIFIER()); + return ParameterDefinition.builder() + .name(parameterName) + .byValue(param.VAL_KEYWORD() != null) + .defaultValue(getDefaultValue(param)) + .description(getParameterDescription(parameterName, description)) + .build(); + }).collect(Collectors.toList()); } private static ParameterDefinition.DefaultValue getDefaultValue(BSLParser.ParamContext param) { @@ -131,9 +70,9 @@ private static ParameterDefinition.DefaultValue getDefaultValue(BSLParser.ParamC defaultValue = new ParameterDefinition.DefaultValue(ParameterDefinition.ParameterType.NULL, value.intern()); } else if (constValue.string() != null) { var value = constValue.string().STRING().stream() - .map(TerminalNode::getSymbol) - .map(Token::getText) - .collect(Collectors.joining("\n")); + .map(TerminalNode::getSymbol) + .map(Token::getText) + .collect(Collectors.joining("\n")); defaultValue = new ParameterDefinition.DefaultValue(ParameterDefinition.ParameterType.STRING, value.intern()); } else if (constValue.numeric() != null) { var value = constValue.numeric().getText(); @@ -153,20 +92,103 @@ private static ParameterDefinition.DefaultValue getDefaultValue(BSLParser.ParamC private static String getParameterName(TerminalNode identifier) { return Optional.ofNullable(identifier) - .map(ParseTree::getText) - .map(String::intern) - .orElse(""); + .map(ParseTree::getText) + .map(String::intern) + .orElse(""); } - private static Optional getParameterDescription(String parameterName, Optional description) { + private static Optional getParameterDescription(String parameterName, + Optional description) { return description.map(MethodDescription::getParameters) - .stream() - .flatMap(Collection::stream) - .filter(parameterDescription -> parameterDescription.getName().equalsIgnoreCase(parameterName)) - .findFirst() - .map(p -> new ParameterDescription(p.getName(), p.getTypes(), p.getLink(), p.isHyperlink())); + .stream() + .flatMap(Collection::stream) + .filter(parameterDescription -> parameterDescription.name().equalsIgnoreCase(parameterName)) + .findFirst() + .map(p -> new ParameterDescription(p.name(), p.element(), p.types())); + + } + public List compute(BSLTokenizer tokenizer) { + this.tokenizer = tokenizer; + methods.clear(); + visitFile(tokenizer.getAst()); + + // Используется постпривязка регионов, тк порядок обхода не соответствует порядку следования объект (в метод заходит раньше региона) + for (var region : regionData) { + for (var method : methods) { + if (SimpleRange.containsRange(region.range, method.getRange())) { + method.setRegion(region.region); + } + } + } + return methods; + } + + @Override + public ParseTree visitRegionStart(BSLParser.RegionStartContext ctx) { + var name = ctx.regionName().getText(); + + log.debug("Start region: {}", name); + + var data = new RegionData(); + data.start = ctx; + if (regions.isEmpty()) { + data.region = new RegionSymbol(name, null); + } else { + data.region = new RegionSymbol(name, regions.peek().region); + } + regions.push(data); + regionData.add(data); + return super.visitRegionStart(ctx); + } + + @Override + public ParseTree visitRegionEnd(BSLParser.RegionEndContext ctx) { + if (!regions.isEmpty()) { + var regionData = regions.pop(); + log.debug("End region: {}", regionData.region.name()); + regionData.end = ctx; + regionData.range = SimpleRange.create(regionData.start.start, regionData.end.stop); + } + return super.visitRegionEnd(ctx); + } + + @Override + public ParseTree visitFunction(BSLParser.FunctionContext ctx) { + var declaration = ctx.funcDeclaration(); + log.debug("Function: {}", declaration.subName().getText()); + var startNode = declaration.FUNCTION_KEYWORD(); + handleMethod(startNode, declaration.subName().getStart(), declaration.paramList(), true, declaration.EXPORT_KEYWORD() != null); + return super.visitFunction(ctx); + } + + @Override + public ParseTree visitProcedure(BSLParser.ProcedureContext ctx) { + var declaration = ctx.procDeclaration(); + log.debug("Procedure: {}", declaration.subName().getText()); + + TerminalNode startNode = declaration.PROCEDURE_KEYWORD(); + handleMethod(startNode, declaration.subName().getStart(), declaration.paramList(), false, declaration.EXPORT_KEYWORD() != null); + return super.visitProcedure(ctx); + } + + private void handleMethod(TerminalNode startNode, Token subName, BSLParser.ParamListContext paramList, boolean function, boolean export) { + var description = createDescription(startNode.getSymbol()); + var deprecated = description + .map(MethodDescription::isDeprecated) + .orElse(false); + + var method = MethodSymbol.builder() + .name(subName.getText().intern()) + .function(function) + .export(export) + .fullDescription(description) + .deprecated(deprecated) + .parameters(createParameters(paramList, description)) + .range(SimpleRange.create(startNode.getSymbol(), startNode.getSymbol())) + .build(); + methods.add(method); } private Optional createDescription(Token token) { @@ -175,6 +197,13 @@ private Optional createDescription(Token token) { return Optional.empty(); } - return Optional.of(new MethodDescription(comments)); + return Optional.of(MethodDescription.create(comments)); + } + + private static class RegionData { + RegionSymbol region; + BSLParser.RegionStartContext start; + BSLParser.RegionEndContext end; + SimpleRange range; } } diff --git a/src/main/java/ru/alkoleft/bsl/doc/bsl/symbols/ParameterDefinition.java b/src/main/java/ru/alkoleft/bsl/doc/bsl/symbols/ParameterDefinition.java index 61fe89d..7d95bbc 100644 --- a/src/main/java/ru/alkoleft/bsl/doc/bsl/symbols/ParameterDefinition.java +++ b/src/main/java/ru/alkoleft/bsl/doc/bsl/symbols/ParameterDefinition.java @@ -1,6 +1,7 @@ package ru.alkoleft.bsl.doc.bsl.symbols; -import com.github._1c_syntax.bsl.languageserver.context.symbol.description.TypeDescription; +import com.github._1c_syntax.bsl.parser.description.ParameterDescription; +import com.github._1c_syntax.bsl.parser.description.TypeDescription; import lombok.Builder; import lombok.Value; @@ -18,21 +19,18 @@ public class ParameterDefinition { boolean byValue; public String getDescription() { - if (description.isEmpty()) { - return ""; - } - return description.get().getTypes() - .stream() - .findFirst() - .map(TypeDescription::getDescription).orElse(""); + return description.map(parameterDescription -> parameterDescription.types() + .stream() + .findFirst() + .map(TypeDescription::description).orElse("")).orElse(""); } public List getTypes() { - if (description.isEmpty()) { - return Collections.emptyList(); - } else { - return description.get().getTypes(); - } + return description.map(ParameterDescription::types).orElse(Collections.emptyList()); + } + + public boolean isRequired() { + return defaultValue.type == ParameterType.EMPTY; } public enum ParameterType { @@ -45,12 +43,7 @@ public enum ParameterType { EMPTY } - @Value - public static class DefaultValue { + public record DefaultValue(ParameterType type, String value) { public static final DefaultValue EMPTY = new DefaultValue(ParameterType.EMPTY, ""); - - ParameterType type; - String value; } - } diff --git a/src/main/java/ru/alkoleft/bsl/doc/bsl/symbols/ParameterDescription.java b/src/main/java/ru/alkoleft/bsl/doc/bsl/symbols/ParameterDescription.java deleted file mode 100644 index 85c7e57..0000000 --- a/src/main/java/ru/alkoleft/bsl/doc/bsl/symbols/ParameterDescription.java +++ /dev/null @@ -1,34 +0,0 @@ -package ru.alkoleft.bsl.doc.bsl.symbols; - -import com.github._1c_syntax.bsl.languageserver.context.symbol.description.TypeDescription; -import lombok.AllArgsConstructor; -import lombok.Value; - -import java.util.List; - -/** - * Описание параметра из комментария - описания метода - */ -@AllArgsConstructor -@Value -public class ParameterDescription { - /** - * Имя параметра - */ - String name; - /** - * Возможные типы параметра. Может быть пустым - */ - List types; - /** - * Если описание параметров содержит только ссылку, то здесь будет ее значение - *

- * TODO Временное решение, надо будет продумать в следующем релизе - */ - String link; - /** - * Признак того, что параметр является гиперссылкой - */ - boolean isHyperlink; - -} \ No newline at end of file diff --git a/src/main/java/ru/alkoleft/bsl/doc/bsl/symbols/RegionSymbol.java b/src/main/java/ru/alkoleft/bsl/doc/bsl/symbols/RegionSymbol.java index a00668c..0be9254 100644 --- a/src/main/java/ru/alkoleft/bsl/doc/bsl/symbols/RegionSymbol.java +++ b/src/main/java/ru/alkoleft/bsl/doc/bsl/symbols/RegionSymbol.java @@ -1,16 +1,10 @@ package ru.alkoleft.bsl.doc.bsl.symbols; -import lombok.Value; - -@Value -public class RegionSymbol { +public record RegionSymbol(String name, RegionSymbol parent) { public static final String PUBLIC_REGION_RU = "ПрограммныйИнтерфейс"; public static final String PUBLIC_REGION_EN = "Public"; public static final String INTERNAL_REGION_RU = "СлужебныйПрограммныйИнтерфейс"; public static final String INTERNAL_REGION_EN = "Internal"; public static final String PRIVATE_REGION_RU = "СлужебныеПроцедурыИФункции"; public static final String PRIVATE_REGION_EN = "Private"; - - String name; - RegionSymbol parent; } diff --git a/src/main/java/ru/alkoleft/bsl/doc/bsl/symbols/Trees.java b/src/main/java/ru/alkoleft/bsl/doc/bsl/symbols/Trees.java new file mode 100644 index 0000000..dbebb2b --- /dev/null +++ b/src/main/java/ru/alkoleft/bsl/doc/bsl/symbols/Trees.java @@ -0,0 +1,100 @@ +/* + * This file is a part of BSL Language Server. + * + * Copyright (c) 2018-2023 + * Alexey Sosnoviy , Nikita Fedkin and contributors + * + * SPDX-License-Identifier: LGPL-3.0-or-later + * + * BSL Language Server is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * BSL Language Server is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with BSL Language Server. + */ + +// from https://github.com/1c-syntax/bsl-language-server/blob/develop/src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/Trees.java + +package ru.alkoleft.bsl.doc.bsl.symbols; + +import com.github._1c_syntax.bsl.parser.BSLParser; +import lombok.experimental.UtilityClass; +import org.antlr.v4.runtime.Token; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +@UtilityClass +public class Trees { + private static final Set VALID_TOKEN_TYPES_FOR_COMMENTS_SEARCH = Set.of( + BSLParser.ANNOTATION_ATCLIENT_SYMBOL, + BSLParser.ANNOTATION_ATSERVERNOCONTEXT_SYMBOL, + BSLParser.ANNOTATION_ATCLIENTATSERVERNOCONTEXT_SYMBOL, + BSLParser.ANNOTATION_ATCLIENTATSERVER_SYMBOL, + BSLParser.ANNOTATION_ATSERVER_SYMBOL, + BSLParser.ANNOTATION_CUSTOM_SYMBOL, + BSLParser.ANNOTATION_UNKNOWN, + BSLParser.LINE_COMMENT, + BSLParser.WHITE_SPACE, + BSLParser.AMPERSAND + ); + + /** + * Поиск комментариев назад от указанного токена + * + * @param tokens - список токенов DocumentContext + * @param token - токен, для которого требуется найти комментарии + * @return - список найденных комментариев lines + */ + public static List getComments(List tokens, Token token) { + List comments = new ArrayList<>(); + fillCommentsCollection(tokens, token, comments); + return comments.stream() + .filter(it -> { + var text = it.getText(); + return !text.startsWith("// @skip") && !text.startsWith("// BSLLS:"); + }) + .collect(Collectors.toList()); + } + + private static void fillCommentsCollection(List tokens, Token currentToken, List lines) { + + int index = currentToken.getTokenIndex(); + + if (index == 0) { + return; + } + + var previousToken = tokens.get(index - 1); + + if (abortSearchComments(previousToken, currentToken)) { + return; + } + + fillCommentsCollection(tokens, previousToken, lines); + int type = previousToken.getType(); + if (type == BSLParser.LINE_COMMENT) { + lines.add(previousToken); + } + } + + private static boolean abortSearchComments(Token previousToken, Token currentToken) { + int type = previousToken.getType(); + return !VALID_TOKEN_TYPES_FOR_COMMENTS_SEARCH.contains(type) || isBlankLine(previousToken, currentToken); + } + + private static boolean isBlankLine(Token previousToken, Token currentToken) { + return previousToken.getType() == BSLParser.WHITE_SPACE + && (previousToken.getTokenIndex() == 0 + || (previousToken.getLine() + 1) != currentToken.getLine()); + } +} diff --git a/src/main/java/ru/alkoleft/bsl/doc/bsl/symbols/TypeDescription.java b/src/main/java/ru/alkoleft/bsl/doc/bsl/symbols/TypeDescription.java deleted file mode 100644 index f55df7c..0000000 --- a/src/main/java/ru/alkoleft/bsl/doc/bsl/symbols/TypeDescription.java +++ /dev/null @@ -1,37 +0,0 @@ -package ru.alkoleft.bsl.doc.bsl.symbols; - -import com.github._1c_syntax.bsl.languageserver.context.symbol.description.ParameterDescription; -import lombok.AllArgsConstructor; -import lombok.Value; - -import java.util.List; - -/** - * Описание типа параметра, прочитанного из описания метода - */ -@AllArgsConstructor -@Value -public class TypeDescription { - /** - * Имя типа. На данный момент может быть строковый массив перечисления типов а также гиперссылка на метод - */ - String name; - /** - * Описание типа. Может быть пустым - */ - String description; - /** - * Параметры (ключи или поля) типа для сложных типов данных. Может быть пустым - */ - List parameters; - /** - * Если описание параметров содержит только ссылку, то здесь будет ее значение - *

- * TODO Временное решение, надо будет продумать в следующем релизе - */ - String link; - /** - * Признак того, что параметр является гиперссылкой - */ - boolean isHyperlink; -} diff --git a/src/main/java/ru/alkoleft/bsl/doc/commands/RenderCommand.java b/src/main/java/ru/alkoleft/bsl/doc/commands/RenderCommand.java index 84d8b0f..8b6c4e8 100644 --- a/src/main/java/ru/alkoleft/bsl/doc/commands/RenderCommand.java +++ b/src/main/java/ru/alkoleft/bsl/doc/commands/RenderCommand.java @@ -1,56 +1,138 @@ package ru.alkoleft.bsl.doc.commands; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.NoArgsConstructor; import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; import picocli.CommandLine.Command; import picocli.CommandLine.Option; import picocli.CommandLine.Parameters; -import ru.alkoleft.bsl.doc.render.Render; -import ru.alkoleft.bsl.doc.bsl.BslContext; +import ru.alkoleft.bsl.doc.AutodocManager; import ru.alkoleft.bsl.doc.bsl.Filter; -import ru.alkoleft.bsl.doc.bsl.symbols.RegionSymbol; -import ru.alkoleft.bsl.doc.render.Factory; -import ru.alkoleft.bsl.doc.render.OutputFormat; -import ru.alkoleft.bsl.doc.render.RenderOptions; +import ru.alkoleft.bsl.doc.options.ChildLayout; +import ru.alkoleft.bsl.doc.options.ManualMergeStrategy; +import ru.alkoleft.bsl.doc.options.OutputFormat; +import ru.alkoleft.bsl.doc.options.OutputHierarchy; +import ru.alkoleft.bsl.doc.options.OutputOptions; -import java.nio.file.Files; import java.nio.file.Path; import java.util.List; +@Slf4j @Command(helpCommand = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor public class RenderCommand implements Runnable { @Parameters(description = "source") - Path sources; + private Path sources; + @Parameters(description = "destination") - Path destination; + private Path destination; + + @Option(names = {"-f", "--format"}) + private OutputFormat format; + + @Option(names = {"-h", "--hierarchy"}) + private OutputHierarchy hierarchy; - @Option(names = {"-f", "--format"}, defaultValue = "Markdown") - OutputFormat format; @Option(names = {"-s", "--only-subsystems"}) - List onlySubsystems; + private List onlySubsystems; + + @Option(names = {"-r", "--regions"}, split = " ") + private List regions; + + @Option(names = {"-m", "--manual-docs"}, description = "Path to manual documentations") + private Path manualDocumentation; + + @Option(names = {"-ms", "--merge-strategy"}, description = "Merge strategy for manual and generated documentation") + private ManualMergeStrategy manualMergeStrategy; + + @Option(names = {"-cl", "--child-layout"}, description = "Child pages layout") + private ChildLayout childLayout; + + @Option(names = {"--config"}, description = "Options file") + private Path optionsFile; + + RenderCommandOptions options() { + RenderCommandOptions options; + if (optionsFile != null) { + options = RenderCommandOptions.load(optionsFile); + } else { + options = new RenderCommandOptions(); + } + + if (format != null) { + options.setFormat(format); + } - @Option(names = {"-r", "--regions"}, split = " ", defaultValue = RegionSymbol.PUBLIC_REGION_RU + " " + RegionSymbol.PUBLIC_REGION_EN) - List regions; + if (onlySubsystems != null) { + options.setSubsystems(onlySubsystems); + } + + if (regions != null) { + options.setRegions(regions); + } + + if (manualDocumentation != null) { + options.setManualDocs(manualDocumentation); + } + + if (manualMergeStrategy != null) { + options.setMergeStrategy(manualMergeStrategy); + } + + if (childLayout != null) { + options.setChildLayout(childLayout); + } + + if (hierarchy != null) { + options.setHierarchy(hierarchy); + } + + return options; + } @SneakyThrows @Override public void run() { - var filter = Filter.builder() - .isExport(true); - regions.forEach(filter::region); + var commandOptions = options(); + + var filterBuilder = Filter.builder() + .isExport(true); + commandOptions.getRegions().forEach(filterBuilder::region); + commandOptions.getSubsystems().forEach(filterBuilder::rootSubsystem); + + var optionsBuilder = OutputOptions.builder() + .outputFormat(commandOptions.getFormat()) + .hierarchy(commandOptions.getHierarchy()) + .childLayout(commandOptions.getChildLayout()); - var options = RenderOptions.builder() - .outputFormat(format) - .subsystemHierarchy(true); - onlySubsystems.forEach(options::rootSubsystem); + var filter = filterBuilder.build(); + var options = optionsBuilder.build(); - var renderContext = Factory.createRenderContext(options.build()); - var render = new Render(renderContext); + log.debug("Filter: {}", filter.toString()); + log.debug("Options: {}", options.toString()); + log.debug("Sources: {}", sources); + log.debug("Manual: {}", commandOptions.getManualDocs()); + log.debug("Output: {}", destination); + log.debug("Merge manual: {}", commandOptions.getMergeStrategy()); - BslContext bslContext = new BslContext(sources, filter.build()); - bslContext.load(); + var manager = AutodocManager.builder() + .filter(filter) + .outputOptions(options) + .manualDocumentation(commandOptions.getManualDocs()) + .manualMergeStrategy(commandOptions.getMergeStrategy()) + .destination(destination) + .sources(sources) + .header(commandOptions.getHeaderContent()) + .footer(commandOptions.getFooterContent()) + .build(); - Files.createDirectories(destination); - render.render(bslContext, destination); + manager.loadData(); + manager.clearOutput(); + manager.generateDocumentation(); } } diff --git a/src/main/java/ru/alkoleft/bsl/doc/commands/RenderCommandOptions.java b/src/main/java/ru/alkoleft/bsl/doc/commands/RenderCommandOptions.java new file mode 100644 index 0000000..64c0f4f --- /dev/null +++ b/src/main/java/ru/alkoleft/bsl/doc/commands/RenderCommandOptions.java @@ -0,0 +1,64 @@ +package ru.alkoleft.bsl.doc.commands; + +import com.fasterxml.jackson.databind.ObjectMapper; +import edu.umd.cs.findbugs.annotations.Nullable; +import lombok.Data; +import lombok.SneakyThrows; +import ru.alkoleft.bsl.doc.bsl.symbols.RegionSymbol; +import ru.alkoleft.bsl.doc.options.ChildLayout; +import ru.alkoleft.bsl.doc.options.ManualMergeStrategy; +import ru.alkoleft.bsl.doc.options.OutputFormat; +import ru.alkoleft.bsl.doc.options.OutputHierarchy; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; + +@Data +public class RenderCommandOptions { + private List subsystems; + private List regions = List.of(RegionSymbol.PUBLIC_REGION_RU, RegionSymbol.PUBLIC_REGION_EN); + private Path manualDocs; + private ManualMergeStrategy mergeStrategy = ManualMergeStrategy.NONE; + private ChildLayout childLayout = ChildLayout.SAME_DIRECTORY; + private OutputFormat format = OutputFormat.Markdown; + private PageBlock header; + private PageBlock footer; + private OutputHierarchy hierarchy = OutputHierarchy.FLAT; + + @SneakyThrows + public static RenderCommandOptions load(Path path) { + ObjectMapper mapper = new ObjectMapper(); + return mapper.readValue(path.toFile(), RenderCommandOptions.class); + } + + public String getHeaderContent() { + if (header == null) { + return null; + } else { + return header.getBlockContent(); + } + } + + public String getFooterContent() { + if (footer == null) { + return null; + } else { + return footer.getBlockContent(); + } + } + + private record PageBlock(Path path, String content) { + @SneakyThrows + @Nullable + public String getBlockContent() { + if (content != null) { + return content; + } else if (path != null) { + return Files.readString(path); + } else { + return null; + } + } + } +} diff --git a/src/main/java/ru/alkoleft/bsl/doc/content/processor/MarkdownTitleProcessor.java b/src/main/java/ru/alkoleft/bsl/doc/content/processor/MarkdownTitleProcessor.java new file mode 100644 index 0000000..b4ae405 --- /dev/null +++ b/src/main/java/ru/alkoleft/bsl/doc/content/processor/MarkdownTitleProcessor.java @@ -0,0 +1,33 @@ +package ru.alkoleft.bsl.doc.content.processor; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.stream.Collectors; + +public class MarkdownTitleProcessor implements TitleProcessor { + @Override + public String cleanTitle(String content) { + return content.lines() + .filter(it -> !it.startsWith("# ")) + .collect(Collectors.joining("\n")); + } + + @Override + public String getTitle(String content) { + return null; + } + + @Override + public String getTitle(Path path) { + try (var lines = Files.lines(path)) { + return lines + .filter(it -> it.startsWith("# ")) + .findFirst() + .map(it -> it.substring(2).trim()).orElse(null); + } catch (IOException e) { + // nothing + } + return ""; + } +} diff --git a/src/main/java/ru/alkoleft/bsl/doc/content/processor/TitleProcessor.java b/src/main/java/ru/alkoleft/bsl/doc/content/processor/TitleProcessor.java new file mode 100644 index 0000000..0d16aff --- /dev/null +++ b/src/main/java/ru/alkoleft/bsl/doc/content/processor/TitleProcessor.java @@ -0,0 +1,27 @@ +package ru.alkoleft.bsl.doc.content.processor; + +import lombok.experimental.UtilityClass; +import ru.alkoleft.bsl.doc.options.OutputFormat; + +import java.nio.file.Path; + +public interface TitleProcessor { + static TitleProcessor getInstance() { + return Factory.instance; + } + + String cleanTitle(String content); + + String getTitle(String content); + + String getTitle(Path path); + + @UtilityClass + class Factory { + TitleProcessor instance; + + public TitleProcessor create(OutputFormat format) { + return instance = new MarkdownTitleProcessor(); + } + } +} diff --git a/src/main/java/ru/alkoleft/bsl/doc/manual/ManualContent.java b/src/main/java/ru/alkoleft/bsl/doc/manual/ManualContent.java new file mode 100644 index 0000000..4919849 --- /dev/null +++ b/src/main/java/ru/alkoleft/bsl/doc/manual/ManualContent.java @@ -0,0 +1,65 @@ +package ru.alkoleft.bsl.doc.manual; + +import lombok.SneakyThrows; +import org.apache.commons.io.FileUtils; +import ru.alkoleft.bsl.doc.model.ContentModel; +import ru.alkoleft.bsl.doc.model.ContentModelBuilder; +import ru.alkoleft.bsl.doc.model.Page; +import ru.alkoleft.bsl.doc.options.OutputFormat; + +import java.nio.file.Path; + +public class ManualContent { + private final Path location; + private final Path destination; + private ContentModel localModel; + private ContentModel destinationModel; + + public ManualContent(Path location, Path destination) { + this.location = location; + this.destination = destination; + } + + public void buildModel(OutputFormat format) { + if (location == null) { + return; + } + localModel = ContentModelBuilder.build(location, format); + destinationModel = null; + } + + public void copy() { + if (location == null) { + return; + } + copyFolder(location, destination); + destinationModel = new ContentModel(); + localModel.getPages().stream() + .map(it -> createDestinationPage(it, location, destination)) + .forEach(destinationModel.getPages()::add); + } + + public boolean isNotContains(Path path) { + if (destinationModel == null) { + return true; + } + return destinationModel.getPages() + .stream() + .map(Page::getPath) + .noneMatch(it -> it.equals(path)); + } + + public ContentModel getContentModel() { + return destinationModel; + } + + private Page createDestinationPage(Page localPage, Path local, Path dest) { + var destPath = dest.resolve(local.relativize(localPage.getPath())); + return new Page(destPath, localPage.getTitle(), localPage.getType()); + } + + @SneakyThrows + private void copyFolder(Path src, Path dest) { + FileUtils.copyDirectory(src.toFile(), dest.toFile()); + } +} diff --git a/src/main/java/ru/alkoleft/bsl/doc/model/ContentModel.java b/src/main/java/ru/alkoleft/bsl/doc/model/ContentModel.java new file mode 100644 index 0000000..708f976 --- /dev/null +++ b/src/main/java/ru/alkoleft/bsl/doc/model/ContentModel.java @@ -0,0 +1,42 @@ +package ru.alkoleft.bsl.doc.model; + +import lombok.Value; + +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.stream.Collectors; + +@Value +public class ContentModel { + List pages = new ArrayList<>(); + + public Page append(Path path) { + var page = pages.stream() + .filter(it -> path.equals(it.getPath())) + .findAny() + .orElse(null); + + if (page == null) { + pages.add(page = new Page(path, null, PageType.UNKNOWN)); + } + return page; + } + + public List getChildrenPages(Path path) { + return pages.stream() + .filter(it -> isChild(it, path)) + .sorted(Comparator.comparing(Page::getPath)) + .collect(Collectors.toList()); + } + + boolean isChild(Page page, Path path) { + var pagePath = page.getPath(); + if (pagePath.getFileName().toString().startsWith("index.")) { + return pagePath.getParent().getParent().equals(path); + } else { + return pagePath.getParent().equals(path); + } + } +} diff --git a/src/main/java/ru/alkoleft/bsl/doc/model/ContentModelBuilder.java b/src/main/java/ru/alkoleft/bsl/doc/model/ContentModelBuilder.java new file mode 100644 index 0000000..62fdd6e --- /dev/null +++ b/src/main/java/ru/alkoleft/bsl/doc/model/ContentModelBuilder.java @@ -0,0 +1,35 @@ +package ru.alkoleft.bsl.doc.model; + +import lombok.experimental.UtilityClass; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.io.FilenameUtils; +import ru.alkoleft.bsl.doc.content.processor.TitleProcessor; +import ru.alkoleft.bsl.doc.options.OutputFormat; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +@Slf4j +@UtilityClass +public class ContentModelBuilder { + public ContentModel build(Path location, OutputFormat format) { + var model = new ContentModel(); + + try (var stream = Files.walk(location)) { + stream.filter(path -> path.toFile().isFile()) + .filter(path -> matchFileExtension(path, format.getExtension())) + .map(it -> new Page(it, it.getFileName().getName(0).toString(), PageType.MANUAL)) + .peek(it -> it.setTitle(TitleProcessor.getInstance().getTitle(it.getPath()))) + .forEach(model.getPages()::add); + } catch (IOException e) { + log.error("Error cleaning", e); + } + + return model; + } + + private boolean matchFileExtension(Path path, String fileExtension) { + return FilenameUtils.getExtension(path.toString()).equals(fileExtension); + } +} diff --git a/src/main/java/ru/alkoleft/bsl/doc/model/Links.java b/src/main/java/ru/alkoleft/bsl/doc/model/Links.java new file mode 100644 index 0000000..c2b0db6 --- /dev/null +++ b/src/main/java/ru/alkoleft/bsl/doc/model/Links.java @@ -0,0 +1,20 @@ +package ru.alkoleft.bsl.doc.model; + +import lombok.Setter; +import lombok.experimental.UtilityClass; + +import java.nio.file.Path; + +@UtilityClass +public class Links { + @Setter + private Path currentPath; + + public String getPageLink(Page from, Page to) { + return from.getPath().relativize(to.getPath()).toString(); + } + + public String getPageLink(Page to) { + return currentPath.getParent().relativize(to.getPath()).toString(); + } +} diff --git a/src/main/java/ru/alkoleft/bsl/doc/model/Page.java b/src/main/java/ru/alkoleft/bsl/doc/model/Page.java new file mode 100644 index 0000000..b0cd1a2 --- /dev/null +++ b/src/main/java/ru/alkoleft/bsl/doc/model/Page.java @@ -0,0 +1,25 @@ +package ru.alkoleft.bsl.doc.model; + +import lombok.AllArgsConstructor; +import lombok.Data; +import ru.alkoleft.bsl.doc.content.processor.TitleProcessor; + +import java.nio.file.Path; + +@Data +@AllArgsConstructor +public class Page { + private Path path; + private String title; + private PageType type; + + public String getTitle() { + if (title != null) { + return title; + } else if (path == null) { + return null; + } + + return title = TitleProcessor.getInstance().getTitle(path); + } +} diff --git a/src/main/java/ru/alkoleft/bsl/doc/model/PageType.java b/src/main/java/ru/alkoleft/bsl/doc/model/PageType.java new file mode 100644 index 0000000..0e55542 --- /dev/null +++ b/src/main/java/ru/alkoleft/bsl/doc/model/PageType.java @@ -0,0 +1,8 @@ +package ru.alkoleft.bsl.doc.model; + +public enum PageType { + UNKNOWN, + SUBSYSTEM, + MODULE, + MANUAL +} diff --git a/src/main/java/ru/alkoleft/bsl/doc/options/ChildLayout.java b/src/main/java/ru/alkoleft/bsl/doc/options/ChildLayout.java new file mode 100644 index 0000000..f9ec591 --- /dev/null +++ b/src/main/java/ru/alkoleft/bsl/doc/options/ChildLayout.java @@ -0,0 +1,6 @@ +package ru.alkoleft.bsl.doc.options; + +public enum ChildLayout { + SUB_DIRECTORY, + SAME_DIRECTORY +} diff --git a/src/main/java/ru/alkoleft/bsl/doc/options/ManualMergeStrategy.java b/src/main/java/ru/alkoleft/bsl/doc/options/ManualMergeStrategy.java new file mode 100644 index 0000000..9d17a64 --- /dev/null +++ b/src/main/java/ru/alkoleft/bsl/doc/options/ManualMergeStrategy.java @@ -0,0 +1,8 @@ +package ru.alkoleft.bsl.doc.options; + +public enum ManualMergeStrategy { + NONE, + REPLACE, + APPEND, + MERGE +} diff --git a/src/main/java/ru/alkoleft/bsl/doc/options/OutputFormat.java b/src/main/java/ru/alkoleft/bsl/doc/options/OutputFormat.java new file mode 100644 index 0000000..124baaa --- /dev/null +++ b/src/main/java/ru/alkoleft/bsl/doc/options/OutputFormat.java @@ -0,0 +1,16 @@ +package ru.alkoleft.bsl.doc.options; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum OutputFormat { + Docusaurus("docusaurus", "md"), + Markdown("md", "md"), + ConfluenceMarkdown("confluence-md", "md"); + + private final String path; + private final String extension; + +} diff --git a/src/main/java/ru/alkoleft/bsl/doc/options/OutputHierarchy.java b/src/main/java/ru/alkoleft/bsl/doc/options/OutputHierarchy.java new file mode 100644 index 0000000..cd1b88b --- /dev/null +++ b/src/main/java/ru/alkoleft/bsl/doc/options/OutputHierarchy.java @@ -0,0 +1,6 @@ +package ru.alkoleft.bsl.doc.options; + +public enum OutputHierarchy { + FLAT, + SUBSYSTEM +} diff --git a/src/main/java/ru/alkoleft/bsl/doc/options/OutputOptions.java b/src/main/java/ru/alkoleft/bsl/doc/options/OutputOptions.java new file mode 100644 index 0000000..31bb1a6 --- /dev/null +++ b/src/main/java/ru/alkoleft/bsl/doc/options/OutputOptions.java @@ -0,0 +1,15 @@ +package ru.alkoleft.bsl.doc.options; + +import lombok.Builder; +import lombok.Value; + +import java.nio.file.Path; + +@Builder +@Value +public class OutputOptions { + OutputFormat outputFormat; + OutputHierarchy hierarchy; + Path destination; + ChildLayout childLayout; +} diff --git a/src/main/java/ru/alkoleft/bsl/doc/render/BaseRender.java b/src/main/java/ru/alkoleft/bsl/doc/render/BaseRender.java new file mode 100644 index 0000000..04e2c81 --- /dev/null +++ b/src/main/java/ru/alkoleft/bsl/doc/render/BaseRender.java @@ -0,0 +1,26 @@ +package ru.alkoleft.bsl.doc.render; + +import com.github.jknack.handlebars.Context; +import lombok.Getter; +import lombok.experimental.UtilityClass; +import ru.alkoleft.bsl.doc.render.handlebars.RenderContext; + +import java.nio.file.Path; + +@UtilityClass +public class BaseRender { + @Getter + RenderContext renderContext; + + public void setContext(RenderContext renderContext) { + BaseRender.renderContext = renderContext; + } + + public void renderToFile(String templateName, Context context, Path output) { + renderContext.renderToFile(templateName, context, output); + } + + public String render(String templateName, Context context) { + return renderContext.render(templateName, context); + } +} diff --git a/src/main/java/ru/alkoleft/bsl/doc/render/BslRender.java b/src/main/java/ru/alkoleft/bsl/doc/render/BslRender.java new file mode 100644 index 0000000..3d055d5 --- /dev/null +++ b/src/main/java/ru/alkoleft/bsl/doc/render/BslRender.java @@ -0,0 +1,31 @@ +package ru.alkoleft.bsl.doc.render; + +import lombok.SneakyThrows; +import lombok.experimental.UtilityClass; +import lombok.extern.slf4j.Slf4j; +import ru.alkoleft.bsl.doc.bsl.ModuleInfo; +import ru.alkoleft.bsl.doc.render.contexts.ContextFactory; +import ru.alkoleft.bsl.doc.render.contexts.ModuleContext; +import ru.alkoleft.bsl.doc.render.contexts.SubsystemContext; + +@Slf4j +@UtilityClass +public class BslRender { + + @SneakyThrows + public String renderModule(ModuleInfo module, int index) { + return renderModule(ContextFactory.create(module, index)); + } + + @SneakyThrows + public String renderModule(ModuleContext module) { + log.debug("Render module '{}'", module.getName()); + return BaseRender.render("module", ContextFactory.createContext(module)); + } + + @SneakyThrows + public String renderSubsystem(SubsystemContext subsystem) { + log.debug("Render subsystem '{}'", subsystem.getName()); + return BaseRender.render("subsystem", ContextFactory.createContext(subsystem)); + } +} diff --git a/src/main/java/ru/alkoleft/bsl/doc/render/Factory.java b/src/main/java/ru/alkoleft/bsl/doc/render/Factory.java deleted file mode 100644 index 3bf7b1c..0000000 --- a/src/main/java/ru/alkoleft/bsl/doc/render/Factory.java +++ /dev/null @@ -1,20 +0,0 @@ -package ru.alkoleft.bsl.doc.render; - -import lombok.experimental.UtilityClass; -import ru.alkoleft.bsl.doc.render.handlebars.HandlebarsRenderContext; -import ru.alkoleft.bsl.doc.render.velocity.VelocityRenderContext; - -@UtilityClass -public class Factory { - public Render createRender(RenderOptions options) { - return new Render(null); - } - - public RenderContext createRenderContext(RenderOptions options) { - return new HandlebarsRenderContext(options.getOutputFormat().getPath()); - } - - public RenderContext createVelocityRenderContext(RenderOptions options) { - return new VelocityRenderContext(options.getOutputFormat().getPath()); - } -} diff --git a/src/main/java/ru/alkoleft/bsl/doc/render/ItemRender.java b/src/main/java/ru/alkoleft/bsl/doc/render/ItemRender.java deleted file mode 100644 index 8c0ddde..0000000 --- a/src/main/java/ru/alkoleft/bsl/doc/render/ItemRender.java +++ /dev/null @@ -1,10 +0,0 @@ -package ru.alkoleft.bsl.doc.render; - -import java.io.IOException; -import java.nio.file.Path; - -public interface ItemRender { - void put(String key, Object value); - - void renderToFile(Path fileName) throws IOException; -} diff --git a/src/main/java/ru/alkoleft/bsl/doc/render/OutputFormat.java b/src/main/java/ru/alkoleft/bsl/doc/render/OutputFormat.java deleted file mode 100644 index 0b5fd20..0000000 --- a/src/main/java/ru/alkoleft/bsl/doc/render/OutputFormat.java +++ /dev/null @@ -1,15 +0,0 @@ -package ru.alkoleft.bsl.doc.render; - -import lombok.Getter; - -public enum OutputFormat { - Docusaurus("docusaurus"), - Markdown("md"); - - @Getter - private final String path; - - OutputFormat(String path) { - this.path = path; - } -} diff --git a/src/main/java/ru/alkoleft/bsl/doc/render/PathResolver.java b/src/main/java/ru/alkoleft/bsl/doc/render/PathResolver.java new file mode 100644 index 0000000..6161cca --- /dev/null +++ b/src/main/java/ru/alkoleft/bsl/doc/render/PathResolver.java @@ -0,0 +1,44 @@ +package ru.alkoleft.bsl.doc.render; + +import com.github._1c_syntax.utils.Lazy; +import lombok.AllArgsConstructor; +import ru.alkoleft.bsl.doc.options.OutputFormat; + +import java.nio.file.Path; +import java.util.Stack; + +@AllArgsConstructor +public class PathResolver { + + private final Path destination; + private final OutputFormat format; + private final Stack paths = new Stack<>(); + private final Lazy currentPath = new Lazy<>(this::computeCurrentPath); + + public void entrance(String path) { + paths.push(path); + currentPath.clear(); + } + + public void exit() { + paths.pop(); + currentPath.clear(); + } + + public Path getCurrentPath() { + return currentPath.getOrCompute(); + } + + public Path getFilePath(String name) { + return getCurrentPath().resolve(name + "." + format.getExtension()); + } + + private Path computeCurrentPath() { + Path result = destination; + var iterator = paths.elements().asIterator(); + while (iterator.hasNext()) { + result = result.resolve(iterator.next()); + } + return result; + } +} diff --git a/src/main/java/ru/alkoleft/bsl/doc/render/Render.java b/src/main/java/ru/alkoleft/bsl/doc/render/Render.java deleted file mode 100644 index 94b6a44..0000000 --- a/src/main/java/ru/alkoleft/bsl/doc/render/Render.java +++ /dev/null @@ -1,46 +0,0 @@ -package ru.alkoleft.bsl.doc.render; - -import com.github._1c_syntax.mdclasses.mdo.AbstractMDObjectBSL; -import lombok.SneakyThrows; -import ru.alkoleft.bsl.doc.bsl.BslContext; -import ru.alkoleft.bsl.doc.bsl.ModuleContext; -import ru.alkoleft.bsl.doc.render.RenderContext; - -import java.nio.file.Path; -import java.util.concurrent.atomic.AtomicInteger; - -public class Render { - - RenderContext renderContext; - - public Render(RenderContext renderContext) { - this.renderContext = renderContext; - } - - public void render(BslContext bslContext, Path output) { - AtomicInteger index = new AtomicInteger(); - - renderContext.setContext(bslContext); - bslContext.getModules().forEach(it -> renderModule(it, output, index.getAndIncrement())); - } - - @SneakyThrows - public void renderModule(ModuleContext module, Path outputPath, int index) { - var itemRender = renderContext.getRender("module"); - - itemRender.put("index", index); - itemRender.put("name", module.getOwner().getName()); - itemRender.put("present", getPresent(module.getOwner())); - itemRender.put("methods", module.getMethods()); - itemRender.put("description", module.getDescription()); - itemRender.renderToFile(outputPath.resolve(module.getOwner().getName() + ".md")); - } - - private String getPresent(AbstractMDObjectBSL object) { - if (object.getSynonyms().isEmpty()) { - return object.getName(); - } else { - return object.getSynonyms().get(0).getContent(); - } - } -} diff --git a/src/main/java/ru/alkoleft/bsl/doc/render/RenderContext.java b/src/main/java/ru/alkoleft/bsl/doc/render/RenderContext.java deleted file mode 100644 index 81fe585..0000000 --- a/src/main/java/ru/alkoleft/bsl/doc/render/RenderContext.java +++ /dev/null @@ -1,11 +0,0 @@ -package ru.alkoleft.bsl.doc.render; - -import ru.alkoleft.bsl.doc.bsl.BslContext; - -import java.io.IOException; - -public interface RenderContext { - ItemRender getRender(String name) throws IOException; - - void setContext(BslContext context); -} diff --git a/src/main/java/ru/alkoleft/bsl/doc/render/RenderOptions.java b/src/main/java/ru/alkoleft/bsl/doc/render/RenderOptions.java deleted file mode 100644 index 20f9758..0000000 --- a/src/main/java/ru/alkoleft/bsl/doc/render/RenderOptions.java +++ /dev/null @@ -1,17 +0,0 @@ -package ru.alkoleft.bsl.doc.render; - -import lombok.Builder; -import lombok.Singular; -import lombok.Value; - -import java.util.List; - -@Builder -@Value -public class RenderOptions { - OutputFormat outputFormat; - - boolean subsystemHierarchy; - @Singular - List rootSubsystems; -} diff --git a/src/main/java/ru/alkoleft/bsl/doc/render/StructureRender.java b/src/main/java/ru/alkoleft/bsl/doc/render/StructureRender.java new file mode 100644 index 0000000..ce29722 --- /dev/null +++ b/src/main/java/ru/alkoleft/bsl/doc/render/StructureRender.java @@ -0,0 +1,115 @@ +package ru.alkoleft.bsl.doc.render; + +import lombok.SneakyThrows; +import ru.alkoleft.bsl.doc.bsl.BslContext; +import ru.alkoleft.bsl.doc.bsl.helpers.MDOHelper; +import ru.alkoleft.bsl.doc.model.ContentModel; +import ru.alkoleft.bsl.doc.model.Links; +import ru.alkoleft.bsl.doc.model.PageType; +import ru.alkoleft.bsl.doc.options.ChildLayout; +import ru.alkoleft.bsl.doc.options.OutputOptions; +import ru.alkoleft.bsl.doc.render.contexts.ContextFactory; +import ru.alkoleft.bsl.doc.render.output.OutputStrategy; +import ru.alkoleft.bsl.doc.structure.Item; +import ru.alkoleft.bsl.doc.structure.MDObjectItem; +import ru.alkoleft.bsl.doc.structure.ModuleItem; +import ru.alkoleft.bsl.doc.structure.StructureVisitor; +import ru.alkoleft.bsl.doc.structure.SubsystemItem; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; + +public class StructureRender implements StructureVisitor { + private final OutputStrategy outputStrategy; + private final ContentModel contentModel; + private final OutputOptions outputOptions; + private boolean withRoot = false; + private int subsystemLevel = 0; + private PathResolver pathResolver; + + public StructureRender(OutputOptions outputOptions, OutputStrategy outputStrategy, ContentModel contentModel) { + this.outputStrategy = outputStrategy; + this.contentModel = contentModel; + this.outputOptions = outputOptions; + } + + public void render(List structure, Path destination) { + pathResolver = new PathResolver(destination, outputStrategy.getFormat()); + subsystemLevel = 0; + withRoot = outputOptions.getChildLayout() == ChildLayout.SUB_DIRECTORY || structure.size() > 1; + + for (int i = 0; i < structure.size(); i++) { + structure.get(i).accept(this, i); + } + } + + @Override + public void visit(SubsystemItem item, int index) { + var isRoot = subsystemLevel == 0; + subsystemLevel++; + if (!withRoot && isRoot) { + item.accentChildren(this); + renderSubsystemPage(item); + } else { + pathResolver.entrance(item.getName()); + item.accentChildren(this); + if (outputOptions.getChildLayout() == ChildLayout.SAME_DIRECTORY) { + renderSubsystemPage(item); + pathResolver.exit(); + } else { + pathResolver.exit(); + renderSubsystemPage(item); + } + } + subsystemLevel--; + } + + @Override + @SneakyThrows + public void visit(ModuleItem item, int index) { + var moduleContext = BslContext.getCurrent().getModuleContext(item.getModule()); + if (moduleContext.isEmpty()) { + return; + } + var path = pathResolver.getFilePath(MDOHelper.getOwner(item.getModule()).getMdoReference().getMdoRefRu()); + if (!Files.exists(path.getParent())) { + Files.createDirectories(path.getParent()); + } + if (outputStrategy.needRender(path)) { + Links.setCurrentPath(path); + var content = BslRender.renderModule(moduleContext, index); + outputStrategy.save(path, content) + .setType(PageType.MODULE); + } + } + + @Override + public void visit(MDObjectItem item, int index) { + item.accentChildren(this); + } + + private void renderSubsystemPage(SubsystemItem item) { + var path = getSubsystemPagePath(item); + + if (outputStrategy.needRender(path)) { + var context = ContextFactory.create(item.getSubsystem(), 0, subsystemLevel); + context.setContentModel(contentModel); + if (outputOptions.getChildLayout() == ChildLayout.SAME_DIRECTORY) { + context.setOutputPath(path.getParent()); + } else { + context.setOutputPath(path.getParent().resolve(item.getName())); + } + Links.setCurrentPath(path); + var content = BslRender.renderSubsystem(context); + outputStrategy.save(path, content) + .setType(PageType.SUBSYSTEM); + } + } + + private Path getSubsystemPagePath(SubsystemItem item) { + return outputOptions.getChildLayout() == ChildLayout.SAME_DIRECTORY + ? pathResolver.getFilePath("index") + : pathResolver.getFilePath(item.getName()); + } +} diff --git a/src/main/java/ru/alkoleft/bsl/doc/render/StructureStrategy.java b/src/main/java/ru/alkoleft/bsl/doc/render/StructureStrategy.java deleted file mode 100644 index 553ab9a..0000000 --- a/src/main/java/ru/alkoleft/bsl/doc/render/StructureStrategy.java +++ /dev/null @@ -1,64 +0,0 @@ -package ru.alkoleft.bsl.doc.render; - -import com.github._1c_syntax.bsl.mdo.MD; -import com.github._1c_syntax.bsl.mdo.MDObject; -import com.github._1c_syntax.mdclasses.Configuration; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; - -public abstract class StructureStrategy { - - protected Path path; - protected String extension; - - StructureStrategy(Path path, String extension) throws IOException { - this.path = path; - this.extension = extension; - - createIfNotExists(path); - } - - public Path getPath(MDObject object) throws IOException { - Path objectPath = path.resolve(getObjectPath(object)); - createIfNotExists(objectPath.getParent()); - return objectPath; - - } - - public Path getPath(MD object) { - return path.resolve(getFileName(object)); - } - - public String getLink(MDObject object) { - return "../" + getObjectPath(object); - } - - protected abstract String getObjectPath(MDObject object); - - public String getFileName(MD object) { - return String.format("%s.%s", object.getName(), extension); - } - - public String getFileName(Configuration object) { - return String.format("configuration.%s", extension); - } - - protected void createIfNotExists(Path path) throws IOException { - if (!Files.exists(path)) { - Files.createDirectories(path); - } - } - - static class Metadata extends StructureStrategy { - public Metadata(Path path, String extension) throws IOException { - super(path, extension); - } - - @Override - public String getObjectPath(MDObject object) { - return Path.of(object.getMdoType().getGroupName(), getFileName(object)).toString(); - } - } -} diff --git a/src/main/java/ru/alkoleft/bsl/doc/render/TemplatesDefinition.java b/src/main/java/ru/alkoleft/bsl/doc/render/TemplatesDefinition.java new file mode 100644 index 0000000..a52c481 --- /dev/null +++ b/src/main/java/ru/alkoleft/bsl/doc/render/TemplatesDefinition.java @@ -0,0 +1,4 @@ +package ru.alkoleft.bsl.doc.render; + +public record TemplatesDefinition(String path, String headerTemplate, String footerTemplate) { +} diff --git a/src/main/java/ru/alkoleft/bsl/doc/render/UsedInHandlebars.java b/src/main/java/ru/alkoleft/bsl/doc/render/UsedInHandlebars.java new file mode 100644 index 0000000..9c8a55a --- /dev/null +++ b/src/main/java/ru/alkoleft/bsl/doc/render/UsedInHandlebars.java @@ -0,0 +1,18 @@ +package ru.alkoleft.bsl.doc.render; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Служебная аннотация для пометки методов и полей, которые используются в шаблонах + */ +@Target({ElementType.METHOD, ElementType.FIELD}) +@Retention(RetentionPolicy.SOURCE) +public @interface UsedInHandlebars { + /** + * Имя шаблона, где используется + */ + String value(); +} diff --git a/src/main/java/ru/alkoleft/bsl/doc/render/contexts/BaseContext.java b/src/main/java/ru/alkoleft/bsl/doc/render/contexts/BaseContext.java new file mode 100644 index 0000000..758b2f1 --- /dev/null +++ b/src/main/java/ru/alkoleft/bsl/doc/render/contexts/BaseContext.java @@ -0,0 +1,20 @@ +package ru.alkoleft.bsl.doc.render.contexts; + +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +import java.nio.file.Path; + +@Getter +@AllArgsConstructor +@EqualsAndHashCode +public class BaseContext { + private final int index; + private final String name; + private final String present; + private final String description; + @Setter + private Path outputPath; +} diff --git a/src/main/java/ru/alkoleft/bsl/doc/render/contexts/ContextFactory.java b/src/main/java/ru/alkoleft/bsl/doc/render/contexts/ContextFactory.java new file mode 100644 index 0000000..7bc0d33 --- /dev/null +++ b/src/main/java/ru/alkoleft/bsl/doc/render/contexts/ContextFactory.java @@ -0,0 +1,52 @@ +package ru.alkoleft.bsl.doc.render.contexts; + +import com.github._1c_syntax.bsl.mdo.CommonModule; +import com.github._1c_syntax.bsl.mdo.Subsystem; +import com.github.jknack.handlebars.Context; +import com.github.jknack.handlebars.context.FieldValueResolver; +import com.github.jknack.handlebars.context.JavaBeanValueResolver; +import com.github.jknack.handlebars.context.MapValueResolver; +import com.github.jknack.handlebars.context.MethodValueResolver; +import lombok.experimental.UtilityClass; +import ru.alkoleft.bsl.doc.bsl.ModuleInfo; +import ru.alkoleft.bsl.doc.bsl.helpers.MDOHelper; + +@UtilityClass +public class ContextFactory { + + public ModuleContext create(ModuleInfo module, int index) { + return ModuleContext.builder() + .index(index) + .name(module.getOwner().getName()) + .present(MDOHelper.getPresent(module.getOwner())) + .isCommonModule(module.getOwner() instanceof CommonModule) + .ownerType(module.getOwner().getMdoType().nameRu()) + .moduleType(MDOHelper.getPresent(module.getModule().getModuleType())) + .methods(module.getMethods()) + .description(module.getDescription()) + .build(); + } + + public SubsystemContext create(Subsystem subsystem, int index, int level) { + return SubsystemContext.builder() + .subsystem(subsystem) + .index(index) + .name(subsystem.getName()) + .present(MDOHelper.getPresent(subsystem)) + .description(subsystem.getComment()) + .explanation(subsystem.getExplanation().get("ru")) // Берем русскую локаль. Если ее не будет, то вернет любую + .level(level) + .build(); + } + + public Context createContext(Object obj) { + return Context.newBuilder(obj) + .resolver( + MapValueResolver.INSTANCE, + JavaBeanValueResolver.INSTANCE, + MethodValueResolver.INSTANCE, + FieldValueResolver.INSTANCE + ) + .build(); + } +} diff --git a/src/main/java/ru/alkoleft/bsl/doc/render/contexts/ModuleContext.java b/src/main/java/ru/alkoleft/bsl/doc/render/contexts/ModuleContext.java new file mode 100644 index 0000000..8a75d79 --- /dev/null +++ b/src/main/java/ru/alkoleft/bsl/doc/render/contexts/ModuleContext.java @@ -0,0 +1,35 @@ +package ru.alkoleft.bsl.doc.render.contexts; + +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Value; +import ru.alkoleft.bsl.doc.bsl.symbols.MethodSymbol; + +import java.nio.file.Path; +import java.util.List; + +@EqualsAndHashCode(callSuper = true) +@Value +public class ModuleContext extends BaseContext { + boolean isCommonModule; + String ownerType; + String moduleType; + List methods; + + @Builder + public ModuleContext(int index, + String name, + String present, + String description, + Path outputPath, + boolean isCommonModule, + String ownerType, + String moduleType, + List methods) { + super(index, name, present, description, outputPath); + this.isCommonModule = isCommonModule; + this.ownerType = ownerType; + this.moduleType = moduleType; + this.methods = methods; + } +} diff --git a/src/main/java/ru/alkoleft/bsl/doc/render/contexts/SubsystemContext.java b/src/main/java/ru/alkoleft/bsl/doc/render/contexts/SubsystemContext.java new file mode 100644 index 0000000..3102c93 --- /dev/null +++ b/src/main/java/ru/alkoleft/bsl/doc/render/contexts/SubsystemContext.java @@ -0,0 +1,62 @@ +package ru.alkoleft.bsl.doc.render.contexts; + +import com.github._1c_syntax.bsl.mdo.Constant; +import com.github._1c_syntax.bsl.mdo.Subsystem; +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import ru.alkoleft.bsl.doc.bsl.BslContext; +import ru.alkoleft.bsl.doc.model.ContentModel; +import ru.alkoleft.bsl.doc.model.Page; +import ru.alkoleft.bsl.doc.render.UsedInHandlebars; + +import java.nio.file.Path; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +@EqualsAndHashCode(callSuper = true) +@Getter +public class SubsystemContext extends BaseContext { + private final int level; + private final Subsystem subsystem; + /** + * Описание подсистемы + */ + private final String explanation; + @Setter + private ContentModel contentModel; + + @Builder + public SubsystemContext(int index, + String name, + String present, + String description, + Path outputPath, + int level, + Subsystem subsystem, + String explanation) { + super(index, name, present, description, outputPath); + this.level = level; + this.subsystem = subsystem; + this.explanation = explanation; + } + + @UsedInHandlebars("subsystems.hbs") + public List getConstants() { + return BslContext.getCurrent().getSubsystemObjects(subsystem) + .filter(Constant.class::isInstance) + .map(Constant.class::cast) + .collect(Collectors.toList()); + } + + @UsedInHandlebars("subsystems.hbs") + public List getChildrenPages() { + if (contentModel != null) { + return contentModel.getChildrenPages(getOutputPath()); + } else { + return Collections.emptyList(); + } + } +} diff --git a/src/main/java/ru/alkoleft/bsl/doc/render/handlebars/HandleLinks.java b/src/main/java/ru/alkoleft/bsl/doc/render/handlebars/HandleLinks.java deleted file mode 100644 index 86a1585..0000000 --- a/src/main/java/ru/alkoleft/bsl/doc/render/handlebars/HandleLinks.java +++ /dev/null @@ -1,36 +0,0 @@ -package ru.alkoleft.bsl.doc.render.handlebars; - -import com.github.jknack.handlebars.Helper; -import com.github.jknack.handlebars.Options; -import ru.alkoleft.bsl.doc.bsl.BslContext; - -import java.io.IOException; -import java.util.Locale; -import java.util.regex.Pattern; - -public class HandleLinks implements Helper { - Pattern pattern = Pattern.compile("см\\. (([\\wА-Яа-я\\.\\d]+)\\.)*([\\wА-Яа-я\\d]+)"); - - BslContext context; - - @Override - public Object apply(String context, Options options) throws IOException { - var matcher = pattern.matcher(context); - if (matcher.find()) { - return matcher.replaceAll(matchResult -> String.format("[%s](%s)", matchResult.group(0), getLink(matcher.group(2), matcher.group(3)))); - } - return context; - } - - private String getLink(String owner, String method) { - if (owner != null && method != null) { - return owner + "#" + method.toLowerCase(Locale.ROOT); - } else if (context.contains(method)) { - return method; - } else if (method != null) { - return "#" + method.toLowerCase(Locale.ROOT); - } else{ - return ""; - } - } -} diff --git a/src/main/java/ru/alkoleft/bsl/doc/render/handlebars/HandlebarItemRender.java b/src/main/java/ru/alkoleft/bsl/doc/render/handlebars/HandlebarItemRender.java deleted file mode 100644 index 80cc207..0000000 --- a/src/main/java/ru/alkoleft/bsl/doc/render/handlebars/HandlebarItemRender.java +++ /dev/null @@ -1,33 +0,0 @@ -package ru.alkoleft.bsl.doc.render.handlebars; - -import com.github.jknack.handlebars.Template; -import ru.alkoleft.bsl.doc.render.ItemRender; - -import java.io.FileWriter; -import java.io.IOException; -import java.nio.file.Path; -import java.util.HashMap; -import java.util.Map; - -public class HandlebarItemRender implements ItemRender { - - private final Template template; - - HandlebarItemRender(Template template) { - this.template = template; - } - - Map context = new HashMap<>(); - - @Override - public void put(String key, Object value) { - context.put(key, value); - } - - @Override - public void renderToFile(Path fileName) throws IOException { - try (FileWriter writer = new FileWriter(fileName.toFile())) { - template.apply(context, writer); - } - } -} diff --git a/src/main/java/ru/alkoleft/bsl/doc/render/handlebars/HandlebarsRenderContext.java b/src/main/java/ru/alkoleft/bsl/doc/render/handlebars/HandlebarsRenderContext.java deleted file mode 100644 index 938c80c..0000000 --- a/src/main/java/ru/alkoleft/bsl/doc/render/handlebars/HandlebarsRenderContext.java +++ /dev/null @@ -1,45 +0,0 @@ -package ru.alkoleft.bsl.doc.render.handlebars; - -import com.github.jknack.handlebars.Handlebars; -import com.github.jknack.handlebars.Template; -import ru.alkoleft.bsl.doc.bsl.BslContext; -import ru.alkoleft.bsl.doc.render.RenderContext; - -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; - -public class HandlebarsRenderContext implements RenderContext { - - private final String path; - private final Handlebars handlebars; - private final Map loadedTemplates = new HashMap<>(); - - private final HandleLinks linksRender; - - public HandlebarsRenderContext(String path) { - this.path = path; - handlebars = new Handlebars().with(value -> value); - handlebars.registerHelper("links", linksRender = new HandleLinks()); - handlebars.registerHelper("shift", new Shifter()); - } - - public void setContext(BslContext context) { - linksRender.context = context; - } - - @Override - public HandlebarItemRender getRender(String name) throws IOException { - return new HandlebarItemRender(getTemplate(name)); - } - - private Template getTemplate(String name) throws IOException { - if (loadedTemplates.containsKey(name)) { - return loadedTemplates.get(name); - } - var location = String.format("%s/%s", path, name); - var template = handlebars.compile(location); - loadedTemplates.put(name, template); - return template; - } -} diff --git a/src/main/java/ru/alkoleft/bsl/doc/render/handlebars/RenderContext.java b/src/main/java/ru/alkoleft/bsl/doc/render/handlebars/RenderContext.java new file mode 100644 index 0000000..3bfba94 --- /dev/null +++ b/src/main/java/ru/alkoleft/bsl/doc/render/handlebars/RenderContext.java @@ -0,0 +1,126 @@ +package ru.alkoleft.bsl.doc.render.handlebars; + +import com.github.jknack.handlebars.Context; +import com.github.jknack.handlebars.Handlebars; +import com.github.jknack.handlebars.Helper; +import com.github.jknack.handlebars.Template; +import com.github.jknack.handlebars.cache.ConcurrentMapTemplateCache; +import com.github.jknack.handlebars.helper.ConditionalHelpers; +import com.github.jknack.handlebars.io.URLTemplateLoader; +import lombok.SneakyThrows; +import lombok.experimental.UtilityClass; +import lombok.extern.slf4j.Slf4j; +import ru.alkoleft.bsl.doc.bsl.BslContext; +import ru.alkoleft.bsl.doc.bsl.helpers.Strings; +import ru.alkoleft.bsl.doc.render.TemplatesDefinition; +import ru.alkoleft.bsl.doc.render.handlebars.helpers.Debugger; +import ru.alkoleft.bsl.doc.render.handlebars.helpers.Links; +import ru.alkoleft.bsl.doc.render.handlebars.helpers.MdoPresent; +import ru.alkoleft.bsl.doc.render.handlebars.helpers.PageLink; +import ru.alkoleft.bsl.doc.render.handlebars.helpers.Shifter; +import ru.alkoleft.bsl.doc.render.handlebars.helpers.SingleLine; + +import java.io.FileWriter; +import java.io.IOException; +import java.io.StringWriter; +import java.net.URL; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.Map; + +@Slf4j +public class RenderContext { + private final URL baseURL; + private final Handlebars handlebars; + private final Map loadedTemplates = new HashMap<>(); + private final Links linksRender; + private final TemplatesDefinition templatesDefinition; + + private RenderContext(TemplatesDefinition templatesDefinition) { + this.templatesDefinition = templatesDefinition; + this.baseURL = getClass().getClassLoader().getResource(templatesDefinition.path()); + handlebars = new Handlebars() + .with(new URLTemplateLoader() { + @Override + protected URL getResource(String location) throws IOException { + return new URL(baseURL + location); + } + }) + .with(new ConcurrentMapTemplateCache()); + + handlebars.registerHelper("great", ConditionalHelpers.gt); + handlebars.registerHelper("eq", ConditionalHelpers.eq); + + handlebars.registerHelper("links", linksRender = new Links()); + handlebars.registerHelper("mdo-present", new MdoPresent()); + handlebars.registerHelper("shift", new Shifter()); + handlebars.registerHelper("debug", new Debugger()); + handlebars.registerHelper("single-line", new SingleLine()); + handlebars.registerHelper("page-link", new PageLink()); + + // Хелпер 'add' для сложения чисел (уровней вложенности) + handlebars.registerHelper("add", (Helper) (context, options) -> { + // context — это первое число, options.param(0) — второе + int addition = options.param(0, 1); // 1 — значение по умолчанию + return context.intValue() + addition; + }); + + // Хелпер 'indent' для генерации пробелов + handlebars.registerHelper("indent", (context, options) -> { + int level = (context instanceof Number) ? ((Number) context).intValue() : 0; + // 2 пробела за каждый уровень + return " ".repeat(level); + }); + } + + public void setContext(BslContext context) { + linksRender.setContext(context); + } + + @SneakyThrows + public void renderToFile(String templateName, Context context, Path output) { + var template = getTemplate(templateName); + try (var writer = new FileWriter(output.toFile())) { + template.apply(context, writer); + } + } + + @SneakyThrows + public String render(String templateName, Context context) { + var template = getTemplate(templateName); + var writer = new StringWriter(); + template.apply(context, writer); + return writer.toString(); + } + + @SneakyThrows + private Template getTemplate(String name) { + if (loadedTemplates.containsKey(name)) { + return loadedTemplates.get(name); + } + Template template; + if (Strings.isNullOrEmpty(templatesDefinition.headerTemplate()) + && Strings.isNullOrEmpty(templatesDefinition.footerTemplate())) { + template = handlebars.compile(name); + } else { + var builder = new StringBuilder(); + if (!Strings.isNullOrEmpty(templatesDefinition.headerTemplate())) { + builder.append(templatesDefinition.headerTemplate()).append('\n'); + } + builder.append(handlebars.getLoader().sourceAt(name).content(handlebars.getCharset())); + if (!Strings.isNullOrEmpty(templatesDefinition.footerTemplate())) { + builder.append('\n').append(templatesDefinition.footerTemplate()); + } + template = handlebars.compileInline(builder.toString()); + } + loadedTemplates.put(name, template); + return template; + } + + @UtilityClass + public static class Factory { + public RenderContext create(TemplatesDefinition templatesDefinition) { + return new RenderContext(templatesDefinition); + } + } +} diff --git a/src/main/java/ru/alkoleft/bsl/doc/render/handlebars/helpers/Debugger.java b/src/main/java/ru/alkoleft/bsl/doc/render/handlebars/helpers/Debugger.java new file mode 100644 index 0000000..f268479 --- /dev/null +++ b/src/main/java/ru/alkoleft/bsl/doc/render/handlebars/helpers/Debugger.java @@ -0,0 +1,16 @@ +package ru.alkoleft.bsl.doc.render.handlebars.helpers; + +import com.github.jknack.handlebars.Helper; +import com.github.jknack.handlebars.Options; +import lombok.extern.slf4j.Slf4j; + +import java.io.IOException; + +@Slf4j +public class Debugger implements Helper { + @Override + public Object apply(Object context, Options options) throws IOException { + log.debug(options.fn().toString()); + return null; + } +} diff --git a/src/main/java/ru/alkoleft/bsl/doc/render/handlebars/helpers/Links.java b/src/main/java/ru/alkoleft/bsl/doc/render/handlebars/helpers/Links.java new file mode 100644 index 0000000..6fb4168 --- /dev/null +++ b/src/main/java/ru/alkoleft/bsl/doc/render/handlebars/helpers/Links.java @@ -0,0 +1,58 @@ +package ru.alkoleft.bsl.doc.render.handlebars.helpers; + +import com.github.jknack.handlebars.Context; +import com.github.jknack.handlebars.Helper; +import com.github.jknack.handlebars.Options; +import lombok.Setter; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import ru.alkoleft.bsl.doc.bsl.BslContext; + +import java.util.Locale; +import java.util.regex.Pattern; + +@Slf4j +public class Links implements Helper { + private static final Pattern LINK_PATTERN = Pattern.compile("см\\. (([\\wА-Яа-я\\.\\d]+)\\.)*([\\wА-Яа-я\\d]+)"); + private static final Pattern WARNING_PATTERN = Pattern.compile("(\\n\\s*\\n|^)(Важно[\\s\\S]+)(\\n\\s*\\n|$)"); + @Setter + private BslContext context; + + @Override + public Object apply(String context, Options options) { + return context; + } + + @SneakyThrows + private String replaceTo(String baseValue, ru.alkoleft.bsl.doc.bsl.Links.Link link, Options options) { + var methodInfo = BslContext.getCurrent().getMethodInfo(link); + if (methodInfo == null || methodInfo.isPublishing() || methodInfo.getMethod() == null) { + return String.format("[%s](%s)", baseValue, getLink(link)); + } + + var context = Context.newBuilder(methodInfo.getMethod().getReturnedValue()).build(); + return baseValue + options.apply(options.fn, context).toString(); + } + + private String handleWarning(String content) { + // TODO Реализовать и другие блоки https://docusaurus.io/docs/markdown-features/admonitions + if (content.contains("Важно")) { + var matcher = WARNING_PATTERN.matcher(content); + content = matcher + .replaceAll(m -> m.group(1) + ":::tip важно\n\n" + matcher.group(2) + "\n\n:::" + matcher.group(3)); + } + return content; + } + + private String getLink(ru.alkoleft.bsl.doc.bsl.Links.Link link) { + if (link.ownerName() != null && link.methodName() != null) { + return link.ownerName() + "#" + link.methodName().toLowerCase(Locale.ROOT); + } else if (link.ownerName() != null) { + return link.ownerName(); + } else if (link.methodName() != null) { + return "#" + link.methodName().toLowerCase(Locale.ROOT); + } else { + return ""; + } + } +} diff --git a/src/main/java/ru/alkoleft/bsl/doc/render/handlebars/helpers/MdoPresent.java b/src/main/java/ru/alkoleft/bsl/doc/render/handlebars/helpers/MdoPresent.java new file mode 100644 index 0000000..0831d44 --- /dev/null +++ b/src/main/java/ru/alkoleft/bsl/doc/render/handlebars/helpers/MdoPresent.java @@ -0,0 +1,20 @@ +package ru.alkoleft.bsl.doc.render.handlebars.helpers; + +import com.github._1c_syntax.bsl.mdo.MD; +import com.github.jknack.handlebars.Helper; +import com.github.jknack.handlebars.Options; +import ru.alkoleft.bsl.doc.bsl.helpers.MDOHelper; +import ru.alkoleft.bsl.doc.render.contexts.SubsystemContext; + +public class MdoPresent implements Helper { + @Override + public Object apply(Object context, Options options) { + if (context instanceof MD md) { + return MDOHelper.getPresent(md); + } else if (context instanceof SubsystemContext subsystemContext) { + return MDOHelper.getPresent(subsystemContext.getSubsystem()); + } else { + return context.toString(); + } + } +} diff --git a/src/main/java/ru/alkoleft/bsl/doc/render/handlebars/helpers/PageLink.java b/src/main/java/ru/alkoleft/bsl/doc/render/handlebars/helpers/PageLink.java new file mode 100644 index 0000000..a28e16f --- /dev/null +++ b/src/main/java/ru/alkoleft/bsl/doc/render/handlebars/helpers/PageLink.java @@ -0,0 +1,13 @@ +package ru.alkoleft.bsl.doc.render.handlebars.helpers; + +import com.github.jknack.handlebars.Helper; +import com.github.jknack.handlebars.Options; +import ru.alkoleft.bsl.doc.model.Links; +import ru.alkoleft.bsl.doc.model.Page; + +public class PageLink implements Helper { + @Override + public Object apply(Page context, Options options) { + return Links.getPageLink(context); + } +} diff --git a/src/main/java/ru/alkoleft/bsl/doc/render/handlebars/Shifter.java b/src/main/java/ru/alkoleft/bsl/doc/render/handlebars/helpers/Shifter.java similarity index 55% rename from src/main/java/ru/alkoleft/bsl/doc/render/handlebars/Shifter.java rename to src/main/java/ru/alkoleft/bsl/doc/render/handlebars/helpers/Shifter.java index 703434b..f1b6400 100644 --- a/src/main/java/ru/alkoleft/bsl/doc/render/handlebars/Shifter.java +++ b/src/main/java/ru/alkoleft/bsl/doc/render/handlebars/helpers/Shifter.java @@ -1,4 +1,4 @@ -package ru.alkoleft.bsl.doc.render.handlebars; +package ru.alkoleft.bsl.doc.render.handlebars.helpers; import com.github.jknack.handlebars.Helper; import com.github.jknack.handlebars.Options; @@ -10,13 +10,16 @@ public class Shifter implements Helper { @Override public Object apply(Object context, Options options) throws IOException { - Options.Buffer buffer = options.buffer(); - int startedShift = shift; + var buffer = options.buffer(); + var startedShift = shift; + + int delta = context instanceof Integer ? (Integer) context : 0; + shift += delta; shift++; var content = options.fn(); - shift--; - if (startedShift > 0) { - var newChar = "\n" + new String(new char[startedShift]).replace('\0', '\t'); + shift = startedShift; + if (startedShift + delta > 0) { + var newChar = "\n" + new String(new char[2 * (startedShift + delta)]).replace('\0', ' '); buffer.append(content.toString().replace("\n", newChar)); } else { buffer.append(content); diff --git a/src/main/java/ru/alkoleft/bsl/doc/render/handlebars/helpers/SingleLine.java b/src/main/java/ru/alkoleft/bsl/doc/render/handlebars/helpers/SingleLine.java new file mode 100644 index 0000000..0fea76e --- /dev/null +++ b/src/main/java/ru/alkoleft/bsl/doc/render/handlebars/helpers/SingleLine.java @@ -0,0 +1,24 @@ +package ru.alkoleft.bsl.doc.render.handlebars.helpers; + +import com.github.jknack.handlebars.Helper; +import com.github.jknack.handlebars.Options; +import com.github.jknack.handlebars.TagType; + +import java.io.IOException; + +public class SingleLine implements Helper { + @Override + public Object apply(Object context, Options options) throws IOException { + var buffer = options.buffer(); + + String content; + if (options.tagType == TagType.SECTION) { + content = options.fn().toString(); + } else { + content = context.toString(); + } + + buffer.append(content.replace("\n", "
")); + return buffer; + } +} diff --git a/src/main/java/ru/alkoleft/bsl/doc/render/output/AppendStrategy.java b/src/main/java/ru/alkoleft/bsl/doc/render/output/AppendStrategy.java new file mode 100644 index 0000000..cfb799b --- /dev/null +++ b/src/main/java/ru/alkoleft/bsl/doc/render/output/AppendStrategy.java @@ -0,0 +1,10 @@ +package ru.alkoleft.bsl.doc.render.output; + +import java.nio.file.Path; + +public class AppendStrategy extends OutputStrategy { + @Override + public boolean needRender(Path location) { + return manualContent.isNotContains(location); + } +} diff --git a/src/main/java/ru/alkoleft/bsl/doc/render/output/MergeStrategy.java b/src/main/java/ru/alkoleft/bsl/doc/render/output/MergeStrategy.java new file mode 100644 index 0000000..2139bfe --- /dev/null +++ b/src/main/java/ru/alkoleft/bsl/doc/render/output/MergeStrategy.java @@ -0,0 +1,32 @@ +package ru.alkoleft.bsl.doc.render.output; + +import com.github.jknack.handlebars.internal.Files; +import lombok.SneakyThrows; +import ru.alkoleft.bsl.doc.content.processor.TitleProcessor; +import ru.alkoleft.bsl.doc.model.Page; + +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.util.regex.Pattern; + +public class MergeStrategy extends OutputStrategy { + private static final Pattern REPLACE_PATTERN = + Pattern.compile("([\\w\\W]*)(^.*generated_content.*$\\n?)([\\w\\W]*)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); + + @SneakyThrows + @Override + public Page save(Path itemPath, String content) { + if (manualContent.isNotContains(itemPath)) { + return super.save(itemPath, content); + } + var fileContent = Files.read(itemPath.toFile(), StandardCharsets.UTF_8); + var parts = REPLACE_PATTERN.matcher(fileContent); + content = TitleProcessor.getInstance().cleanTitle(content); + if (parts.find()) { + var result = parts.group(1) + content + parts.group(3); + return super.save(itemPath, result); + } else { + return super.save(itemPath, content); + } + } +} diff --git a/src/main/java/ru/alkoleft/bsl/doc/render/output/OutputStrategy.java b/src/main/java/ru/alkoleft/bsl/doc/render/output/OutputStrategy.java new file mode 100644 index 0000000..76cff76 --- /dev/null +++ b/src/main/java/ru/alkoleft/bsl/doc/render/output/OutputStrategy.java @@ -0,0 +1,59 @@ +package ru.alkoleft.bsl.doc.render.output; + +import lombok.Getter; +import lombok.Setter; +import lombok.SneakyThrows; +import ru.alkoleft.bsl.doc.bsl.helpers.Strings; +import ru.alkoleft.bsl.doc.content.processor.TitleProcessor; +import ru.alkoleft.bsl.doc.manual.ManualContent; +import ru.alkoleft.bsl.doc.model.ContentModel; +import ru.alkoleft.bsl.doc.model.Page; +import ru.alkoleft.bsl.doc.options.ManualMergeStrategy; +import ru.alkoleft.bsl.doc.options.OutputFormat; + +import java.nio.file.Files; +import java.nio.file.Path; + +@Setter +@Getter +public class OutputStrategy { + protected OutputFormat format; + protected ManualContent manualContent; + protected ContentModel contentModel; + + public static OutputStrategy create(ManualMergeStrategy strategy) { + return switch (strategy) { + case APPEND -> new AppendStrategy(); + case MERGE -> new MergeStrategy(); + default -> new OutputStrategy(); + }; + } + + public boolean needRender(Path location) { + return true; + } + + protected Page addToContentModel(Path path, String content) { + var page = getContentModel().append(path); + + if (Strings.isNullOrEmpty(page.getTitle())) { + var title = TitleProcessor.getInstance().getTitle(content); + page.setTitle(title); + } + + return page; + } + + public void init(OutputFormat format, ManualContent manualContent, ContentModel contentModel) { + this.format = format; + this.manualContent = manualContent; + this.contentModel = contentModel; + } + + @SneakyThrows + public Page save(Path itemPath, String content) { + Files.createDirectories(itemPath.getParent()); + Files.writeString(itemPath, content); + return addToContentModel(itemPath, content); + } +} diff --git a/src/main/java/ru/alkoleft/bsl/doc/render/velocity/VelocityItemRender.java b/src/main/java/ru/alkoleft/bsl/doc/render/velocity/VelocityItemRender.java deleted file mode 100644 index b54d579..0000000 --- a/src/main/java/ru/alkoleft/bsl/doc/render/velocity/VelocityItemRender.java +++ /dev/null @@ -1,42 +0,0 @@ -package ru.alkoleft.bsl.doc.render.velocity; - -import org.apache.velocity.Template; -import org.apache.velocity.VelocityContext; -import ru.alkoleft.bsl.doc.render.ItemRender; - -import java.io.BufferedWriter; -import java.io.FileWriter; -import java.io.IOException; -import java.io.OutputStreamWriter; -import java.io.StringWriter; -import java.nio.file.Path; - -public class VelocityItemRender implements ItemRender { - private final Template template; - - VelocityItemRender(Template template) { - this.template = template; - } - - VelocityContext context = new VelocityContext(); - - public void put(String key, Object value) { - context.put(key, value); - } - - public void renderToFile(Path fileName) throws IOException { - try (FileWriter writer = new FileWriter(fileName.toFile())) { - template.merge(context, writer); - } - } - - public String renderToString() { - StringWriter writer = new StringWriter(); - template.merge(context, writer); - return writer.toString(); - } - - public void renderToConsole() { - template.merge(context, new BufferedWriter(new OutputStreamWriter(System.out))); - } -} diff --git a/src/main/java/ru/alkoleft/bsl/doc/render/velocity/VelocityRenderContext.java b/src/main/java/ru/alkoleft/bsl/doc/render/velocity/VelocityRenderContext.java deleted file mode 100644 index 2bfafb6..0000000 --- a/src/main/java/ru/alkoleft/bsl/doc/render/velocity/VelocityRenderContext.java +++ /dev/null @@ -1,44 +0,0 @@ -package ru.alkoleft.bsl.doc.render.velocity; - -import org.apache.velocity.Template; -import org.apache.velocity.app.VelocityEngine; -import ru.alkoleft.bsl.doc.bsl.BslContext; -import ru.alkoleft.bsl.doc.render.ItemRender; -import ru.alkoleft.bsl.doc.render.RenderContext; - -import java.io.BufferedWriter; -import java.io.IOException; -import java.io.OutputStreamWriter; -import java.io.StringWriter; -import java.util.Properties; - -public class VelocityRenderContext implements RenderContext { - VelocityEngine velocityEngine; - String path; - - public VelocityRenderContext(String name) { - path = name; - - velocityEngine = new VelocityEngine(); - Properties properties = new Properties(); - properties.setProperty("resource.loaders", "file"); -// properties.setProperty("runtime.log.logsystem.class", "org.apache.velocity.runtime.log.AvalonLogChute"); - properties.setProperty("resource.loader.file.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader"); - velocityEngine.init(properties); - velocityEngine.init(); - } - - public Template getTemplate(String name) { - return velocityEngine.getTemplate(String.format("%s/%s.vm", path, name)); - } - - @Override - public ItemRender getRender(String name) throws IOException { - return new VelocityItemRender(getTemplate(name)); - } - - @Override - public void setContext(BslContext context) { - - } -} diff --git a/src/main/java/ru/alkoleft/bsl/doc/structure/Factory.java b/src/main/java/ru/alkoleft/bsl/doc/structure/Factory.java new file mode 100644 index 0000000..04fb97c --- /dev/null +++ b/src/main/java/ru/alkoleft/bsl/doc/structure/Factory.java @@ -0,0 +1,44 @@ +package ru.alkoleft.bsl.doc.structure; + +import com.github._1c_syntax.bsl.mdo.MD; +import com.github._1c_syntax.bsl.mdo.ModuleOwner; +import com.github._1c_syntax.bsl.mdo.Subsystem; +import lombok.experimental.UtilityClass; +import ru.alkoleft.bsl.doc.bsl.BslContext; +import ru.alkoleft.bsl.doc.bsl.helpers.BslFilter; + +@UtilityClass +public class Factory { + public Item createSubSystemItem(Subsystem subsystem, BslContext context) { + var item = new SubsystemItem(subsystem); + fillChildrenSubsystems(item, context); + fillChildrenObjects(item, context); + return item; + } + + public Item createMDObjectItem(MD owner) { + var item = new MDObjectItem(owner); + if (owner instanceof ModuleOwner moduleOwner) { + moduleOwner.getModules() + .stream() + .filter(BslFilter::checkModule) + .map(ModuleItem::new) + .forEach(item.getChildren()::add); + } + return item; + } + + private void fillChildrenObjects(SubsystemItem item, BslContext context) { + context.getSubsystemObjects(item.getSubsystem()) + .map(Factory::createMDObjectItem) + .filter(it -> !it.getChildren().isEmpty()) + .forEach(item.getChildren()::add); + } + + private void fillChildrenSubsystems(SubsystemItem item, BslContext context) { + context.getChildrenSubsystems(item.getSubsystem()) + .map(it -> createSubSystemItem(it, context)) + .forEach(item.getChildren()::add); + } + +} diff --git a/src/main/java/ru/alkoleft/bsl/doc/structure/FlatStructureBuilder.java b/src/main/java/ru/alkoleft/bsl/doc/structure/FlatStructureBuilder.java new file mode 100644 index 0000000..7c404b3 --- /dev/null +++ b/src/main/java/ru/alkoleft/bsl/doc/structure/FlatStructureBuilder.java @@ -0,0 +1,16 @@ +package ru.alkoleft.bsl.doc.structure; + +import ru.alkoleft.bsl.doc.bsl.BslContext; + +import java.util.List; +import java.util.stream.Collectors; + +public class FlatStructureBuilder implements StructureBuilder { + + @Override + public List build(BslContext context) { + return context.getModules() + .map(ModuleItem::new) + .collect(Collectors.toList()); + } +} diff --git a/src/main/java/ru/alkoleft/bsl/doc/structure/Item.java b/src/main/java/ru/alkoleft/bsl/doc/structure/Item.java new file mode 100644 index 0000000..9fb1d2f --- /dev/null +++ b/src/main/java/ru/alkoleft/bsl/doc/structure/Item.java @@ -0,0 +1,33 @@ +package ru.alkoleft.bsl.doc.structure; + +import lombok.Getter; +import lombok.Setter; + +import java.util.ArrayList; +import java.util.List; + +@Getter +public abstract class Item { + private final Object object; + private final String name; + private final List children = new ArrayList<>(); + @Setter + private String pageName; + + public Item(Object object, String name) { + this.object = object; + this.name = name; + } + + public String getPresent() { + return name; + } + + public abstract void accept(StructureVisitor visitor, int index); + + public void accentChildren(StructureVisitor visitor) { + for (int i = 0; i < getChildren().size(); i++) { + getChildren().get(i).accept(visitor, i); + } + } +} diff --git a/src/main/java/ru/alkoleft/bsl/doc/structure/MDObjectItem.java b/src/main/java/ru/alkoleft/bsl/doc/structure/MDObjectItem.java new file mode 100644 index 0000000..2b60165 --- /dev/null +++ b/src/main/java/ru/alkoleft/bsl/doc/structure/MDObjectItem.java @@ -0,0 +1,23 @@ +package ru.alkoleft.bsl.doc.structure; + +import com.github._1c_syntax.bsl.mdo.MD; + +public class MDObjectItem extends Item { + public MDObjectItem(MD object) { + super(object, object.getName()); + } + + @Override + public String getPresent() { + return getMDObject().getMdoRef(); + } + + @Override + public void accept(StructureVisitor visitor, int index) { + visitor.visit(this, index); + } + + public MD getMDObject() { + return (MD) getObject(); + } +} diff --git a/src/main/java/ru/alkoleft/bsl/doc/structure/ModuleItem.java b/src/main/java/ru/alkoleft/bsl/doc/structure/ModuleItem.java new file mode 100644 index 0000000..9d06374 --- /dev/null +++ b/src/main/java/ru/alkoleft/bsl/doc/structure/ModuleItem.java @@ -0,0 +1,18 @@ +package ru.alkoleft.bsl.doc.structure; + +import com.github._1c_syntax.bsl.mdo.Module; + +public class ModuleItem extends Item { + public ModuleItem(Module module) { + super(module, module.getModuleType().name()); + } + + public Module getModule() { + return (Module) getObject(); + } + + @Override + public void accept(StructureVisitor visitor, int index) { + visitor.visit(this, index); + } +} diff --git a/src/main/java/ru/alkoleft/bsl/doc/structure/StructureBuilder.java b/src/main/java/ru/alkoleft/bsl/doc/structure/StructureBuilder.java new file mode 100644 index 0000000..71ae405 --- /dev/null +++ b/src/main/java/ru/alkoleft/bsl/doc/structure/StructureBuilder.java @@ -0,0 +1,43 @@ +package ru.alkoleft.bsl.doc.structure; + +import ru.alkoleft.bsl.doc.bsl.BslContext; +import ru.alkoleft.bsl.doc.options.OutputHierarchy; +import ru.alkoleft.bsl.doc.options.OutputOptions; + +import java.util.List; +import java.util.Objects; + +public interface StructureBuilder { + + static StructureBuilder builder(OutputOptions options) { + if (Objects.requireNonNull(options.getHierarchy()) == OutputHierarchy.SUBSYSTEM) { + return new SubsystemsStructureBuilder(); + } + return new FlatStructureBuilder(); + } + + static void print(List structure) { + print(structure, ""); + } + + private static void print(List structure, String prefix) { + Item item; + for (int index = 0; index < structure.size(); index++) { + item = structure.get(index); + if (index == structure.size() - 1) { + System.out.printf("%s└── %s\n", prefix, item.getPresent()); + if (!item.getChildren().isEmpty()) { + print(item.getChildren(), prefix + " "); + } + } else { + System.out.printf("%s├── %s\n", prefix, item.getPresent()); + if (!item.getChildren().isEmpty()) { + print(item.getChildren(), prefix + "│   "); + } + } + } + + } + + List build(BslContext context); +} diff --git a/src/main/java/ru/alkoleft/bsl/doc/structure/StructureVisitor.java b/src/main/java/ru/alkoleft/bsl/doc/structure/StructureVisitor.java new file mode 100644 index 0000000..861ce98 --- /dev/null +++ b/src/main/java/ru/alkoleft/bsl/doc/structure/StructureVisitor.java @@ -0,0 +1,9 @@ +package ru.alkoleft.bsl.doc.structure; + +public interface StructureVisitor { + void visit(SubsystemItem item, int index); + + void visit(ModuleItem item, int index); + + void visit(MDObjectItem item, int index); +} diff --git a/src/main/java/ru/alkoleft/bsl/doc/structure/SubsystemItem.java b/src/main/java/ru/alkoleft/bsl/doc/structure/SubsystemItem.java new file mode 100644 index 0000000..debd118 --- /dev/null +++ b/src/main/java/ru/alkoleft/bsl/doc/structure/SubsystemItem.java @@ -0,0 +1,18 @@ +package ru.alkoleft.bsl.doc.structure; + +import com.github._1c_syntax.bsl.mdo.Subsystem; + +public class SubsystemItem extends Item { + public SubsystemItem(Subsystem subsystem) { + super(subsystem, subsystem.getName()); + } + + @Override + public void accept(StructureVisitor visitor, int index) { + visitor.visit(this, index); + } + + public Subsystem getSubsystem() { + return (Subsystem) getObject(); + } +} diff --git a/src/main/java/ru/alkoleft/bsl/doc/structure/SubsystemsStructureBuilder.java b/src/main/java/ru/alkoleft/bsl/doc/structure/SubsystemsStructureBuilder.java new file mode 100644 index 0000000..687a9b4 --- /dev/null +++ b/src/main/java/ru/alkoleft/bsl/doc/structure/SubsystemsStructureBuilder.java @@ -0,0 +1,16 @@ +package ru.alkoleft.bsl.doc.structure; + +import ru.alkoleft.bsl.doc.bsl.BslContext; + +import java.util.List; +import java.util.stream.Collectors; + +public class SubsystemsStructureBuilder implements StructureBuilder { + + public List build(BslContext context) { + + return context.getRootSubsystems(true) + .map(it -> Factory.createSubSystemItem(it, context)) + .collect(Collectors.toList()); + } +} diff --git a/src/main/resources/confluence-md/module.hbs b/src/main/resources/confluence-md/module.hbs new file mode 100644 index 0000000..24d99cb --- /dev/null +++ b/src/main/resources/confluence-md/module.hbs @@ -0,0 +1,38 @@ +# {{moduleType}} {{#unless isCommonModule}}{{ownerType}}.{{/unless}}{{name}} + +{{~#if description}} + +{{links description}} + +{{~/if}} + +{{~#each methods}} + +###### {{name}} + +`{{{callExample}}}` + +{{#if description}}{{links description}}{{/if}} +{{~#if parameters}} +{{#each parameters as | parameter|}} +* `{{parameter.name}}` +{{#if required}} * Обязательный параметр{{else}} * Значение по умолчанию `{{{parameter.defaultValue.value}}}`{{/if}} +{{~#each parameter.types as | type typeIndex|}} + * Тип {{#with type}}{{#shift 1}}{{#block "value-definition"}}{{/block}}{{/shift}}{{~/with}} + {{~else}} - {{links description}} +{{~/each}} +{{~/each}} + {{~/if}} +{{~#if returnedValue}} + +*Возвращаемое значение* +{{#with returnedValue~}}{{#block "value-definition"}}{{/block}}{{~/with}} +{{~/if}} +{{~#if examples}} +Примеры: + +```bsl +{{examples}} +``` +{{/if}} +{{~/each}} diff --git a/src/main/resources/confluence-md/subsystem.hbs b/src/main/resources/confluence-md/subsystem.hbs new file mode 100644 index 0000000..0f7edc1 --- /dev/null +++ b/src/main/resources/confluence-md/subsystem.hbs @@ -0,0 +1,21 @@ +# {{#great level 2}}Подсистема {{/great}}{{present}} +{{~#if description}} +{{description}} +{{~/if}} + +{{~#if explanation}} +{{explanation}} +{{~/if}} + +{{~#if constants}} + +## Константы +{{#each constants as | item|}} +* {{mdo-present item}} ({{item.name}}) +{{~/each}} +{{~/if}} + +## Содержимое +{{#each childrenPages as | item|}} +* [{{item.title}}]({{page-link item}}) +{{~/each}} diff --git a/src/main/resources/confluence-md/value-definition.hbs b/src/main/resources/confluence-md/value-definition.hbs new file mode 100644 index 0000000..ff80144 --- /dev/null +++ b/src/main/resources/confluence-md/value-definition.hbs @@ -0,0 +1,20 @@ +{{~> inlineType field=this level=1}} +{{~#*inline "inlineType"}} + {{~#if this.hyperlink~}}`см. {{this.hyperlink.link}}`{{~else}}`{{name}}`{{~/if}}{{#if description}} - {{description}}{{~/if}} + {{~#if this.fields~}} + {{~#each this.fields}} +{{indent level}}* {{> inlineField field=this level=(add ../level 1)}} + {{~/each}} + {{/if}} +{{~/inline}} +{{~#*inline "inlineField"~}} +`{{name}}` + {{~#if types~}} + {{~#each types}} + {{~#if @first}} - {{> inlineType type=this level=(add ../level 1)}} + {{~else}} +{{indent level}} - {{> inlineType type=this level=(add ../level 1)}} + {{~/if}} + {{~/each}} + {{~/if}} +{{~/inline}} \ No newline at end of file diff --git a/src/main/resources/docusaurus/module.hbs b/src/main/resources/docusaurus/module.hbs index 9ed483f..a121593 100644 --- a/src/main/resources/docusaurus/module.hbs +++ b/src/main/resources/docusaurus/module.hbs @@ -12,20 +12,23 @@ title: {{name}} {{~/if}} ## Методы модуля - -{{~#each methods}} - +{{#each methods}} --- -### `{{name}}` +### `{{name}}`{{#debug}}Формирование описания метода {{name}}{{/debug}} + +{{#if deprecated~}} +:::caution Устаревший +::: +{{~/if}} {{#if description}}{{links description}}{{/if}} {{~#if parameters}} **Параметры метода** {{#each parameters as | parameter|}} -* `{{parameter.name}}` +* `{{parameter.name}}`{{#debug}}Формирование описания параметра {{parameter.name}}{{/debug}} {{~#each parameter.types as | type typeIndex|}} - * `{{type.name}}` - {{links type.description}} + * {{#with type}}{{#shift 3}}{{#block "value-definition"}}{{/block}}{{/shift}}{{~/with}} {{~else}} - {{links description}} {{~/each}} {{~/each}} @@ -34,7 +37,7 @@ title: {{name}} **Возвращает** -{{#with returnedValue}}{{#block "docusaurus/value-definition"}}{{/block}}{{~/with}} +{{#with returnedValue}}{{#block "value-definition"}}{{/block}}{{~/with}} {{~/if}} {{~#if examples}} @@ -42,10 +45,8 @@ title: {{name}} Примеры: ```bsl -{{~#each examples}} -{{this}} -{{~/each}} +{{examples}} ``` {{/if}} -{{~/each}} +{{/each}} diff --git a/src/main/resources/docusaurus/value-definition.hbs b/src/main/resources/docusaurus/value-definition.hbs index 793e2c4..7774102 100644 --- a/src/main/resources/docusaurus/value-definition.hbs +++ b/src/main/resources/docusaurus/value-definition.hbs @@ -1,17 +1,26 @@ -{{~#if link~}} -{{links link}} -{{~/if}} -{{~#if parameters}}
{{description}} ({{name}}) -{{~else}}`{{name}}` - {{links description}} -{{~/if}} -{{~#if parameters}} -{{/if}} - -{{~#each parameters}} -{{#shift}}* `{{name}}` - {{#each types~}}{{#block "docusaurus/value-definition"}}{{/block}}{{~/each}}{{/shift}} -{{~/each}} - -{{~#if parameters}} +{{~> inlineType field=this level=1}} +{{~#*inline "inlineType"}} + {{~#if this.fields}} +
+ {{/if~}} + {{~#if this.hyperlink~}}`см. {{this.hyperlink.link}}`{{~else}}`{{name}}`{{~/if}}{{#if description}} - {{description}}{{~/if}} + {{~#if this.fields}} + + {{~#each this.fields}} + {{indent level}}* {{> inlineField field=this level=(add ../level 1)}} + {{~/each}}
-{{/if}} \ No newline at end of file + {{/if}} +{{~/inline}} +{{~#*inline "inlineField"~}} + `{{name}}` + {{~#if types~}} + {{~#each types}} + {{~#if @first}} - {{> inlineType type=this level=(add ../level 1)}} + {{~else}} + {{indent level}} - {{> inlineType type=this level=(add ../level 1)}} + {{~/if}} + {{~/each}} + {{~/if}} +{{~/inline}} \ No newline at end of file diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml new file mode 100644 index 0000000..59dc38f --- /dev/null +++ b/src/main/resources/logback.xml @@ -0,0 +1,11 @@ + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + \ No newline at end of file diff --git a/src/main/resources/md/module.hbs b/src/main/resources/md/module.hbs index 7daf3c6..da123ad 100644 --- a/src/main/resources/md/module.hbs +++ b/src/main/resources/md/module.hbs @@ -1,7 +1,5 @@ # {{present}} ({{name}}) -## Методы модуля - {{~#each methods}} ### `{{name}}({{#each parameters}}{{#if @index}}, {{/if}}{{name}}{{/each}})` @@ -9,13 +7,11 @@ {{#if description}}{{description}}{{/if}} {{~#if parameters}} -Параметры метода: - | Имя | Тип | Описание | |-----|-----|---------| {{~#each parameters as | parameter|}} {{~#each parameter.types as | type typeIndex|}} -| {{#unless typeIndex}}`{{parameter.name}}`{{/unless}} | `{{type.name}}` | {{type.description}} | +| {{#unless typeIndex}}`{{parameter.name}}`{{/unless}} | {{#if type.hyperlink}}`см. {{type.hyperlink.link}}`{{else}}`{{type.name}}`{{/if}} | {{#single-line}}{{links type.description}}{{/single-line}} | {{~else}} | {{name}} | | {{description}} | {{~/each}} @@ -23,7 +19,7 @@ {{~/if}} {{~#if returnedValue}} -Возвращает: +Возвращаемое значение {{#if returnedValue.link}} [{{returnedValue.link}}]({{returnedValue.link}}) diff --git a/src/main/resources/md/module.vm b/src/main/resources/md/module.vm deleted file mode 100644 index 3506296..0000000 --- a/src/main/resources/md/module.vm +++ /dev/null @@ -1,18 +0,0 @@ -#set( $H = '#' ) -$H $present ($name) - -$H$H Методы модуля -#foreach( $method in $methods) - -$H$H$H $method.name - -$method.description -#if (!$method.parameters.empty) - -**Параметры метода** -#foreach( $parameter in $method.parameters) - -* $parameter.name - $parameter.description -#end -#end -#end \ No newline at end of file diff --git a/src/main/resources/md/subsystem.hbs b/src/main/resources/md/subsystem.hbs new file mode 100644 index 0000000..ed07816 --- /dev/null +++ b/src/main/resources/md/subsystem.hbs @@ -0,0 +1,11 @@ +# {{present}} +{{~#if description}} +{{description}} +{{~/if}} + +{{explanation}} + +## Содержимое +{{#each childrenPages as | item|}} +* [{{item.title}}]({{page-link item}}) +{{~/each}} diff --git a/src/test/java/ru/alkoleft/bsl/doc/commands/RenderCommandTest.java b/src/test/java/ru/alkoleft/bsl/doc/commands/RenderCommandTest.java new file mode 100644 index 0000000..1761f77 --- /dev/null +++ b/src/test/java/ru/alkoleft/bsl/doc/commands/RenderCommandTest.java @@ -0,0 +1,58 @@ +package ru.alkoleft.bsl.doc.commands; + +import lombok.SneakyThrows; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import ru.alkoleft.bsl.doc.bsl.symbols.RegionSymbol; +import ru.alkoleft.bsl.doc.options.ManualMergeStrategy; +import ru.alkoleft.bsl.doc.options.OutputFormat; +import ru.alkoleft.bsl.doc.options.OutputHierarchy; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.Objects; + +class RenderCommandTest { + + @Test + void run() { + RenderCommand.builder() + .sources(getResource("configuration")) + .destination(Path.of("/tmp/bsl-doc-fixture")) + .format(OutputFormat.ConfluenceMarkdown) + .onlySubsystems(List.of("ППИ")) + .regions(List.of(RegionSymbol.PUBLIC_REGION_RU)) + .manualDocumentation(getResource("docs")) + .manualMergeStrategy(ManualMergeStrategy.MERGE) + .hierarchy(OutputHierarchy.SUBSYSTEM) + .build() + .run(); + } + + @SneakyThrows + @Test + void runFromFile(@TempDir Path tempDir) { + var config = String.format( + "{\"subsystems\": [\"ППИ\"]," + + "\"childLayout\": \"SUB_DIRECTORY\"," + + "\"manualDocs\": \"%s\"," + + "\"mergeStrategy\": \"MERGE\"," + + "\"format\": \"ConfluenceMarkdown\"," + + "\"header\": {\"content\": \"__{{present}}__\\n\"}}", getResource("docs").toString().replace("\\", "\\\\")); + var configPath = tempDir.resolve("config.json"); + Files.writeString(configPath, config); + + RenderCommand.builder() + .sources(getResource("configuration")) + .destination(tempDir.resolve("bsl-doc-fixture")) + .optionsFile(configPath) + .build() + .run(); + } + + @SneakyThrows + private Path getResource(String name) { + return Path.of(Objects.requireNonNull(getClass().getClassLoader().getResource(name)).toURI()); + } +} diff --git a/src/test/resources/configuration/.project b/src/test/resources/configuration/.project new file mode 100644 index 0000000..84e659a --- /dev/null +++ b/src/test/resources/configuration/.project @@ -0,0 +1,18 @@ + + + BslDoc + + + + + + org.eclipse.xtext.ui.shared.xtextBuilder + + + + + + org.eclipse.xtext.ui.shared.xtextNature + com._1c.g5.v8.dt.core.V8ConfigurationNature + + diff --git a/src/test/resources/configuration/.settings/com.e1c.v8codestyle.autosort.prefs b/src/test/resources/configuration/.settings/com.e1c.v8codestyle.autosort.prefs new file mode 100644 index 0000000..23b0625 --- /dev/null +++ b/src/test/resources/configuration/.settings/com.e1c.v8codestyle.autosort.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +topObjects=true diff --git a/src/test/resources/configuration/.settings/com.e1c.v8codestyle.bsl.prefs b/src/test/resources/configuration/.settings/com.e1c.v8codestyle.bsl.prefs new file mode 100644 index 0000000..ac5ba52 --- /dev/null +++ b/src/test/resources/configuration/.settings/com.e1c.v8codestyle.bsl.prefs @@ -0,0 +1,3 @@ +addModuleStrictTypesAnnotation=false +createModuleStructure=false +eclipse.preferences.version=1 diff --git a/src/test/resources/configuration/.settings/com.e1c.v8codestyle.prefs b/src/test/resources/configuration/.settings/com.e1c.v8codestyle.prefs new file mode 100644 index 0000000..9e9b57e --- /dev/null +++ b/src/test/resources/configuration/.settings/com.e1c.v8codestyle.prefs @@ -0,0 +1,3 @@ +commonChecks=true +eclipse.preferences.version=1 +standardChecks=true diff --git a/src/test/resources/configuration/.settings/org.eclipse.core.resources.prefs b/src/test/resources/configuration/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 0000000..99f26c0 --- /dev/null +++ b/src/test/resources/configuration/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +encoding/=UTF-8 diff --git a/src/test/resources/configuration/DT-INF/PROJECT.PMF b/src/test/resources/configuration/DT-INF/PROJECT.PMF new file mode 100644 index 0000000..e2a2031 --- /dev/null +++ b/src/test/resources/configuration/DT-INF/PROJECT.PMF @@ -0,0 +1,2 @@ +Manifest-Version: 1.0 +Runtime-Version: 8.3.21 diff --git "a/src/test/resources/configuration/src/Catalogs/\320\241\320\277\321\200\320\260\320\262\320\276\321\207\320\275\320\270\320\2721/ManagerModule.bsl" "b/src/test/resources/configuration/src/Catalogs/\320\241\320\277\321\200\320\260\320\262\320\276\321\207\320\275\320\270\320\2721/ManagerModule.bsl" new file mode 100644 index 0000000..4765a70 --- /dev/null +++ "b/src/test/resources/configuration/src/Catalogs/\320\241\320\277\321\200\320\260\320\262\320\276\321\207\320\275\320\270\320\2721/ManagerModule.bsl" @@ -0,0 +1,13 @@ +#Область ПрограммныйИнтерфейс + +// Процедура1. +// Устаревший. +// +// Параметры: +// П1 П1 +// П2 П2 +Процедура Процедура1(П1, П2) Экспорт + +КонецПроцедуры + +#КонецОбласти \ No newline at end of file diff --git "a/src/test/resources/configuration/src/Catalogs/\320\241\320\277\321\200\320\260\320\262\320\276\321\207\320\275\320\270\320\2721/\320\241\320\277\321\200\320\260\320\262\320\276\321\207\320\275\320\270\320\2721.mdo" "b/src/test/resources/configuration/src/Catalogs/\320\241\320\277\321\200\320\260\320\262\320\276\321\207\320\275\320\270\320\2721/\320\241\320\277\321\200\320\260\320\262\320\276\321\207\320\275\320\270\320\2721.mdo" new file mode 100644 index 0000000..8ad2ff6 --- /dev/null +++ "b/src/test/resources/configuration/src/Catalogs/\320\241\320\277\321\200\320\260\320\262\320\276\321\207\320\275\320\270\320\2721/\320\241\320\277\321\200\320\260\320\262\320\276\321\207\320\275\320\270\320\2721.mdo" @@ -0,0 +1,33 @@ + + + + + + + + + + Справочник1 + + ru + Справочник1 + + true + Catalog.Справочник1.StandardAttribute.Code + Catalog.Справочник1.StandardAttribute.Description + DontUse + Use + Managed + Use + 2 + true + 9 + 25 + String + Variable + true + true + AsDescription + InDialog + BothWays + diff --git "a/src/test/resources/configuration/src/Catalogs/\320\241\320\277\321\200\320\260\320\262\320\276\321\207\320\275\320\270\320\2722/ObjectModule.bsl" "b/src/test/resources/configuration/src/Catalogs/\320\241\320\277\321\200\320\260\320\262\320\276\321\207\320\275\320\270\320\2722/ObjectModule.bsl" new file mode 100644 index 0000000..4765a70 --- /dev/null +++ "b/src/test/resources/configuration/src/Catalogs/\320\241\320\277\321\200\320\260\320\262\320\276\321\207\320\275\320\270\320\2722/ObjectModule.bsl" @@ -0,0 +1,13 @@ +#Область ПрограммныйИнтерфейс + +// Процедура1. +// Устаревший. +// +// Параметры: +// П1 П1 +// П2 П2 +Процедура Процедура1(П1, П2) Экспорт + +КонецПроцедуры + +#КонецОбласти \ No newline at end of file diff --git "a/src/test/resources/configuration/src/Catalogs/\320\241\320\277\321\200\320\260\320\262\320\276\321\207\320\275\320\270\320\2722/\320\241\320\277\321\200\320\260\320\262\320\276\321\207\320\275\320\270\320\2722.mdo" "b/src/test/resources/configuration/src/Catalogs/\320\241\320\277\321\200\320\260\320\262\320\276\321\207\320\275\320\270\320\2722/\320\241\320\277\321\200\320\260\320\262\320\276\321\207\320\275\320\270\320\2722.mdo" new file mode 100644 index 0000000..c6caceb --- /dev/null +++ "b/src/test/resources/configuration/src/Catalogs/\320\241\320\277\321\200\320\260\320\262\320\276\321\207\320\275\320\270\320\2722/\320\241\320\277\321\200\320\260\320\262\320\276\321\207\320\275\320\270\320\2722.mdo" @@ -0,0 +1,33 @@ + + + + + + + + + + Справочник2 + + ru + Справочник2 + + true + Catalog.Справочник2.StandardAttribute.Code + Catalog.Справочник2.StandardAttribute.Description + DontUse + Use + Managed + Use + 2 + true + 9 + 25 + String + Variable + true + true + AsDescription + InDialog + BothWays + diff --git "a/src/test/resources/configuration/src/Catalogs/\320\241\320\277\321\200\320\260\320\262\320\276\321\207\320\275\320\270\320\272\320\230\320\234\320\276\320\264\321\203\320\273\321\214/ManagerModule.bsl" "b/src/test/resources/configuration/src/Catalogs/\320\241\320\277\321\200\320\260\320\262\320\276\321\207\320\275\320\270\320\272\320\230\320\234\320\276\320\264\321\203\320\273\321\214/ManagerModule.bsl" new file mode 100644 index 0000000..e127192 --- /dev/null +++ "b/src/test/resources/configuration/src/Catalogs/\320\241\320\277\321\200\320\260\320\262\320\276\321\207\320\275\320\270\320\272\320\230\320\234\320\276\320\264\321\203\320\273\321\214/ManagerModule.bsl" @@ -0,0 +1,13 @@ +#Область ПрограммныйИнтерфейс + +// Процедура1. +// Устаревший. +// +// Параметры: +// П1 П1 +// П2 П2 +Процедура СправочникИМодуль(П1, П2) Экспорт + +КонецПроцедуры + +#КонецОбласти \ No newline at end of file diff --git "a/src/test/resources/configuration/src/Catalogs/\320\241\320\277\321\200\320\260\320\262\320\276\321\207\320\275\320\270\320\272\320\230\320\234\320\276\320\264\321\203\320\273\321\214/\320\241\320\277\321\200\320\260\320\262\320\276\321\207\320\275\320\270\320\272\320\230\320\234\320\276\320\264\321\203\320\273\321\214.mdo" "b/src/test/resources/configuration/src/Catalogs/\320\241\320\277\321\200\320\260\320\262\320\276\321\207\320\275\320\270\320\272\320\230\320\234\320\276\320\264\321\203\320\273\321\214/\320\241\320\277\321\200\320\260\320\262\320\276\321\207\320\275\320\270\320\272\320\230\320\234\320\276\320\264\321\203\320\273\321\214.mdo" new file mode 100644 index 0000000..352ac3c --- /dev/null +++ "b/src/test/resources/configuration/src/Catalogs/\320\241\320\277\321\200\320\260\320\262\320\276\321\207\320\275\320\270\320\272\320\230\320\234\320\276\320\264\321\203\320\273\321\214/\320\241\320\277\321\200\320\260\320\262\320\276\321\207\320\275\320\270\320\272\320\230\320\234\320\276\320\264\321\203\320\273\321\214.mdo" @@ -0,0 +1,33 @@ + + + + + + + + + + СправочникИМодуль + + ru + СправочникИМодуль + + true + Catalog.СправочникИМодуль.StandardAttribute.Code + Catalog.СправочникИМодуль.StandardAttribute.Description + DontUse + Use + Managed + Use + 2 + true + 9 + 25 + String + Variable + true + true + AsDescription + InDialog + BothWays + diff --git "a/src/test/resources/configuration/src/CommonModules/\320\237\321\200\320\265\320\276\320\261\321\200\320\260\320\267\320\276\320\262\320\260\320\275\320\270\321\217/Module.bsl" "b/src/test/resources/configuration/src/CommonModules/\320\237\321\200\320\265\320\276\320\261\321\200\320\260\320\267\320\276\320\262\320\260\320\275\320\270\321\217/Module.bsl" new file mode 100644 index 0000000..130f397 --- /dev/null +++ "b/src/test/resources/configuration/src/CommonModules/\320\237\321\200\320\265\320\276\320\261\321\200\320\260\320\267\320\276\320\262\320\260\320\275\320\270\321\217/Module.bsl" @@ -0,0 +1,35 @@ +#Область ПрограммныйИнтерфейс + +// ЗначениеВСтроку +// Преобразует переданное значение в строку +// +// Параметры: +// Значение - Произвольный - значение для преобразования в строковое представление +// ФорматнаяСтрока - Строка - формат получаемой строки для примитивных типов +// Кешировать - Булево - признак необходимости кэширования результата +// +// Возвращаемое значение: +// Строка - результат преобразования значения +// +Функция ЗначениеВСтроку(Значение, ФорматнаяСтрока = "", Кешировать = Ложь) Экспорт + +КонецФункции + +// ЧислоИзОднойСистемыВДругую +// Возвращает представление числа, преобразованного из одной системы счисления в другую. +// +// Параметры: +// ПредставлениеЧисла - Строка - представление числа. +// - Число - Преобразуемое число +// ОснованиеЧисла - Число - основание системы счисления переданного представления числа (1-32). +// ОснованиеРезультата - Число - основание системы счисления результата (1-32). +// ДлинаРезультата - Число - количество символов в возвращаемом представлении, если результат меньшей длины - будет дополнен нулями слева. +// ТекстОшибки - Строка - в этот параметр будет помещен текст возникшей ошибки. +// +// Возвращаемое значение: +// Строка - представление числа в требуемой системе счисления, "0" при ошибке. +Функция ЧислоИзОднойСистемыВДругую(ПредставлениеЧисла, ОснованиеЧисла, ОснованиеРезультата, ДлинаРезультата = 1, ТекстОшибки = "") Экспорт + +КонецФункции + +#КонецОбласти \ No newline at end of file diff --git "a/src/test/resources/configuration/src/CommonModules/\320\237\321\200\320\265\320\276\320\261\321\200\320\260\320\267\320\276\320\262\320\260\320\275\320\270\321\217/\320\237\321\200\320\265\320\276\320\261\321\200\320\260\320\267\320\276\320\262\320\260\320\275\320\270\321\217.mdo" "b/src/test/resources/configuration/src/CommonModules/\320\237\321\200\320\265\320\276\320\261\321\200\320\260\320\267\320\276\320\262\320\260\320\275\320\270\321\217/\320\237\321\200\320\265\320\276\320\261\321\200\320\260\320\267\320\276\320\262\320\260\320\275\320\270\321\217.mdo" new file mode 100644 index 0000000..0dc6fb9 --- /dev/null +++ "b/src/test/resources/configuration/src/CommonModules/\320\237\321\200\320\265\320\276\320\261\321\200\320\260\320\267\320\276\320\262\320\260\320\275\320\270\321\217/\320\237\321\200\320\265\320\276\320\261\321\200\320\260\320\267\320\276\320\262\320\260\320\275\320\270\321\217.mdo" @@ -0,0 +1,9 @@ + + + Преобразования + + ru + Преобразования + + true + diff --git "a/src/test/resources/configuration/src/CommonModules/\320\241\320\277\321\200\320\260\320\262\320\276\321\207\320\275\320\270\320\272\320\230\320\234\320\276\320\264\321\203\320\273\321\214/Module.bsl" "b/src/test/resources/configuration/src/CommonModules/\320\241\320\277\321\200\320\260\320\262\320\276\321\207\320\275\320\270\320\272\320\230\320\234\320\276\320\264\321\203\320\273\321\214/Module.bsl" new file mode 100644 index 0000000..9b37653 --- /dev/null +++ "b/src/test/resources/configuration/src/CommonModules/\320\241\320\277\321\200\320\260\320\262\320\276\321\207\320\275\320\270\320\272\320\230\320\234\320\276\320\264\321\203\320\273\321\214/Module.bsl" @@ -0,0 +1,33 @@ +#Область ПрограммныйИнтерфейс + +// Преобразует переданное значение в строку +// +// Параметры: +// Значение - Произвольный - значение для преобразования в строковое представление +// ФорматнаяСтрока - Строка - формат получаемой строки для примитивных типов +// Кешировать - Булево - признак необходимости кэширования результата +// +// Возвращаемое значение: +// Строка - результат преобразования значения +// +Функция СправочникИМодуль1(Значение, ФорматнаяСтрока = "", Кешировать = Ложь) Экспорт + +КонецФункции + +// Возвращает представление числа, преобразованного из одной системы счисления в другую. +// +// Параметры: +// ПредставлениеЧисла - Строка - представление числа. +// - Число - Преобразуемое число +// ОснованиеЧисла - Число - основание системы счисления переданного представления числа (1-32). +// ОснованиеРезультата - Число - основание системы счисления результата (1-32). +// ДлинаРезультата - Число - количество символов в возвращаемом представлении, если результат меньшей длины - будет дополнен нулями слева. +// ТекстОшибки - Строка - в этот параметр будет помещен текст возникшей ошибки. +// +// Возвращаемое значение: +// Строка - представление числа в требуемой системе счисления, "0" при ошибке. +Функция СправочникИМодуль2(ПредставлениеЧисла, ОснованиеЧисла, ОснованиеРезультата, ДлинаРезультата = 1, ТекстОшибки = "") Экспорт + +КонецФункции + +#КонецОбласти \ No newline at end of file diff --git "a/src/test/resources/configuration/src/CommonModules/\320\241\320\277\321\200\320\260\320\262\320\276\321\207\320\275\320\270\320\272\320\230\320\234\320\276\320\264\321\203\320\273\321\214/\320\241\320\277\321\200\320\260\320\262\320\276\321\207\320\275\320\270\320\272\320\230\320\234\320\276\320\264\321\203\320\273\321\214.mdo" "b/src/test/resources/configuration/src/CommonModules/\320\241\320\277\321\200\320\260\320\262\320\276\321\207\320\275\320\270\320\272\320\230\320\234\320\276\320\264\321\203\320\273\321\214/\320\241\320\277\321\200\320\260\320\262\320\276\321\207\320\275\320\270\320\272\320\230\320\234\320\276\320\264\321\203\320\273\321\214.mdo" new file mode 100644 index 0000000..547d6b0 --- /dev/null +++ "b/src/test/resources/configuration/src/CommonModules/\320\241\320\277\321\200\320\260\320\262\320\276\321\207\320\275\320\270\320\272\320\230\320\234\320\276\320\264\321\203\320\273\321\214/\320\241\320\277\321\200\320\260\320\262\320\276\321\207\320\275\320\270\320\272\320\230\320\234\320\276\320\264\321\203\320\273\321\214.mdo" @@ -0,0 +1,9 @@ + + + СправочникИМодуль + + ru + СправочникИМодуль + + true + diff --git "a/src/test/resources/configuration/src/CommonModules/\320\241\321\202\321\200\320\276\320\272\320\276\320\262\321\213\320\265\320\244\321\203\320\275\320\272\321\206\320\270\320\270/Module.bsl" "b/src/test/resources/configuration/src/CommonModules/\320\241\321\202\321\200\320\276\320\272\320\276\320\262\321\213\320\265\320\244\321\203\320\275\320\272\321\206\320\270\320\270/Module.bsl" new file mode 100644 index 0000000..c6e16a7 --- /dev/null +++ "b/src/test/resources/configuration/src/CommonModules/\320\241\321\202\321\200\320\276\320\272\320\276\320\262\321\213\320\265\320\244\321\203\320\275\320\272\321\206\320\270\320\270/Module.bsl" @@ -0,0 +1,72 @@ +#Область ПрограммныйИнтерфейс + +// Устаревшая +// Возвращает строку, полученную из массива элементов, списка значений или таблицы значений, разделенных символом разделителя +// +// Параметры: +// Коллекция - ФиксированныйМассив, Массив, СписокЗначений, ТаблицаЗначений - коллекция элементов из +// которых необходимо получить строку +// Разделитель - Строка - любой набор символов, +// который будет использован как разделитель между элементами в строке +// Колонки - Строка - Имена колонок таблицы +// значений, разделенные ";" или "*" если необходимо вывести все колонки +// ИгнорироватьОшибки - Булево - Если установлен в Истина, +// то неверные имена колонок игнорируются, в противном случае будет выдана ошибка, содержащая неверные колонки +// +// Возвращаемое значение: +// Строка - строка, полученная из коллекции элементов, разделенных символом разделителя +// +Функция ПолучитьСтрокуИзКоллекцииПодстрок(Коллекция, Разделитель = ",", Колонки = Неопределено, ИгнорироватьОшибки = Ложь) Экспорт + +КонецФункции + +// Возвращает строку, полученную из массива элементов, списка значений или таблицы значений, разделенных символом разделителя +// +// Параметры: +// Коллекция - ФиксированныйМассив, Массив, СписокЗначений, ТаблицаЗначений - коллекция элементов из +// которых необходимо получить строку +// Разделитель - Строка - любой набор символов, +// который будет использован как разделитель между элементами в строке +// Колонки - Строка - Имена колонок таблицы +// значений, разделенные ";" или "*" если необходимо вывести все колонки +// ИгнорироватьОшибки - Булево - Если установлен в Истина, +// то неверные имена колонок игнорируются, в противном случае будет выдана ошибка, содержащая неверные колонки +// +// Возвращаемое значение: +// Строка - строка, полученная из коллекции элементов, разделенных символом разделителя +// +Функция ПолучитьСтрокуИзКоллекцииПодстрок2(Коллекция, Разделитель = ",", Колонки = Неопределено, ИгнорироватьОшибки = Ложь) Экспорт + +КонецФункции + +// раскладывает строку с разделителями в указанную коллекцию +// +// Параметры: +// ВходнаяСтрока - Строка - строка с разделителями +// ВидКоллекции - Строка - имя коллекции +// доступные варианты "Массив", "ФиксированныйМассив", "СписокЗначений" +// Разделитель - Строка - разделитель входной строки +// УдалятьКонцевыеПробелы - Булево - удаление концевых пробелов разложенных строк +// +// Возвращаемое значение: +// Массив, СписокЗначений, ФиксированныйМассив - "заказанная" коллекция +// +Функция РазложитьСтрокуВКоллекцию(Знач ВходнаяСтрока, ВидКоллекции = "Массив", Разделитель = ",", УдалятьКонцевыеПробелы = Ложь) Экспорт + +КонецФункции // РазложитьСтрокуВКоллекцию + +// Производит поиск в исходной строке вхождений искомой строки +// +// Параметры: +// ИсходнаяСтрока - Строка - Анализируемая строка +// СтрокаПоиска - Строка - Искомая строка +// +// Возвращаемое значение: +// Массив Из Структура - Результат метода: +// * НомерСимвола - Число - начало строки вхождения +// * ДлинаСтроки - Число - длина строки вхождения +// +Функция ПолучитьПараметрыВхожденияСтроки(ИсходнаяСтрока, СтрокаПоиска) Экспорт +КонецФункции + +#КонецОбласти diff --git "a/src/test/resources/configuration/src/CommonModules/\320\241\321\202\321\200\320\276\320\272\320\276\320\262\321\213\320\265\320\244\321\203\320\275\320\272\321\206\320\270\320\270/\320\241\321\202\321\200\320\276\320\272\320\276\320\262\321\213\320\265\320\244\321\203\320\275\320\272\321\206\320\270\320\270.mdo" "b/src/test/resources/configuration/src/CommonModules/\320\241\321\202\321\200\320\276\320\272\320\276\320\262\321\213\320\265\320\244\321\203\320\275\320\272\321\206\320\270\320\270/\320\241\321\202\321\200\320\276\320\272\320\276\320\262\321\213\320\265\320\244\321\203\320\275\320\272\321\206\320\270\320\270.mdo" new file mode 100644 index 0000000..4f360c6 --- /dev/null +++ "b/src/test/resources/configuration/src/CommonModules/\320\241\321\202\321\200\320\276\320\272\320\276\320\262\321\213\320\265\320\244\321\203\320\275\320\272\321\206\320\270\320\270/\320\241\321\202\321\200\320\276\320\272\320\276\320\262\321\213\320\265\320\244\321\203\320\275\320\272\321\206\320\270\320\270.mdo" @@ -0,0 +1,9 @@ + + + СтроковыеФункции + + ru + Строковые функции + + true + diff --git a/src/test/resources/configuration/src/Configuration/CommandInterface.cmi b/src/test/resources/configuration/src/Configuration/CommandInterface.cmi new file mode 100644 index 0000000..0cf6de8 --- /dev/null +++ b/src/test/resources/configuration/src/Configuration/CommandInterface.cmi @@ -0,0 +1,2 @@ + + diff --git a/src/test/resources/configuration/src/Configuration/Configuration.mdo b/src/test/resources/configuration/src/Configuration/Configuration.mdo new file mode 100644 index 0000000..1393c89 --- /dev/null +++ b/src/test/resources/configuration/src/Configuration/Configuration.mdo @@ -0,0 +1,52 @@ + + + BslDoc + + ru + Bsl doc + + + + + + + + + 8.3.21 + ManagedApplication + PersonalComputer + Russian + + + true + + + OSBackup + true + + + Language.Русский + Managed + NotAutoFree + DontUse + DontUse + 8.3.21 + + Русский + + ru + Русский + + ru + + Subsystem.ППИ + CommonModule.Преобразования + CommonModule.СтроковыеФункции + CommonModule.СправочникИМодуль + Constant.Константа + Constant.Константа1 + Constant.Константа2 + Catalog.Справочник1 + Catalog.Справочник2 + Catalog.СправочникИМодуль + diff --git a/src/test/resources/configuration/src/Configuration/MainSectionCommandInterface.cmi b/src/test/resources/configuration/src/Configuration/MainSectionCommandInterface.cmi new file mode 100644 index 0000000..0cf6de8 --- /dev/null +++ b/src/test/resources/configuration/src/Configuration/MainSectionCommandInterface.cmi @@ -0,0 +1,2 @@ + + diff --git "a/src/test/resources/configuration/src/Constants/\320\232\320\276\320\275\321\201\321\202\320\260\320\275\321\202\320\260/\320\232\320\276\320\275\321\201\321\202\320\260\320\275\321\202\320\260.mdo" "b/src/test/resources/configuration/src/Constants/\320\232\320\276\320\275\321\201\321\202\320\260\320\275\321\202\320\260/\320\232\320\276\320\275\321\201\321\202\320\260\320\275\321\202\320\260.mdo" new file mode 100644 index 0000000..c5cfc4a --- /dev/null +++ "b/src/test/resources/configuration/src/Constants/\320\232\320\276\320\275\321\201\321\202\320\260\320\275\321\202\320\260/\320\232\320\276\320\275\321\201\321\202\320\260\320\275\321\202\320\260.mdo" @@ -0,0 +1,23 @@ + + + + + + + + Константа + + ru + Константа + + + String + + 10 + + + true + + + Managed + diff --git "a/src/test/resources/configuration/src/Constants/\320\232\320\276\320\275\321\201\321\202\320\260\320\275\321\202\320\2601/\320\232\320\276\320\275\321\201\321\202\320\260\320\275\321\202\320\2601.mdo" "b/src/test/resources/configuration/src/Constants/\320\232\320\276\320\275\321\201\321\202\320\260\320\275\321\202\320\2601/\320\232\320\276\320\275\321\201\321\202\320\260\320\275\321\202\320\2601.mdo" new file mode 100644 index 0000000..34a3141 --- /dev/null +++ "b/src/test/resources/configuration/src/Constants/\320\232\320\276\320\275\321\201\321\202\320\260\320\275\321\202\320\2601/\320\232\320\276\320\275\321\201\321\202\320\260\320\275\321\202\320\2601.mdo" @@ -0,0 +1,23 @@ + + + + + + + + Константа1 + + ru + Константа 1 + + + String + + 10 + + + true + + + Managed + diff --git "a/src/test/resources/configuration/src/Constants/\320\232\320\276\320\275\321\201\321\202\320\260\320\275\321\202\320\2602/\320\232\320\276\320\275\321\201\321\202\320\260\320\275\321\202\320\2602.mdo" "b/src/test/resources/configuration/src/Constants/\320\232\320\276\320\275\321\201\321\202\320\260\320\275\321\202\320\2602/\320\232\320\276\320\275\321\201\321\202\320\260\320\275\321\202\320\2602.mdo" new file mode 100644 index 0000000..53b83bd --- /dev/null +++ "b/src/test/resources/configuration/src/Constants/\320\232\320\276\320\275\321\201\321\202\320\260\320\275\321\202\320\2602/\320\232\320\276\320\275\321\201\321\202\320\260\320\275\321\202\320\2602.mdo" @@ -0,0 +1,23 @@ + + + + + + + + Константа2 + + ru + Константа 2 + + + String + + 10 + + + true + + + Managed + diff --git "a/src/test/resources/configuration/src/Subsystems/\320\237\320\237\320\230/CommandInterface.cmi" "b/src/test/resources/configuration/src/Subsystems/\320\237\320\237\320\230/CommandInterface.cmi" new file mode 100644 index 0000000..0cf6de8 --- /dev/null +++ "b/src/test/resources/configuration/src/Subsystems/\320\237\320\237\320\230/CommandInterface.cmi" @@ -0,0 +1,2 @@ + + diff --git "a/src/test/resources/configuration/src/Subsystems/\320\237\320\237\320\230/Subsystems/\320\236\320\264\320\270\320\275\320\260\320\272\320\276\320\262\321\213\320\265\320\230\320\274\320\265\320\275\320\260/CommandInterface.cmi" "b/src/test/resources/configuration/src/Subsystems/\320\237\320\237\320\230/Subsystems/\320\236\320\264\320\270\320\275\320\260\320\272\320\276\320\262\321\213\320\265\320\230\320\274\320\265\320\275\320\260/CommandInterface.cmi" new file mode 100644 index 0000000..0cf6de8 --- /dev/null +++ "b/src/test/resources/configuration/src/Subsystems/\320\237\320\237\320\230/Subsystems/\320\236\320\264\320\270\320\275\320\260\320\272\320\276\320\262\321\213\320\265\320\230\320\274\320\265\320\275\320\260/CommandInterface.cmi" @@ -0,0 +1,2 @@ + + diff --git "a/src/test/resources/configuration/src/Subsystems/\320\237\320\237\320\230/Subsystems/\320\236\320\264\320\270\320\275\320\260\320\272\320\276\320\262\321\213\320\265\320\230\320\274\320\265\320\275\320\260/\320\236\320\264\320\270\320\275\320\260\320\272\320\276\320\262\321\213\320\265\320\230\320\274\320\265\320\275\320\260.mdo" "b/src/test/resources/configuration/src/Subsystems/\320\237\320\237\320\230/Subsystems/\320\236\320\264\320\270\320\275\320\260\320\272\320\276\320\262\321\213\320\265\320\230\320\274\320\265\320\275\320\260/\320\236\320\264\320\270\320\275\320\260\320\272\320\276\320\262\321\213\320\265\320\230\320\274\320\265\320\275\320\260.mdo" new file mode 100644 index 0000000..0731dc6 --- /dev/null +++ "b/src/test/resources/configuration/src/Subsystems/\320\237\320\237\320\230/Subsystems/\320\236\320\264\320\270\320\275\320\260\320\272\320\276\320\262\321\213\320\265\320\230\320\274\320\265\320\275\320\260/\320\236\320\264\320\270\320\275\320\260\320\272\320\276\320\262\321\213\320\265\320\230\320\274\320\265\320\275\320\260.mdo" @@ -0,0 +1,13 @@ + + + ОдинаковыеИмена + + ru + Тест одинаковых имен метаданных + + true + true + CommonModule.СправочникИМодуль + Catalog.СправочникИМодуль + Subsystem.ППИ + diff --git "a/src/test/resources/configuration/src/Subsystems/\320\237\320\237\320\230/Subsystems/\320\237\321\200\320\270\320\272\320\273\320\260\320\264\320\275\321\213\320\265/CommandInterface.cmi" "b/src/test/resources/configuration/src/Subsystems/\320\237\320\237\320\230/Subsystems/\320\237\321\200\320\270\320\272\320\273\320\260\320\264\320\275\321\213\320\265/CommandInterface.cmi" new file mode 100644 index 0000000..0cf6de8 --- /dev/null +++ "b/src/test/resources/configuration/src/Subsystems/\320\237\320\237\320\230/Subsystems/\320\237\321\200\320\270\320\272\320\273\320\260\320\264\320\275\321\213\320\265/CommandInterface.cmi" @@ -0,0 +1,2 @@ + + diff --git "a/src/test/resources/configuration/src/Subsystems/\320\237\320\237\320\230/Subsystems/\320\237\321\200\320\270\320\272\320\273\320\260\320\264\320\275\321\213\320\265/Subsystems/\320\224\320\276\320\272\321\203\320\274\320\265\320\275\321\202\321\213/CommandInterface.cmi" "b/src/test/resources/configuration/src/Subsystems/\320\237\320\237\320\230/Subsystems/\320\237\321\200\320\270\320\272\320\273\320\260\320\264\320\275\321\213\320\265/Subsystems/\320\224\320\276\320\272\321\203\320\274\320\265\320\275\321\202\321\213/CommandInterface.cmi" new file mode 100644 index 0000000..0cf6de8 --- /dev/null +++ "b/src/test/resources/configuration/src/Subsystems/\320\237\320\237\320\230/Subsystems/\320\237\321\200\320\270\320\272\320\273\320\260\320\264\320\275\321\213\320\265/Subsystems/\320\224\320\276\320\272\321\203\320\274\320\265\320\275\321\202\321\213/CommandInterface.cmi" @@ -0,0 +1,2 @@ + + diff --git "a/src/test/resources/configuration/src/Subsystems/\320\237\320\237\320\230/Subsystems/\320\237\321\200\320\270\320\272\320\273\320\260\320\264\320\275\321\213\320\265/Subsystems/\320\224\320\276\320\272\321\203\320\274\320\265\320\275\321\202\321\213/\320\224\320\276\320\272\321\203\320\274\320\265\320\275\321\202\321\213.mdo" "b/src/test/resources/configuration/src/Subsystems/\320\237\320\237\320\230/Subsystems/\320\237\321\200\320\270\320\272\320\273\320\260\320\264\320\275\321\213\320\265/Subsystems/\320\224\320\276\320\272\321\203\320\274\320\265\320\275\321\202\321\213/\320\224\320\276\320\272\321\203\320\274\320\265\320\275\321\202\321\213.mdo" new file mode 100644 index 0000000..ff1e502 --- /dev/null +++ "b/src/test/resources/configuration/src/Subsystems/\320\237\320\237\320\230/Subsystems/\320\237\321\200\320\270\320\272\320\273\320\260\320\264\320\275\321\213\320\265/Subsystems/\320\224\320\276\320\272\321\203\320\274\320\265\320\275\321\202\321\213/\320\224\320\276\320\272\321\203\320\274\320\265\320\275\321\202\321\213.mdo" @@ -0,0 +1,11 @@ + + + Документы + + ru + Документы + + true + true + Subsystem.ППИ.Subsystem.Прикладные + diff --git "a/src/test/resources/configuration/src/Subsystems/\320\237\320\237\320\230/Subsystems/\320\237\321\200\320\270\320\272\320\273\320\260\320\264\320\275\321\213\320\265/Subsystems/\320\241\320\277\321\200\320\260\320\262\320\276\321\207\320\275\320\270\320\272\320\270/CommandInterface.cmi" "b/src/test/resources/configuration/src/Subsystems/\320\237\320\237\320\230/Subsystems/\320\237\321\200\320\270\320\272\320\273\320\260\320\264\320\275\321\213\320\265/Subsystems/\320\241\320\277\321\200\320\260\320\262\320\276\321\207\320\275\320\270\320\272\320\270/CommandInterface.cmi" new file mode 100644 index 0000000..0cf6de8 --- /dev/null +++ "b/src/test/resources/configuration/src/Subsystems/\320\237\320\237\320\230/Subsystems/\320\237\321\200\320\270\320\272\320\273\320\260\320\264\320\275\321\213\320\265/Subsystems/\320\241\320\277\321\200\320\260\320\262\320\276\321\207\320\275\320\270\320\272\320\270/CommandInterface.cmi" @@ -0,0 +1,2 @@ + + diff --git "a/src/test/resources/configuration/src/Subsystems/\320\237\320\237\320\230/Subsystems/\320\237\321\200\320\270\320\272\320\273\320\260\320\264\320\275\321\213\320\265/Subsystems/\320\241\320\277\321\200\320\260\320\262\320\276\321\207\320\275\320\270\320\272\320\270/\320\241\320\277\321\200\320\260\320\262\320\276\321\207\320\275\320\270\320\272\320\270.mdo" "b/src/test/resources/configuration/src/Subsystems/\320\237\320\237\320\230/Subsystems/\320\237\321\200\320\270\320\272\320\273\320\260\320\264\320\275\321\213\320\265/Subsystems/\320\241\320\277\321\200\320\260\320\262\320\276\321\207\320\275\320\270\320\272\320\270/\320\241\320\277\321\200\320\260\320\262\320\276\321\207\320\275\320\270\320\272\320\270.mdo" new file mode 100644 index 0000000..860a5b2 --- /dev/null +++ "b/src/test/resources/configuration/src/Subsystems/\320\237\320\237\320\230/Subsystems/\320\237\321\200\320\270\320\272\320\273\320\260\320\264\320\275\321\213\320\265/Subsystems/\320\241\320\277\321\200\320\260\320\262\320\276\321\207\320\275\320\270\320\272\320\270/\320\241\320\277\321\200\320\260\320\262\320\276\321\207\320\275\320\270\320\272\320\270.mdo" @@ -0,0 +1,14 @@ + + + Справочники + + ru + Справочники + + true + true + Catalog.Справочник1 + Catalog.Справочник2 + Constant.Константа + Subsystem.ППИ.Subsystem.Прикладные + diff --git "a/src/test/resources/configuration/src/Subsystems/\320\237\320\237\320\230/Subsystems/\320\237\321\200\320\270\320\272\320\273\320\260\320\264\320\275\321\213\320\265/\320\237\321\200\320\270\320\272\320\273\320\260\320\264\320\275\321\213\320\265.mdo" "b/src/test/resources/configuration/src/Subsystems/\320\237\320\237\320\230/Subsystems/\320\237\321\200\320\270\320\272\320\273\320\260\320\264\320\275\321\213\320\265/\320\237\321\200\320\270\320\272\320\273\320\260\320\264\320\275\321\213\320\265.mdo" new file mode 100644 index 0000000..f183539 --- /dev/null +++ "b/src/test/resources/configuration/src/Subsystems/\320\237\320\237\320\230/Subsystems/\320\237\321\200\320\270\320\272\320\273\320\260\320\264\320\275\321\213\320\265/\320\237\321\200\320\270\320\272\320\273\320\260\320\264\320\275\321\213\320\265.mdo" @@ -0,0 +1,19 @@ + + + Прикладные + + ru + Прикладные объекты + + + ru + Подробное описание подсистемы + + true + true + Constant.Константа1 + Constant.Константа2 + Документы + Справочники + Subsystem.ППИ + diff --git "a/src/test/resources/configuration/src/Subsystems/\320\237\320\237\320\230/Subsystems/\320\241\320\265\321\200\320\262\320\270\321\201\320\275\321\213\320\265/CommandInterface.cmi" "b/src/test/resources/configuration/src/Subsystems/\320\237\320\237\320\230/Subsystems/\320\241\320\265\321\200\320\262\320\270\321\201\320\275\321\213\320\265/CommandInterface.cmi" new file mode 100644 index 0000000..0cf6de8 --- /dev/null +++ "b/src/test/resources/configuration/src/Subsystems/\320\237\320\237\320\230/Subsystems/\320\241\320\265\321\200\320\262\320\270\321\201\320\275\321\213\320\265/CommandInterface.cmi" @@ -0,0 +1,2 @@ + + diff --git "a/src/test/resources/configuration/src/Subsystems/\320\237\320\237\320\230/Subsystems/\320\241\320\265\321\200\320\262\320\270\321\201\320\275\321\213\320\265/\320\241\320\265\321\200\320\262\320\270\321\201\320\275\321\213\320\265.mdo" "b/src/test/resources/configuration/src/Subsystems/\320\237\320\237\320\230/Subsystems/\320\241\320\265\321\200\320\262\320\270\321\201\320\275\321\213\320\265/\320\241\320\265\321\200\320\262\320\270\321\201\320\275\321\213\320\265.mdo" new file mode 100644 index 0000000..690fea9 --- /dev/null +++ "b/src/test/resources/configuration/src/Subsystems/\320\237\320\237\320\230/Subsystems/\320\241\320\265\321\200\320\262\320\270\321\201\320\275\321\213\320\265/\320\241\320\265\321\200\320\262\320\270\321\201\320\275\321\213\320\265.mdo" @@ -0,0 +1,13 @@ + + + Сервисные + + ru + Сервисные методы + + true + true + CommonModule.Преобразования + CommonModule.СтроковыеФункции + Subsystem.ППИ + diff --git "a/src/test/resources/configuration/src/Subsystems/\320\237\320\237\320\230/\320\237\320\237\320\230.mdo" "b/src/test/resources/configuration/src/Subsystems/\320\237\320\237\320\230/\320\237\320\237\320\230.mdo" new file mode 100644 index 0000000..2ab22b9 --- /dev/null +++ "b/src/test/resources/configuration/src/Subsystems/\320\237\320\237\320\230/\320\237\320\237\320\230.mdo" @@ -0,0 +1,13 @@ + + + ППИ + + ru + ППИ + + true + true + Прикладные + Сервисные + ОдинаковыеИмена + diff --git a/src/test/resources/docs/Hello.md b/src/test/resources/docs/Hello.md new file mode 100644 index 0000000..b2468c7 --- /dev/null +++ b/src/test/resources/docs/Hello.md @@ -0,0 +1 @@ +# Hi \ No newline at end of file diff --git "a/src/test/resources/docs/\320\237\321\200\320\270\320\272\320\273\320\260\320\264\320\275\321\213\320\265/index.md" "b/src/test/resources/docs/\320\237\321\200\320\270\320\272\320\273\320\260\320\264\320\275\321\213\320\265/index.md" new file mode 100644 index 0000000..5a84a5a --- /dev/null +++ "b/src/test/resources/docs/\320\237\321\200\320\270\320\272\320\273\320\260\320\264\320\275\321\213\320\265/index.md" @@ -0,0 +1,5 @@ +# Прикладные объекты + +Основной раздел системы + + \ No newline at end of file