summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Allen <dan.j.allen@gmail.com>2014-07-16 03:24:14 -0600
committerDan Allen <dan.j.allen@gmail.com>2014-07-16 03:24:14 -0600
commit66f2b14a08061784eddf699d4e92f8ea9e2a5ef3 (patch)
tree5d8e77ff2e8e7435c843fa152e444e1b6ea95733
parent8bb3670694da11122e25adc2f384aa508b865266 (diff)
parenta3a6538b70cc803d35f68ff3c724e06eaf102c8b (diff)
Merge pull request #865 from mojavelinux/issue-861
resolves #861 add source map information to blocks
-rw-r--r--lib/asciidoctor/abstract_block.rb16
-rw-r--r--lib/asciidoctor/abstract_node.rb5
-rw-r--r--lib/asciidoctor/block.rb8
-rw-r--r--lib/asciidoctor/document.rb40
-rw-r--r--lib/asciidoctor/parser.rb8
-rw-r--r--lib/asciidoctor/reader.rb36
-rw-r--r--lib/asciidoctor/section.rb5
-rw-r--r--test/document_test.rb30
8 files changed, 99 insertions, 49 deletions
diff --git a/lib/asciidoctor/abstract_block.rb b/lib/asciidoctor/abstract_block.rb
index 3ecb1c8e..7e8637d6 100644
--- a/lib/asciidoctor/abstract_block.rb
+++ b/lib/asciidoctor/abstract_block.rb
@@ -21,7 +21,10 @@ class AbstractBlock < AbstractNode
# Public: Get/Set the caption for this block
attr_accessor :caption
- def initialize(parent, context)
+ # Public: Gets/Sets the location in the AsciiDoc source where this block begins
+ attr_accessor :source_location
+
+ def initialize(parent, context, opts = {})
super
@content_model = :compound
@subs = []
@@ -38,6 +41,7 @@ class AbstractBlock < AbstractNode
end
@next_section_index = 0
@next_section_number = 1
+ @source_location = nil
end
def block?
@@ -181,6 +185,16 @@ class AbstractBlock < AbstractNode
@blocks.select {|block| block.context == :section }
end
+ # Public: Get the source file where this block started
+ def file
+ @source_location ? @source_location.file : nil
+ end
+
+ # Public: Get the source line number where this block started
+ def lineno
+ @source_location ? @source_location.lineno : nil
+ end
+
# Public: Remove a substitution from this block
#
# sub - The Symbol substitution name
diff --git a/lib/asciidoctor/abstract_node.rb b/lib/asciidoctor/abstract_node.rb
index dc2282d1..bee857bc 100644
--- a/lib/asciidoctor/abstract_node.rb
+++ b/lib/asciidoctor/abstract_node.rb
@@ -24,7 +24,7 @@ class AbstractNode
# Public: Get the Hash of attributes for this node
attr_reader :attributes
- def initialize(parent, context)
+ def initialize(parent, context, opts = {})
# document is a special case, should refer to itself
if context == :document
@parent = nil
@@ -38,7 +38,8 @@ class AbstractNode
end
@context = context
@node_name = context.to_s
- @attributes = {}
+ # QUESTION are we correct in duplicating the attributes (seems to be just as fast)
+ @attributes = opts.key?(:attributes) ? (opts[:attributes] || {}).dup : {}
@passthroughs = []
end
diff --git a/lib/asciidoctor/block.rb b/lib/asciidoctor/block.rb
index 8bbcdbe8..1ee57b4d 100644
--- a/lib/asciidoctor/block.rb
+++ b/lib/asciidoctor/block.rb
@@ -40,14 +40,8 @@ class Block < AbstractBlock
#--
# QUESTION should we store source_data as lines for blocks that have compound content models?
def initialize(parent, context, opts = {})
- super(parent, context)
+ super
@content_model = opts[:content_model] || DEFAULT_CONTENT_MODEL[context]
- if (attrs = opts[:attributes]).nil_or_empty?
- @attributes = {}
- else
- # QUESTION are we correct in duplicating the attributes (seems to be just as fast)
- @attributes = attrs.dup
- end
if opts.has_key? :subs
# FIXME this is a bit funky
# we have to be defensive to avoid lock_in_subs wiping out the override
diff --git a/lib/asciidoctor/document.rb b/lib/asciidoctor/document.rb
index 91a7fa33..5421990b 100644
--- a/lib/asciidoctor/document.rb
+++ b/lib/asciidoctor/document.rb
@@ -77,6 +77,9 @@ class Document < AbstractBlock
#
attr_reader :compat_mode
+ # Public: Get the Boolean flag that indicates whether source map information is tracked by the parser
+ attr_reader :sourcemap
+
# Public: Get the Hash of document references
attr_reader :references
@@ -86,23 +89,25 @@ class Document < AbstractBlock
# Public: Get the Hash of callouts
attr_reader :callouts
- # Public: The section level 0 block
+ # Public: Get the level-0 Section
attr_reader :header
- # Public: Base directory for converting this document. Defaults to directory of the source file.
+ # Public: Get the String base directory for converting this document.
+ #
+ # Defaults to directory of the source file.
# If the source is a string, defaults to the current directory.
attr_reader :base_dir
- # Public: A reference to the parent document of this nested document.
+ # Public: Get a reference to the parent Document of this nested document.
attr_reader :parent_document
- # Public: The Reader associated with this document
+ # Public: Get the Reader associated with this document
attr_reader :reader
- # Public: The Converter associated with this document
+ # Public: Get the Converter associated with this document
attr_reader :converter
- # Public: The extensions registry
+ # Public: Get the extensions registry
attr_reader :extensions
# Public: Initialize a {Document} object.
@@ -138,6 +143,7 @@ class Document < AbstractBlock
@attribute_overrides = attr_overrides
@safe = parent_doc.safe
@compat_mode = parent_doc.compat_mode
+ @sourcemap = parent_doc.sourcemap
@converter = parent_doc.converter
initialize_extensions = false
@extensions = parent_doc.extensions
@@ -167,7 +173,16 @@ class Document < AbstractBlock
attr_overrides[key.downcase] = value
end
@attribute_overrides = attr_overrides
- @safe = nil
+ # safely resolve the safe mode from const, int or string
+ @safe = if !(safe_mode = options[:safe])
+ SafeMode::SECURE
+ elsif safe_mode.is_a? ::Fixnum
+ # be permissive in case API user wants to define new levels
+ safe_mode
+ else
+ SafeMode.const_get(safe_mode.to_s.upcase).to_i rescue SafeMode::SECURE.to_i
+ end
+ @sourcemap = options.key?(:sourcemap) && options[:sourcemap] == true
@converter = nil
initialize_extensions = defined? ::Asciidoctor::Extensions
@extensions = nil # initialize furthur down
@@ -179,17 +194,6 @@ class Document < AbstractBlock
@callouts = Callouts.new
@attributes_modified = ::Set.new
@options = options
- unless parent_doc
- # safely resolve the safe mode from const, int or string
- if !(safe_mode = options[:safe])
- @safe = SafeMode::SECURE
- elsif safe_mode.is_a? ::Fixnum
- # be permissive in case API user wants to define new levels
- @safe = safe_mode
- else
- @safe = SafeMode.const_get(safe_mode.to_s.upcase).to_i rescue SafeMode::SECURE.to_i
- end
- end
header_footer = (options[:header_footer] ||= false)
attrs = @attributes
diff --git a/lib/asciidoctor/parser.rb b/lib/asciidoctor/parser.rb
index 35404eab..d2669376 100644
--- a/lib/asciidoctor/parser.rb
+++ b/lib/asciidoctor/parser.rb
@@ -105,11 +105,13 @@ class Parser
# check if the first line is the document title
# if so, add a header to the document and parse the header metadata
if is_next_line_document_title?(reader, block_attributes)
+ source_location = reader.cursor if document.sourcemap
document.id, _, doctitle, _, _ = parse_section_title(reader, document)
unless assigned_doctitle
document.title = doctitle
assigned_doctitle = doctitle
end
+ document.header.source_location = source_location if source_location
document.attributes['doctitle'] = section_title = doctitle
# QUESTION: should the id assignment on Document be encapsulated in the Document class?
unless document.id
@@ -426,6 +428,8 @@ class Parser
block = nil
style = nil
explicit_style = nil
+ sourcemap = document.sourcemap
+ source_location = nil
while !block && reader.has_more_lines?
# if parsing metadata, read until there is no more to read
@@ -438,6 +442,7 @@ class Parser
end
# QUESTION should we introduce a parsing context object?
+ source_location = reader.cursor if sourcemap
this_line = reader.read_line
delimited_block = false
block_context = nil
@@ -894,6 +899,7 @@ class Parser
# REVIEW we may no longer need this nil check
# FIXME we've got to clean this up, it's horrible!
if block
+ block.source_location = source_location if source_location
# REVIEW seems like there is a better way to organize this wrap-up
block.title = attributes['title'] unless block.title?
# FIXME HACK don't hardcode logic for alt, caption and scaledwidth on images down here
@@ -1547,9 +1553,11 @@ class Parser
# attributes - a Hash of attributes to assign to this section (default: {})
def self.initialize_section(reader, parent, attributes = {})
document = parent.document
+ source_location = reader.cursor if document.sourcemap
sect_id, sect_reftext, sect_title, sect_level, _ = parse_section_title(reader, document)
attributes['reftext'] = sect_reftext if sect_reftext
section = Section.new parent, sect_level, document.attributes.has_key?('numbered')
+ section.source_location = source_location if source_location
section.id = sect_id
section.title = sect_title
# parse style, id and role from first positional attribute
diff --git a/lib/asciidoctor/reader.rb b/lib/asciidoctor/reader.rb
index ab94979f..0075ab32 100644
--- a/lib/asciidoctor/reader.rb
+++ b/lib/asciidoctor/reader.rb
@@ -1029,26 +1029,26 @@ class PreprocessorReader < Reader
end
# effectively fill the buffer
- @lines = prepare_lines data, :normalize => true, :condense => false, :indent => attributes['indent']
-
- # FIXME we eventually want to handle leveloffset without affecting the lines
- if attributes.has_key? 'leveloffset'
- @lines.unshift ''
- @lines.unshift %(:leveloffset: #{attributes['leveloffset']})
- @lines.push ''
- if (old_leveloffset = @document.attr 'leveloffset')
- @lines.push %(:leveloffset: #{old_leveloffset})
- else
- @lines.push ':leveloffset!:'
- end
- end
-
- # FIXME kind of a hack
- #Document::AttributeEntry.new('infile', @file).save_to_next_block @document
- #Document::AttributeEntry.new('indir', @dir).save_to_next_block @document
- if @lines.empty?
+ if (@lines = prepare_lines data, :normalize => true, :condense => false, :indent => attributes['indent']).empty?
pop_include
else
+ # FIXME we eventually want to handle leveloffset without affecting the lines
+ if attributes.has_key? 'leveloffset'
+ @lines.unshift ''
+ @lines.unshift %(:leveloffset: #{attributes['leveloffset']})
+ @lines.push ''
+ if (old_leveloffset = @document.attr 'leveloffset')
+ @lines.push %(:leveloffset: #{old_leveloffset})
+ else
+ @lines.push ':leveloffset!:'
+ end
+ # compensate for these extra lines
+ @lineno -= 2
+ end
+
+ # FIXME kind of a hack
+ #Document::AttributeEntry.new('infile', @file).save_to_next_block @document
+ #Document::AttributeEntry.new('indir', @dir).save_to_next_block @document
@eof = false
@look_ahead = 0
end
diff --git a/lib/asciidoctor/section.rb b/lib/asciidoctor/section.rb
index efa840f3..633da77e 100644
--- a/lib/asciidoctor/section.rb
+++ b/lib/asciidoctor/section.rb
@@ -39,8 +39,8 @@ class Section < AbstractBlock
# Public: Initialize an Asciidoctor::Section object.
#
# parent - The parent Asciidoc Object.
- def initialize(parent = nil, level = nil, numbered = true)
- super(parent, :section)
+ def initialize(parent = nil, level = nil, numbered = true, opts = {})
+ super(parent, :section, opts)
if level.nil?
if parent
@level = parent.level + 1
@@ -50,7 +50,6 @@ class Section < AbstractBlock
else
@level = level
end
- #@numbered = numbered && @level > 0 && @level < 4
@numbered = numbered && @level > 0
@special = parent && parent.context == :section && parent.special
@index = 0
diff --git a/test/document_test.rb b/test/document_test.rb
index 092389c0..a03e771f 100644
--- a/test/document_test.rb
+++ b/test/document_test.rb
@@ -253,6 +253,36 @@ preamble
flunk %(attributes argument should not be modified)
end
end
+
+ test 'should track file and line information with blocks if sourcemap option is set' do
+ doc = Asciidoctor.load_file fixture_path('sample.asciidoc'), :sourcemap => true
+
+ section_1 = doc.sections[0]
+ assert_equal 'Section A', section_1.title
+ refute_nil section_1.source_location
+ assert_equal 'sample.asciidoc', section_1.file
+ assert_equal 10, section_1.lineno
+
+ section_2 = doc.sections[1]
+ assert_equal 'Section B', section_2.title
+ refute_nil section_2.source_location
+ assert_equal 'sample.asciidoc', section_2.file
+ assert_equal 18, section_2.lineno
+
+ last_block = section_2.blocks[-1]
+ assert_equal :ulist, last_block.context
+ refute_nil last_block.source_location
+ assert_equal 'sample.asciidoc', last_block.file
+ assert_equal 23, last_block.lineno
+
+ doc = Asciidoctor.load_file fixture_path('master.adoc'), :sourcemap => true, :safe => :safe
+
+ section_1 = doc.sections[0]
+ assert_equal 'Chapter A', section_1.title
+ refute_nil section_1.source_location
+ assert_equal fixture_path('chapter-a.adoc'), section_1.file
+ assert_equal 1, section_1.lineno
+ end
end
context 'Convert APIs' do