Script to generate Markdown files with embedded PlantUML diagrams for GitLab's PlantUML renderer

huangapple go评论76阅读模式
英文:

Script to generate Markdown files with embedded PlantUML diagrams for GitLab's PlantUML renderer

问题

I understand your request. Here's the translated content:

"我正在设置一个存储由多个Markdown文档组成的软件文档的存储库,并希望能够嵌入PlantUML图表。该存储库托管在Gitlab中,其中包括PlantUML渲染器,但不允许预处理,并因此不允许使用!include子句引用其他文件中的图表。

我想要一个Bash或Python脚本,它可以:

  1. 搜索所有的.md文件,并将它们的内容追加到一个新文件"all-docs.md"中。
  2. 在文件"all-docs.md"中搜索所有的!include [FILEPATH]子句,并将位于该文件[FILEPATH]中的@startuml@enduml之间的内容替换为"all-docs.md"中的内容。

例如:

"all-docs.md"在某个部分包含:

这是Profile类图:

``plantuml
@startuml
!include ./data-models/profile.puml
Profile o-- UidObject
@enduml

而profile.puml的内容是:

@startuml
class Profile <UidObject> {
    + string name
    + string email
    + string phone
    + Date birthDate
}
@enduml

脚本运行后的结果将是在"all-docs.md"中拥有以下内容:

这是Profile类图:

``plantuml
@startuml
class Profile <UidObject> {
    + string name
    + string email
    + string phone
    + Date birthDate
}
Profile o-- UidObject
@enduml

该存储库具有以下结构。

/
├── assets/
├── docs/
├── uml/
  • "assets/"目录包含各种资源,如图像、图标和其他资源。
  • "docs/"目录包含文档(Markdown文件)。
  • "uml/"目录包含包含用于生成软件文档的图表的PlantUML源文件。"
英文:

I am setting up a repository to store software documentation consisting of several documents which are written in Markdown, and I want to be able to embed PlantUML diagrams in them. The repository is hosted in Gitlab, which includes a PlantUML renderer but does not allow preprocessing and therefore using the !include clause to reference diagrams in other files.

I would like to have a bash or python script that:

  1. Searches all .md files and append their content one after the other in a new file "all-docs.md".
  2. Searches in that file "all-docs.md" for all the !include [FILEPATH] clauses and replace the content which is between @startuml and @enduml from that file [FILEPATH] into "all-docs.md".

For example:

"all-docs.md" contains in certain part:

Here is the Profile class diagram:

``plantuml
@startuml
!include ./data-models/profile.puml
Profile o-- UidObject
@enduml
``

And profile.puml content is:

@startuml
class Profile <UidObject> {
    + string name
    + string email
    + string phone
    + Date birthDate
}
@enduml

The result after the script will be to have in "all-docs.md":

Here is the Profile class diagram:

``plantuml
@startuml
class Profile <UidObject> {
    + string name
    + string email
    + string phone
    + Date birthDate
}
Profile o-- UidObject
@enduml
``

The repo has the following structure.

/
├── assets/
├── docs/
├── uml/
  • The assets/ directory contains various assets such as images, icons, and other resources.
  • The docs/ directory contains the documents (markdown files)
  • The uml/ directory contains contains PlantUML source files that are used to generate diagrams for the software documentation.

答案1

得分: 1

A bashfind 解决方案,使用您提供的输入/文件,类似于:

#!/usr/bin/env bash

#: 在所有-docs.md文件中查找并连接docs/目录中的所有.md文件。
find docs/ -type f -name '*.md' -exec sh -c 'cat -- "$@" >> all-docs.md' sh {} +

