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