映射块的源位置
由于 Asciidoctor 的主要重点是高效地转换文档,因此它默认不尝试在解析时跟踪块的源位置。然而,这些信息对于从源文档中提取信息、改进错误消息以及供扩展使用非常有用。因此,Asciidoctor 提供了一个映射块源位置的标志,称为 sourcemap。本页将举例说明如何启用 sourcemap 以及如何利用它提供的信息。
sourcemap 提供什么?
sourcemap 为解析文档中的所有块提供行和文件信息。具体来说,它提供了关于每个块开始位置的信息。块的开始位置不包括块上方的任何块元数据(块锚点和块属性)。
| sourcemap 还跟踪每个预处理器条件指令的开始行,因此如果指令未关闭,可以报告其位置。如果未启用 sourcemap,则使用文档末尾的位置。 |
sourcemap 仅将源位置信息添加到块。它不跟踪内联元素(如格式化文本或内联图像)或属性条目的源位置。
sourcemap 信息可在块的 source_location 属性上找到。启用 sourcemap 时,此属性的值是 Cursor 对象。Cursor 对象包含以下属性:
| file |
块开始的源文件的绝对文件名(如果输入是字符串,则值为 |
| dir |
块开始的源文件的绝对目录(如果输入是字符串,则值为 base dir) |
| lineno |
块开始的源文件中的行号(在任何空行或块元数据行之后) |
| path |
块开始的源文件的相对路径(从 docdir 开始)(如果输入是字符串,则值为 |
lineno 和 file 属性可以直接作为块本身的同名属性进行访问。
| sourcemap 并不完美。某些边缘情况,例如块跨越多个文件,或块开始和结束在 include 文件的最后一行,sourcemap 可能会报告错误的文件或行信息。如果您正在编写依赖于 sourcemap 的处理器,最好验证光标处的行是否是您期望找到的行,然后进行相应调整。 |
使用 :sourcemap 选项启用
sourcemap 功能可以通过 API 使用 :sourcemap 选项进行控制。此选项的值是布尔值。如果值为 false(默认),则 sourcemap 未启用。如果值为 true,则 sourcemap 已启用。:sourcemap 选项被所有 入口点方法(例如,Asciidoctor#load_file)接受。
以下是如何使用 API 启用 sourcemap 的示例
doc = Asciidoctor.load_file 'doc.adoc', safe: :safe, sourcemap: true
从扩展启用
您可以使用 Asciidoctor 预处理器扩展来启用 sourcemap。如果您需要访问块的源位置的扩展,但又不想要求用户向 Asciidoctor 传递额外的选项,则此技术非常有用。
Asciidoctor::Document.prepend (Module.new do
attr_writer :sourcemap
end) unless Asciidoctor::Document.method_defined? :sourcemap=
# A preprocessor that enables the sourcemap feature if not already enabled via the API.
Asciidoctor::Extensions.register do
preprocessor do
process do |doc, reader|
doc.sourcemap = true
nil
end
end
end
现在 sourcemap 已启用,您的扩展可以访问解析文档中块元素的源位置。
使用 sourcemap
启用 sourcemap 后,解析器将在解析文档的每个块上的 source_location 属性上存储源信息。让我们看一个例子。
首先,创建以下名为 doc.adoc 的 AsciiDoc 文件。
= Document Title
== Section
Paragraph.
Another paragraph.
现在,使用 Asciidoctor 加载此文件,并启用 :sourcemap 选项
doc = Asciidoctor.load_file 'doc.adoc', safe: :safe, sourcemap: true
让我们找到文档中的第一个段落并检查其源位置
first_paragraph = (doc.find_by context: :paragraph)[0]
puts first_paragraph.source_location
您将看到类似以下内容的输出
doc.adoc: line 5
您在这里看到的是光标的字符串值。如果您将 puts 替换为 pp,您会看到更多信息。
#<Asciidoctor::Reader::Cursor @dir="/path/to/docdir", @file="/path/to/docdir/doc.adoc", @lineno=5, @path="doc.adoc">
由于 file 和 lineno 是最有用的属性,它们可以直接从块访问
puts first_paragraph.file
puts first_paragraph.lineno
如果将节的源移至 include 文件,如下所示
= Document Title
include::partials/section.adoc[]
然后源位置将跟随段落进入该文件
#<Asciidoctor::Reader::Cursor @dir="/path/to/docdir/partials", @file="/path/to/docdir/partials/section.adoc", @lineno=3, @path="partials/section.adoc">
如果块具有元数据行,则在报告块的开始位置时会跳过这些行。例如,假设段落定义如下
[#p1]
Paragraph.
现在,源位置中段落的 lineno 比之前大一。
#<Asciidoctor::Reader::Cursor @dir="/path/to/docdir/partials", @file="/path/to/docdir/partials/section.adoc", @lineno=4, @path="partials/section.adoc">
如果您正在编写自定义转换器,则无法访问内联元素的源位置。但是,您可以访问父元素的源位置(例如 node.parent.source_location),这至少应该能让您接近元素的位置。