#: 解析all-docs.md文件并
#: 创建/打印所需的结果/输出。
while IFS= read -r line; do
  if [[ $line == "!include"* ]]; then
    temp=${line#!include *}
    mapfile -t plum < "$temp" &&
    unset -v 'plum[-1]' &&
    printf '%s\n' "${plum[@]:1}"
  else
    printf '%s\n' "$line"
  fi
done {fd}< all-docs.md
  • 将代码的最后一行替换为
done {fd}< all-docs.md > tempfile && mv tempfile all-docs.md
  • 如果您对输出满意并需要对all-docs.md进行永久更改。

或者直接解析find的输出,而不创建all-docs.md,类似于:

#!/usr/bin/env bash

##: 在docs/目录中查找以.md结尾的所有文件
##: 连接所需文件的所有内容。
find docs/ -type f -name '*.md' -exec sh -c 'cat -- "$@"' sh {} + | {
  while IFS= read -r line; do
    [[ $line != "!include"* ]] && { ##: 如果行中没有模式!include。
      printf '%s\n' "$line"         ##: 打印行。
    }
    [[ $line == "!include"* ]] && { ##: 如果行包含模式!include,解析它。
      temp=${line#!include *}       ##: 将FILE_PATH提取到名为temp的变量中。
      if [[ -s "$temp" ]]; then     ##: 如果变量是现有的非空文件。
        mapfile -u3 -t plum 3< "$temp" && ##: 提取所需结果。
        unset -v 'plum[-1]' &&
        printf '%s\n' "${plum[@]:1}"
      else
        printf '%s\n' "$line"  ##: 否则只打印行。
      fi
    }
  done
}
  • 如果创建all-docs.md是必须的要求,然后更改最后一行为:
} > all-docs.md
  • mapfile(又名readarray)和{fd}都需要bash v4+
英文:

A bash and find solution with your given input/files, something like:

#!/usr/bin/env bash

#: Find and concatenate all .md files from docs/ directory in all-docs.md file.
find docs/ -type f -name &#39;*.md&#39; -exec sh -c &#39;cat -- &quot;$@&quot; &gt;&gt; all-docs.md&#39; sh {} +

#: Parse the all-docs.md file and 
#: create/print the desired result/output.
while IFS= read -ru &quot;$fd&quot; line; do
  if [[ $line == &quot;!include&quot;* ]]; then
    temp=${line#!include *}
    mapfile -t plum &lt; &quot;$temp&quot; &amp;&amp;
    unset -v &#39;plum[-1]&#39; &amp;&amp;
    printf &#39;%s\n&#39; &quot;${plum[@]:1}&quot;
  else
    printf &#39;%s\n&#39; &quot;$line&quot;
  fi
done {fd}&lt; all-docs.md

  • Replace the last line of the code to
done {fd}&lt; all-docs.md &gt; tempfile &amp;&amp; mv tempfile all-docs.md
  • If you're satisfied with the output and permanent changes needs to be made for all-docs.md.

Or just parse the output of find directly without creating the all-docs.md, something like:

#!/usr/bin/env bash

##: Find all the files ending in .md in the docs/ directory
##: conCATenate all the contents of the files in question.
find docs/ -type f -name &#39;*.md&#39; -exec sh -c &#39;cat -- &quot;$@&quot;&#39; sh {} + | {
  while IFS= read -r line; do
    [[ $line != &quot;!include&quot;* ]] &amp;&amp; { ##: If line does not have the pattern !include.
      printf &#39;%s\n&#39; &quot;$line&quot;         ##: Print the line as is.
    }
    [[ $line == &quot;!include&quot;* ]] &amp;&amp; { ##: If line has the pattern !include parse it.
      temp=${line#!include *}       ##: Extract FILE_PATH in a variable named temp.
      if [[ -s &quot;$temp&quot; ]]; then     ##: If variable is an existing non-empty file.
        mapfile -u3 -t plum 3&lt; &quot;$temp&quot; &amp;&amp; ##: Extract the desired result.
        unset -v &#39;plum[-1]&#39; &amp;&amp;
        printf &#39;%s\n&#39; &quot;${plum[@]:1}&quot;
      else
        printf &#39;%s\n&#39; &quot;$line&quot;  ##: Otherwise just print the line as is.
      fi
    }
  done
}

If creating the all-docs.md is a must/requirement, then change the last line to:

} &gt; all-docs.md

  • Both mapfile aka readarray and {fd} requires bash v4+

答案2

得分: 0

I was able to find a solution myself using Python.

  1. 使用Python找到了自己的解决方案。
  2. 定义一个名为replace_include_with_content的新函数。该函数以一行作为输入,检查它是否匹配!include子句的模式。如果匹配,它会提取包含路径,构建到相应UML文件的相对路径,读取其内容并返回。否则,它将原样返回该行。
  3. 遍历排序后的Markdown文件,读取它们的内容。在此循环内,遍历每个文件内容的行。对于每一行,我们调用replace_include_with_content函数来检查是否需要替换它。
英文:

I was able to find a solution myself using Python.

  1. Get a list of markdown files in the source "docs" directory and sort them based on their index prefix (files are named "0.introduction.md", "1.general.md", etc):
  2. Define a new function called replace_include_with_content. This function takes a line as input and checks if it matches the pattern for the !include clause. If it does, it extracts the include path, constructs the relative path to the corresponding uml file, reads its content, and returns it. Otherwise, it returns the line as is.
  3. Iterate through the sorted markdown files, read their contents. Within this loop, iterate over the lines of each file's content. For each line, we call the replace_include_with_content function to check if it needs to be replaced.
import os
import re

docs_dir = &quot;docs&quot;
uml_dir = &quot;uml&quot;
output_file = &quot;all-docs.md&quot;

# Get a sorted list of markdown files in the source directory
markdown_files = sorted(
    ,
    key=lambda x: int(x.split(&quot;.&quot;)[0])
)

# Replace function
def replace_include_with_content(line):
    # Check if the line matches the pattern for !include clause
    match = re.match(r&quot;!include\s+(.+)&quot;, line)
    if match:
        include_path = match.group(1)
        uml_file_path = os.path.join(uml_dir, include_path)
        # Read the content of the referenced UML file
        with open(uml_file_path, &quot;r&quot;) as uml_file:
            uml_content = uml_file.readlines()
            filtered_content = []
            include_next_line = False
            for uml_line in uml_content:
                if uml_line.strip() == &quot;@startuml&quot;:
                    include_next_line = True
                elif uml_line.strip() == &quot;@enduml&quot;:
                    include_next_line = False
                elif include_next_line:
                    filtered_content.append(uml_line)
            return &quot;&quot;.join(filtered_content)
    return line

# Open the output file in write mode
with open(output_file, &quot;w&quot;) as outfile:
    for file in markdown_files:
        # Open each markdown file in read mode
        with open(os.path.join(docs_dir, file), &quot;r&quot;) as infile:
            content = infile.readlines()
            for line in content:
                # Replace the !include clause with the content from the referenced UML file
                replaced_line = replace_include_with_content(line)
                outfile.write(replaced_line)
        outfile.write(&quot;\n&quot;)

huangapple
  • 本文由 发表于 2023年5月13日 20:19:26
  • 转载请务必保留本文链接:https://go.coder-hub.com/76242695.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定