6 个版本

0.3.0 2024年4月12日
0.2.1 2019年10月7日
0.2.0 2019年4月11日
0.1.2 2018年12月17日

#55文本编辑器

MIT 许可证

105KB
2K SLoC

Outline

通用 literate 编程编译器。本项目旨在提供一款现代、开发者友好的 literate 编程工具。在网上简要搜索后,其他声称是“现代”的选择已经存在多年,并且语法相当丑陋(据我所知)。如果在这方面我错了,请创建一个 issue 来纠正我!

相比之下,Outline 使用熟悉的语法,可以进一步自定义以满足您的具体需求。它使用可插拔、可配置的输入格式,并自带对以下格式的支持:

  • Markdown;
  • Latex;
  • HTML;以及
  • (某种意义上的) Bird 风格

请参阅示例目录以获取每种风格的完整工作示例。

安装

提供的二进制文件(说实话是一个快速修改以使事情工作的技巧)可以通过 Cargo 安装。如果您尚未安装 Cargo(和 Rust),您可以使用 rustup 开始。

cargo install outline --features bin

outline 包也可用,允许您编写自己的解析器

outline = "0.1.2"

如果您编写了自己的解析器,请随意继续修改提供的 src/bin/main.rs 以支持您的新解析器。

集成

  • JavaScript (Webpack): outline-loader

  • Swift (XCode): 您可以编写构建规则,通过 Outline 二进制文件传递匹配的文件

    处理名称匹配的源文件: *.swift.md

    使用自定义脚本

    INFILE="$INPUT_FILE_DIR/$INPUT_FILE_NAME"
    OUTFILE="$DERIVED_FILE_DIR/$INPUT_FILE_BASE.swift"
    /Users/<username>/.cargo/bin/outline -l swift -s md < $INFILE > $OUTFILE
    

    输出文件: $(DERIVED_FILE_DIR)/$(INPUT_FILE_BASE).swift

  • Rust (Cargo): 您可以编写一个构建脚本,使用 Outline 包编译文件。请参阅 examples/hello-world/ 以获取一个非常牵强但有效的示例。

功能

在所有风格中,代码部分的处理方式相同,支持

  • 宏;
  • 元变量插值;
  • 注释提取;
  • 命名入口点;以及
  • 一个文件中包含多种语言

在所有样式下,文本部分的处理方式也是相同的——直接复制和写入,不进行任何处理。这允许您以任何喜欢的方式编写文档。目前,解析器之间的唯一区别是检测代码块开始和结束的方式。因此,编织的文档文件将与原始的文学源文件非常相似,只是对代码块语法的轻微修改,以确保它们在真实文档语言中有效。鉴于此,请注意,您希望对文档进行的任何后处理或格式更改都应该在生成的文档上执行。

宏使得文学程序能够按照人类读者的逻辑顺序编写。使用大纲,这是通过命名代码块并在以后通过“调用”宏来引用它们来实现的。虽然命名代码块的语法特定于文档样式,但宏调用的语法是相同的。

默认情况下,宏调用以长箭头 ==> 开始,并以句点 . 结束。这两个序列都可以根据您的需求进行自定义。宏调用的唯一限制是它们必须是一行上的唯一内容。也就是说,这以下是有效的

fn main() {
  ==> Calculate the very complex result.
  ==> Print the results for the user.
}

但是,这不会调用在 if() 内命名的宏,因为宏序列不会开始和结束一行

fn main() {
  if (==> A very complex condition is true.) {
    ==> Do something cool.
  }
}

值得注意的另一个宏特性是,如果两个代码块具有相同的名称,它们将按顺序连接。这在定义全局变量或在它们使用的部分附近列出导入时非常有用。

元变量

如果您将宏调用视为函数调用,那么元变量就像参数一样。

默认情况下,为了表明宏包含元变量,变量的名称必须作为宏名称的一部分,由 @{} 分隔。

然后可以在宏内通过在代码中使用其名称的 @{} 来使用该元变量。

最后,通过在调用中将变量的名称替换为其值来调用具有元变量的宏。

示例

Here is our macro with meta variables:

\begin{code}[language=rs,name={Say @{something} to @{someone}}]
println!("Hey, @{someone}! I was told to tell you \"@{something}\"");
\end{code}

Now, to say things to many people:

\begin{code}[language=rs]
==> Say @{Hello} to @{Jim}.
==> Say @{How are you} to @{Tom}.
==> Say @{I am good!} to @{Angela}.
\end{code}

此功能在编写宏时提供了更多的灵活性,同时也可能使意图更加明确。

提取注释

默认情况下,注释提取序列设置为 //,仅为了熟悉。在此序列之后(包括此序列)的任何文本都将从代码块中提取出来,并且不会渲染到编织的源代码中。请注意,由于注释在编译时完全删除,因此它们不必使用您编程语言的实际行注释指示符。事实上,选择一个 不是 正常注释指示符的序列可能更好,这样您仍然可以在您的编织代码输出中保留注释。

