Class: Asciidoctor::Reader
- Inherits:
-
Object
- Object
- Asciidoctor::Reader
- Includes:
- Logging
- Defined in:
- lib/asciidoctor/reader.rb
Overview
Methods for retrieving lines from AsciiDoc source files
Direct Known Subclasses
Defined Under Namespace
Classes: Cursor
Instance Attribute Summary collapse
- #dir ⇒ Object readonly
- #file ⇒ Object readonly
-
#lineno ⇒ Object
readonly
Get the 1-based offset of the current line.
- #path ⇒ Object readonly
-
#process_lines ⇒ Object
Control whether lines are processed using Reader#process_line on first visit (default: true).
-
#source_lines ⇒ Object
readonly
Get the document source as a String Array of lines.
-
#unterminated ⇒ Object
Indicates that the end of the reader was reached with a delimited block still open.
Instance Method Summary collapse
-
#advance ⇒ Object
Advance to the next line by discarding the line at the front of the stack.
- #cursor ⇒ Object
- #cursor_at_line(lineno) ⇒ Object
- #cursor_at_mark ⇒ Object
- #cursor_at_prev_line ⇒ Object
- #cursor_before_mark ⇒ Object
-
#empty? ⇒ Boolean
(also: #eof?)
Check whether this reader is empty (contains no lines).
-
#has_more_lines? ⇒ True
Check whether there are any lines left to read.
-
#initialize(data = nil, cursor = nil, opts = {}) ⇒ Reader
constructor
Initialize the Reader object.
-
#line_info ⇒ A
Get information about the last line read, including file name and line number.
-
#lines ⇒ A
Get a copy of the remaining Array of String lines managed by this Reader.
- #mark ⇒ Object
-
#next_line_empty? ⇒ True
Peek at the next line and check if it’s empty (i.e., whitespace only).
-
#peek_line(direct = false) ⇒ Object
Peek at the next line of source data.
-
#peek_lines(num = nil, direct = false) ⇒ A
Peek at the next multiple lines of source data.
-
#read ⇒ Object
Get the remaining lines of source data joined as a String.
-
#read_line ⇒ Object
Get the next line of source data.
-
#read_lines ⇒ Object
(also: #readlines)
Get the remaining lines of source data.
-
#read_lines_until(options = {}) ⇒ Object
Return all the lines from ‘@lines` until we (1) run out them, (2) find a blank line with `break_on_blank_lines: true`, or (3) find a line for which the given block evals to true.
-
#replace_next_line(replacement) ⇒ Object
(also: #replace_line)
Replace the next line with the specified line.
-
#skip_blank_lines ⇒ Integer
Skip blank lines at the cursor.
-
#skip_comment_lines ⇒ void
Skip consecutive comment lines and block comments.
-
#skip_line_comments ⇒ Object
Skip consecutive comment lines and return them.
-
#source ⇒ Object
Get the source lines for this Reader joined as a String.
-
#string ⇒ Object
Get a copy of the remaining lines managed by this Reader joined as a String.
-
#terminate ⇒ void
Advance to the end of the reader, consuming all remaining lines.
- #to_s ⇒ Object
-
#unshift_line(line_to_restore) ⇒ void
(also: #restore_line)
Push the String line onto the beginning of the Array of source data.
-
#unshift_lines(lines_to_restore) ⇒ void
(also: #restore_lines)
Push an Array of lines onto the front of the Array of source data.
Methods included from Logging
#logger, #message_with_context
Constructor Details
#initialize(data = nil, cursor = nil, opts = {}) ⇒ Reader
Initialize the Reader object
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 |
# File 'lib/asciidoctor/reader.rb', line 42 def initialize data = nil, cursor = nil, opts = {} if !cursor @file = nil @dir = '.' @path = '<stdin>' @lineno = 1 # IMPORTANT lineno assignment must proceed prepare_lines call! elsif ::String === cursor @file = cursor @dir, @path = ::File.split @file @lineno = 1 # IMPORTANT lineno assignment must proceed prepare_lines call! else if (@file = cursor.file) @dir = cursor.dir || (::File.dirname @file) @path = cursor.path || (::File.basename @file) else @dir = cursor.dir || '.' @path = cursor.path || '<stdin>' end @lineno = cursor.lineno || 1 # IMPORTANT lineno assignment must proceed prepare_lines call! end @lines = prepare_lines data, opts @source_lines = @lines.drop 0 @mark = nil @look_ahead = 0 @process_lines = true @unescape_next_line = false @unterminated = nil @saved = nil end |
Instance Attribute Details
#dir ⇒ Object (readonly)
26 27 28 |
# File 'lib/asciidoctor/reader.rb', line 26 def dir @dir end |
#file ⇒ Object (readonly)
25 26 27 |
# File 'lib/asciidoctor/reader.rb', line 25 def file @file end |
#lineno ⇒ Object (readonly)
Get the 1-based offset of the current line.
30 31 32 |
# File 'lib/asciidoctor/reader.rb', line 30 def lineno @lineno end |
#path ⇒ Object (readonly)
27 28 29 |
# File 'lib/asciidoctor/reader.rb', line 27 def path @path end |
#process_lines ⇒ Object
Control whether lines are processed using Reader#process_line on first visit (default: true)
36 37 38 |
# File 'lib/asciidoctor/reader.rb', line 36 def process_lines @process_lines end |
#source_lines ⇒ Object (readonly)
Get the document source as a String Array of lines.
33 34 35 |
# File 'lib/asciidoctor/reader.rb', line 33 def source_lines @source_lines end |
#unterminated ⇒ Object
Indicates that the end of the reader was reached with a delimited block still open.
39 40 41 |
# File 'lib/asciidoctor/reader.rb', line 39 def unterminated @unterminated end |
Instance Method Details
#advance ⇒ Object
Advance to the next line by discarding the line at the front of the stack
214 215 216 |
# File 'lib/asciidoctor/reader.rb', line 214 def advance shift ? true : false end |
#cursor ⇒ Object
479 480 481 |
# File 'lib/asciidoctor/reader.rb', line 479 def cursor Cursor.new @file, @dir, @path, @lineno end |
#cursor_at_line(lineno) ⇒ Object
483 484 485 |
# File 'lib/asciidoctor/reader.rb', line 483 def cursor_at_line lineno Cursor.new @file, @dir, @path, lineno end |
#cursor_at_mark ⇒ Object
487 488 489 |
# File 'lib/asciidoctor/reader.rb', line 487 def cursor_at_mark @mark ? Cursor.new(*@mark) : cursor end |
#cursor_at_prev_line ⇒ Object
500 501 502 |
# File 'lib/asciidoctor/reader.rb', line 500 def cursor_at_prev_line Cursor.new @file, @dir, @path, @lineno - 1 end |
#cursor_before_mark ⇒ Object
491 492 493 494 495 496 497 498 |
# File 'lib/asciidoctor/reader.rb', line 491 def cursor_before_mark if @mark m_file, m_dir, m_path, m_lineno = @mark Cursor.new m_file, m_dir, m_path, m_lineno - 1 else Cursor.new @file, @dir, @path, @lineno - 1 end end |
#empty? ⇒ Boolean Also known as: eof?
Check whether this reader is empty (contains no lines)
91 92 93 94 95 96 97 98 |
# File 'lib/asciidoctor/reader.rb', line 91 def empty? if @lines.empty? @look_ahead = 0 true else false end end |
#has_more_lines? ⇒ True
Check whether there are any lines left to read.
If a previous call to this method resulted in a value of false, immediately returned the cached value. Otherwise, delegate to peek_line to determine if there is a next line available.
79 80 81 82 83 84 85 86 |
# File 'lib/asciidoctor/reader.rb', line 79 def has_more_lines? if @lines.empty? @look_ahead = 0 false else true end end |
#line_info ⇒ A
Get information about the last line read, including file name and line number.
511 512 513 |
# File 'lib/asciidoctor/reader.rb', line 511 def line_info %(#{@path}: line #{@lineno}) end |
#lines ⇒ A
Get a copy of the remaining Array of String lines managed by this Reader
518 519 520 |
# File 'lib/asciidoctor/reader.rb', line 518 def lines @lines.drop 0 end |
#mark ⇒ Object
504 505 506 |
# File 'lib/asciidoctor/reader.rb', line 504 def mark @mark = @file, @dir, @path, @lineno end |
#next_line_empty? ⇒ True
Peek at the next line and check if it’s empty (i.e., whitespace only)
This method Does not consume the line from the stack.
106 107 108 |
# File 'lib/asciidoctor/reader.rb', line 106 def next_line_empty? peek_line.nil_or_empty? end |
#peek_line(direct = false) ⇒ Object
Peek at the next line of source data. Processes the line if not already marked as processed, but does not consume it.
This method will probe the reader for more lines. If there is a next line that has not previously been visited, the line is passed to the Reader#process_line method to be initialized. This call gives sub-classes the opportunity to do preprocessing. If the return value of the Reader#process_line is nil, the data is assumed to be changed and Reader#peek_line is invoked again to perform further processing.
If has_more_lines? is called immediately before peek_line, the direct flag is implicitly true (since the line is flagged as visited).
128 129 130 131 132 133 134 135 136 137 138 139 140 |
# File 'lib/asciidoctor/reader.rb', line 128 def peek_line direct = false if direct || @look_ahead > 0 @unescape_next_line ? ((line = @lines[0]).slice 1, line.length) : @lines[0] elsif @lines.empty? @look_ahead = 0 nil else # FIXME the problem with this approach is that we aren't # retaining the modified line (hence the @unescape_next_line tweak) # perhaps we need a stack of proxied lines (line = process_line @lines[0]) ? line : peek_line end end |
#peek_lines(num = nil, direct = false) ⇒ A
Peek at the next multiple lines of source data. Processes the lines if not already marked as processed, but does not consume them.
This method delegates to Reader#read_line to process and collect the line, then restores the lines to the stack before returning them. This allows the lines to be processed and marked as such so that subsequent reads will not need to process the lines again.
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 |
# File 'lib/asciidoctor/reader.rb', line 155 def peek_lines num = nil, direct = false old_look_ahead = @look_ahead result = [] (num || MAX_INT).times do if (line = direct ? shift : read_line) result << line else @lineno -= 1 if direct break end end unless result.empty? unshift_all result @look_ahead = old_look_ahead if direct end result end |
#read ⇒ Object
Get the remaining lines of source data joined as a String.
Delegates to Reader#read_lines, then joins the result.
207 208 209 |
# File 'lib/asciidoctor/reader.rb', line 207 def read read_lines.join LF end |
#read_line ⇒ Object
Get the next line of source data. Consumes the line returned.
179 180 181 182 |
# File 'lib/asciidoctor/reader.rb', line 179 def read_line # has_more_lines? triggers preprocessor shift if @look_ahead > 0 || has_more_lines? end |
#read_lines ⇒ Object Also known as: readlines
Get the remaining lines of source data.
This method calls Reader#read_line repeatedly until all lines are consumed and returns the lines as a String Array. This method differs from Reader#lines in that it processes each line in turn, hence triggering any preprocessors implemented in sub-classes.
192 193 194 195 196 197 198 199 |
# File 'lib/asciidoctor/reader.rb', line 192 def read_lines lines = [] # has_more_lines? triggers preprocessor while has_more_lines? lines << shift end lines end |
#read_lines_until(options = {}) ⇒ Object
Return all the lines from ‘@lines` until we (1) run out them,
(2) find a blank line with `break_on_blank_lines: true`, or (3) find
a line for which the given block evals to true.
395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 |
# File 'lib/asciidoctor/reader.rb', line 395 def read_lines_until = {} result = [] if @process_lines && [:skip_processing] @process_lines = false restore_process_lines = true end if (terminator = [:terminator]) start_cursor = [:cursor] || cursor break_on_blank_lines = false break_on_list_continuation = false else break_on_blank_lines = [:break_on_blank_lines] break_on_list_continuation = [:break_on_list_continuation] end skip_comments = [:skip_line_comments] complete = line_read = line_restored = nil shift if [:skip_first_line] while !complete && (line = read_line) complete = while true break true if terminator && line == terminator # QUESTION: can we get away with line.empty? here? break true if break_on_blank_lines && line.empty? if break_on_list_continuation && line_read && line == LIST_CONTINUATION [:preserve_last_line] = true break true end break true if block_given? && (yield line) break false end if complete if [:read_last_line] result << line line_read = true end if [:preserve_last_line] unshift line line_restored = true end else unless skip_comments && (line.start_with? '//') && !(line.start_with? '///') result << line line_read = true end end end if restore_process_lines @process_lines = true @look_ahead -= 1 if line_restored && !terminator end if terminator && terminator != line && (context = .fetch :context, terminator) start_cursor = cursor_at_mark if start_cursor == :at_mark logger.warn %(unterminated #{context} block), source_location: start_cursor @unterminated = true end result end |
#replace_next_line(replacement) ⇒ Object Also known as: replace_line
Replace the next line with the specified line.
Calls Reader#advance to consume the current line, then calls Reader#unshift to push the replacement onto the top of the line stack.
257 258 259 260 261 |
# File 'lib/asciidoctor/reader.rb', line 257 def replace_next_line replacement shift unshift replacement true end |
#skip_blank_lines ⇒ Integer
Skip blank lines at the cursor.
278 279 280 281 282 283 284 285 286 287 288 289 290 291 |
# File 'lib/asciidoctor/reader.rb', line 278 def skip_blank_lines return if empty? num_skipped = 0 # optimized code for shortest execution path while (next_line = peek_line) if next_line.empty? shift num_skipped += 1 else return num_skipped end end end |
#skip_comment_lines ⇒ void
This method returns an undefined value.
Skip consecutive comment lines and block comments.
306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 |
# File 'lib/asciidoctor/reader.rb', line 306 def skip_comment_lines return if empty? while (next_line = peek_line) && !next_line.empty? if next_line.start_with? '//' if next_line.start_with? '///' if (ll = next_line.length) > 3 && next_line == '/' * ll read_lines_until terminator: next_line, skip_first_line: true, read_last_line: true, skip_processing: true, context: :comment else break end else shift end else break end end nil end |
#skip_line_comments ⇒ Object
Skip consecutive comment lines and return them.
This method assumes the reader only contains simple lines (no blocks).
331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 |
# File 'lib/asciidoctor/reader.rb', line 331 def skip_line_comments return [] if empty? comment_lines = [] # optimized code for shortest execution path while (next_line = peek_line) && !next_line.empty? if (next_line.start_with? '//') comment_lines << shift else break end end comment_lines end |
#source ⇒ Object
Get the source lines for this Reader joined as a String
528 529 530 |
# File 'lib/asciidoctor/reader.rb', line 528 def source @source_lines.join LF end |
#string ⇒ Object
Get a copy of the remaining lines managed by this Reader joined as a String
523 524 525 |
# File 'lib/asciidoctor/reader.rb', line 523 def string @lines.join LF end |
#terminate ⇒ void
This method returns an undefined value.
Advance to the end of the reader, consuming all remaining lines
350 351 352 353 354 355 |
# File 'lib/asciidoctor/reader.rb', line 350 def terminate @lineno += @lines.size @lines.clear @look_ahead = 0 nil end |
#to_s ⇒ Object
559 560 561 |
# File 'lib/asciidoctor/reader.rb', line 559 def to_s %(#<#{self.class}@#{object_id} {path: #{@path.inspect}, line: #{@lineno}}>) end |
#unshift_line(line_to_restore) ⇒ void Also known as: restore_line
This method returns an undefined value.
Push the String line onto the beginning of the Array of source data.
A line pushed on the reader using this method is not processed again. The method assumes the line was previously retrieved from the reader or does not otherwise contain preprocessor directives. Therefore, it is marked as processed immediately.
228 229 230 231 |
# File 'lib/asciidoctor/reader.rb', line 228 def unshift_line line_to_restore unshift line_to_restore nil end |
#unshift_lines(lines_to_restore) ⇒ void Also known as: restore_lines
This method returns an undefined value.
Push an Array of lines onto the front of the Array of source data.
Lines pushed on the reader using this method are not processed again. The method assumes the lines were previously retrieved from the reader or do not otherwise contain preprocessor directives. Therefore, they are marked as processed immediately.
242 243 244 245 |
# File 'lib/asciidoctor/reader.rb', line 242 def unshift_lines lines_to_restore unshift_all lines_to_restore nil end |