File: sourcemap.adoc

package info (click to toggle)
asciidoctor 2.0.26-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 5,464 kB
  • sloc: ruby: 45,298; sh: 147; xml: 53; javascript: 48; makefile: 26; ml: 1
file content (179 lines) | stat: -rw-r--r-- 6,280 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
= Map Source Location of Blocks
:navtitle: Enable the Sourcemap

Since Asciidoctor's primary focus is on converting documents efficiently, it does not attempt to track the source location of blocks when parsing by default.
However, such information can be useful for extracting information from the source document, improving error messages, and for use in extensions.
Therefore, Asciidoctor provides a flag to map the source location of blocks, known as the sourcemap.
This page examples how to enable the sourcemap and how to make use of the information it provides.

== What does the sourcemap provide?

The sourcemap provides line and file information for all blocks in the parsed document.
Specifically, it provides information about the start of each block.
The start of the block does not include any block metadata (block anchor and block attributes) above the block.

TIP: The sourcemap also tracks the start line of every preprocessor conditional directive so its position can be reported if the directive isn't closed.
If the sourcemap isn't enabled, the location at the end of the document is used instead.

The sourcemap only adds source location information to blocks.
It does not track the source location for inline elements, such as formatted text or an inline image, or for attribute entries.

The sourcemap information is available on the `source_location` property of the block.
When the sourcemap is enabled, the value of this property is a `Cursor` object.
The `Cursor` object contains the following properties:

[horizontal]
file:: the absolute filename of the source file where the block starts (if input is a string, the value is `nil`)
dir:: the absolute directory of the source file where the block starts (if input is a string, the value is the base dir)
lineno:: the line number in the source file where the block starts (after any empty or block metadata lines)
path:: the relative path (starting from docdir) of the source file where the block starts (if input is a string, the value is `<stdin>`)

The `lineno` and `file` properties can be accessed as properties with the same name on the block itself.

IMPORTANT: The sourcemap is not perfect.
There are certain edge cases, such as when the block is split across multiple files or the block starts and ends on the last line of an include file, when the sourcemap may report the wrong file or line information.
If you're writing a processor that relies on the sourcemap, it's a good idea to verify that the line at the cursor is the one you expect to find, then adjust accordingly.

== Enable using :sourcemap option

The sourcemap feature can be controlled from the API using the `:sourcemap` option.
The value of this option is a boolean.
If the value is `false` (default), the sourcemap is not enabled.
If the value is `true`, the sourcemap is enabled.
The `:sourcemap` option is accepted by all xref:index.adoc#entrypoints[entrypoint methods] (e.g., Asciidoctor#load_file).

Here's an example of how to enable the sourcemap using the API:

[,ruby]
----
doc = Asciidoctor.load_file 'doc.adoc', safe: :safe, sourcemap: true
----

== Enable from extension

You can enable the sourcemap using an Asciidoctor preprocessor extension.
This technique is useful if your extension needs access to the source location of blocks, but you don't want to require users to pass an additional option to Asciidoctor.

[,ruby]
----
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
----

Now that the sourcemap is enabled, your extension can access the source location of the block elements in the parsed document.

== Use the sourcemap

When the sourcemap is enabled, the parser will store source information on the `source_location` property on each block in the parsed document.
Let's look at an example.

Start by creating the following AsciiDoc file named [.path]_doc.adoc_.

.doc.adoc
[,asciidoc]
----
= Document Title

== Section

Paragraph.

Another paragraph.
----

Now, load this file using Asciidoctor with the `:sourcemap` option enabled:

[,ruby]
----
doc = Asciidoctor.load_file 'doc.adoc', safe: :safe, sourcemap: true
----

Let's find the first paragraph in the document and inspect its source location:

[,ruby]
----
first_paragraph = (doc.find_by context: :paragraph)[0]
puts first_paragraph.source_location
----

You'll see output similar to what's shown below:

[.output]
....
doc.adoc: line 5
....

What you're seeing here is the string value of the cursor.
There's more information to see if you replace `puts` with `pp`:

[.output]
....
#<Asciidoctor::Reader::Cursor
 @dir="/path/to/docdir",
 @file="/path/to/docdir/doc.adoc",
 @lineno=5,
 @path="doc.adoc">
....

Since file and lineno are the most useful properties, they can be accessed directly from the block:

[,ruby]
----
puts first_paragraph.file
puts first_paragraph.lineno
----

If you move the source of the section to an include file, as shown here:

.doc.adoc
[,asciidoc]
----
= Document Title

\include::partials/section.adoc[]
----

then the source location will follow the paragraph into that file:

[.output]
....
#<Asciidoctor::Reader::Cursor
 @dir="/path/to/docdir/partials",
 @file="/path/to/docdir/partials/section.adoc",
 @lineno=3,
 @path="partials/section.adoc">
....

If the block has metadata lines, those lines are skipped when reporting the start location of the block.
For example, let's assume the paragraph is defined as follows:

[,asciidoc]
----
[#p1]
Paragraph.
----

The lineno of the paragraph in the source location is now one greater than before:

[.output]
....
#<Asciidoctor::Reader::Cursor
 @dir="/path/to/docdir/partials",
 @file="/path/to/docdir/partials/section.adoc",
 @lineno=4,
 @path="partials/section.adoc">
....

If you're writing a custom converter, the source location is not available for inline elements.
However, you can access the source location of the parent element (e.g., `node.parent.source_location`), which should at least get you close to the location of the element.