现在这些注释被提取出来后,我们可以在编织的文档文件中对它们进行不同的处理。尽管某些格式不支持任何特殊行为,只是将这些注释写回到代码中,但某些格式能够提供特殊的渲染。特别是,标准的Markdown和HTML样式能够将提取出的注释渲染在<aside>标签中,然后可以使用CSS将其很好地渲染。

请参考HTML示例,了解如何渲染提取出的注释的一种方式。

命名入口点

默认情况下,程序的入口点总是未命名的代码块。然而,这限制了单个输入文件的输出总是相同的源代码。这也意味着你无法在文档中对入口点进行命名,这可能是有用的。

为了解决这个问题,可以在命令行中将入口点名称传递给Outline。然后,它将从这个具有该名称的代码块开始,而不是从未命名的代码块开始。

请注意,如果你使用命名入口点,就无法引用未命名的代码块作为宏。然而,你可以使用未命名的代码块来提供示例,例如,向文档的读者提供示例,这样它们仍然是有用的。

多语言

某些文档格式允许你指定代码块编写使用的语言。实际上,建议你在编写代码块时始终包含语言,特别是当同一文档中使用多种编程语言时。

通过正确标记所有代码块,可以同时编写使用多种编程语言的项目。虽然这实际与否尚待观察,但它仍然受到支持。通过在命令行中提供语言名称,只有生成纠缠源时使用的代码块才是该语言。例如,以下是一个用两种语言编写的简单程序:

Here we have hello world in Ruby:

\begin{code}[language=rb]
puts "Hello world"
\end{code}

And here it is again in Rust:

\begin{code}[language=rs]
fn main() {
  println!("Hello world");
}
\end{code}

编译时不提供语言信息,仅忽略语言信息,因此将生成包含两种语言的单一输出。然而,向Outline提供--language rb标志将导致仅使用带有rb标签的代码块来生成代码。

用法

Outline 1.0
Cameron Eldridge <cameldridge@gmail.com>
Literate programming compiler

USAGE:
    outline [OPTIONS] [input]...

FLAGS:
    -h, --help       Prints help information
    -V, --version    Prints version information

OPTIONS:
    -o, --output <code_dir>          Output tangled code files to this directory. No code files will be printed by
                                     default.
    -c, --config <config_file>       Sets the config file name [default: Outline.toml]
    -d, --docs <doc_dir>             Directory to output weaved documentation files to. No documentation will be printed
                                     by default.
    -e, --entrypoint <entrypoint>    The named entrypoint to use when tangling code. Defaults to the unnamed code block.
    -l, --language <language>        The language to output the tangled code in. Only code blocks in this language will
                                     be used.
    -s, --style <style>              Sets the style to use. If not specified, it is inferred from the file extension.
                                     When reading from STDIN, defaults to 'md'. [possible values: bird, md, tex, html]

ARGS:
    <input>..

配置

每种样式都支持一些额外的配置,这些配置通过toml配置文件(默认:Outline.toml)提供。可以在配置文件中同时配置多个样式。请注意,如果样式出现在配置文件中,则需要其完整选项集(所有默认值都将被丢弃)。

有关这些选项的更多信息,请参阅API文档

[tex]
code_environment = "code"
default_language = "rs" # optional
comment_start = "//"
interpolation_start = "@{"
interpolation_end = "}"
macro_start = "==> "
macro_end = "."

[md]
fence_sequence = "```"
block_name_start = " - "
comments_as_aside = false
default_language = "rs" # optional
comment_start = "//"
interpolation_start = "@{"
interpolation_end = "}"
macro_start = "==> "
macro_end = "."

[html]
code_tag = "code"
language_attribute = "data-language"
name_attribute = "data-name"
block_class = "block"
language_class = "language-{}"
comments_as_aside = true
default_language = "rs" # optional
comment_start = "//"
interpolation_start = "@{"
interpolation_end = "}"
macro_start = "==> "
macro_end = "."

[bird]
code_marker = "> "
code_name_marker = ">>> "
comment_start = "//"
interpolation_start = "@{"
interpolation_end = "}"
macro_start = "==> "
macro_end = "."

扩展

你可以编写自己的Outline解析器来处理更多格式,或者扩展现有格式。

为此,你需要实现三个特质 - ParserPrinterParserConfig

Parser特质负责决定代码块开始和结束的位置,并根据该信息创建一个Document

Printer负责接受解析后的代码块,并将它们写回,可能是比解析时更“有效”的形式。

ParserConfig公开最常用的配置选项,以便实现核心功能,如宏调用、元变量和注释提取。

目前,如果您想编写自己的解析器,我建议以现有的解析器为起点,然后参考src/bin/main.rs来了解如何使用您完成的解析器。

此外,API文档也是一个很好的参考地方。

依赖项

~0.4–1.1MB
~24K SLoC