diff options
| author | Dan Allen <dan.j.allen@gmail.com> | 2014-07-16 03:24:14 -0600 |
|---|---|---|
| committer | Dan Allen <dan.j.allen@gmail.com> | 2014-07-16 03:24:14 -0600 |
| commit | 66f2b14a08061784eddf699d4e92f8ea9e2a5ef3 (patch) | |
| tree | 5d8e77ff2e8e7435c843fa152e444e1b6ea95733 | |
| parent | 8bb3670694da11122e25adc2f384aa508b865266 (diff) | |
| parent | a3a6538b70cc803d35f68ff3c724e06eaf102c8b (diff) | |
Merge pull request #865 from mojavelinux/issue-861
resolves #861 add source map information to blocks
| -rw-r--r-- | lib/asciidoctor/abstract_block.rb | 16 | ||||
| -rw-r--r-- | lib/asciidoctor/abstract_node.rb | 5 | ||||
| -rw-r--r-- | lib/asciidoctor/block.rb | 8 | ||||
| -rw-r--r-- | lib/asciidoctor/document.rb | 40 | ||||
| -rw-r--r-- | lib/asciidoctor/parser.rb | 8 | ||||
| -rw-r--r-- | lib/asciidoctor/reader.rb | 36 | ||||
| -rw-r--r-- | lib/asciidoctor/section.rb | 5 | ||||
| -rw-r--r-- | test/document_test.rb | 30 |
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 |
