File: integrator.rb

package info (click to toggle)
ruby-jekyll-asciidoc 3.0.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 1,536 kB
  • sloc: ruby: 1,802; sh: 36; makefile: 6
file content (155 lines) | stat: -rw-r--r-- 6,787 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
module Jekyll
  module AsciiDoc
    # Promotes eligible AsciiDoc attributes to page variables and applies page-level settings to all documents handled
    # by the converter included with this plugin. It also copies the custom Pygments stylesheet if Pygments is the
    # source highlighter and configured to use class-based styling.
    class Integrator < ::Jekyll::Generator
      NewLine = Utils::NewLine
      PygmentsRootSelector = /^(.+?)\.pygments +{/

      # Enable plugin when running in safe mode; jekyll-asciidoc gem must also be declared in whitelist
      safe true

      def self.get_instance site
        site.find_generator_instance self
      end

      # This method is triggered each time the site is generated, including after any file has changed when running in
      # watch mode (regardless of incremental setting).
      #
      # @param site [Jekyll::Site] the site being processed.
      #
      # @return [nil] Nothing
      def generate site
        @converter = converter = Converter.get_instance site

        site.pages.select! do |page|
          (converter.matches page.ext) ? (integrate page) : true
        end

        site.collections.each do |name, collection|
          collection.docs.select! do |doc|
            (converter.matches doc.extname) ? (integrate doc, name) : true
          end
        end

        if site.config['asciidoc']['processor'] == 'asciidoctor'
          attrs = site.config['asciidoctor'][:attributes]
          attrs['localdate'], attrs['localtime'] = (site.time.strftime '%Y-%m-%d %H:%M:%S %Z').split ' ', 2
          if ((attrs['source-highlighter'] || '').chomp '@') == 'pygments' &&
              ((attrs['pygments-css'] || '').chomp '@') != 'style' && (attrs.fetch 'pygments-stylesheet', '')
            generate_pygments_stylesheet site, attrs
          end
        end

        nil
      end

      # Integrate the page-related attributes from the AsciiDoc document header into the data Array of the specified
      # {::Jekyll::Page}, {::Jekyll::Post} or {::Jekyll::Document}.
      #
      # @param document [::Jekyll::Page, ::Jekyll::Post, ::Jekyll::Document] the page, post, or document to integrate.
      # @param collection_name [String] the name of the collection to which this document belongs.
      #
      # @return [Boolean] whether the document should be published.
      def integrate document, collection_name = nil
        return true unless (doc = @converter.load_header document)

        data = document.data
        data['asciidoc'] = true
        # NOTE id is already reserved in Jekyll for another purpose, so we'll map id to docid instead
        data['docid'] = doc.id if doc.id
        data['title'] = doc.doctitle if doc.header?
        data['author'] = doc.author if doc.author
        if collection_name && (doc.attr? 'revdate')
          data['date'] = ::Jekyll::Utils.parse_date doc.revdate,
              %(Document '#{document.relative_path}' does not have a valid revdate in the AsciiDoc header.)
        end

        page_attr_prefix = document.site.config['asciidoc']['page_attribute_prefix']
        no_prefix = (prefix_size = page_attr_prefix.length) == 0
        adoc_data = doc.attributes.each_with_object({}) do |(key, val), accum|
          if no_prefix || ((key.start_with? page_attr_prefix) && (key = key[prefix_size..-1]))
            accum[key] = ::String === val ? (parse_yaml_value val) : val
          end
        end
        data.update adoc_data unless adoc_data.empty?

        { 'category' => 'categories', 'tag' => 'tags' }.each do |sole_key, coll_key|
          if (sole_val = data[sole_key])
            (coll_val = data[coll_key] ||= []).delete sole_val
            coll_val.unshift sole_val
          elsif (coll_val = data[coll_key])
            data[sole_key] = coll_val[0]
          end
        end

        # NOTE excerpt must be set before layout is assigned since excerpt cannot have a layout (or be standalone)
        unless ::Jekyll::Page === document
          data['excerpt'] = Excerpt.new document, ((excerpt = data['excerpt']) || doc.source)
          data['excerpt_origin'] = excerpt ? ((adoc_data.key? 'excerpt') ? 'asciidoc-header' : 'front-matter') : 'body'
        end

        case data['layout']
        when nil
          data['standalone'] = true unless data.key? 'layout'
        when '', '_auto'
          layout = collection_name ? (collection_name.chomp 's') : 'page'
          data['layout'] = (document.site.layouts.key? layout) ? layout : 'default'
        when false
          data['layout'] = 'none'
          data['standalone'] = true
        end

        document.extend Document
        document.extend NoLiquid unless data['liquid']
        data.fetch 'published', true
      end

      def generate_pygments_stylesheet site, attrs
        css_base = site.source
        unless (css_dir = (attrs['stylesdir'] || '').chomp '@').empty? || (css_dir.start_with? '/')
          css_dir = %(/#{css_dir})
        end
        css_name = attrs['pygments-stylesheet'] || 'asciidoc-pygments.css'
        css_file = ::File.join css_base, css_dir, css_name
        css_style = (attrs['pygments-style'] || 'vs').chomp '@'
        css = ::Asciidoctor::Stylesheets.instance.pygments_stylesheet_data css_style
        # NOTE apply stronger CSS rule for general text color
        css = css.sub PygmentsRootSelector, '\1.pygments, \1.pygments code {'
        if site.static_files.any? {|f| f.path == css_file }
          ::File.write css_file, css unless css == (::File.read css_file)
        else
          ::FileUtils.mkdir_p ::File.dirname css_file
          ::File.write css_file, css
          site.static_files << (::Jekyll::StaticFile.new site, css_base, css_dir, css_name)
        end
      end

      private

      # Parse the specified value as though it is a single-line value part of a YAML key/value pair.
      #
      # Attempt to parse the specified String value as though it is a single-line value part of a YAML key/value pair.
      # If the value fails to parse, wrap the value in single quotes (after escaping any single quotes in the value) and
      # parse it as a character sequence. If the value is empty, return an empty String.
      #
      # @param val [String] the value to parse.
      #
      # @return [Object, String] the value parsed from the string-based YAML value or an empty String if the specified
      # value is empty.
      def parse_yaml_value val
        if val.empty?
          ''
        else
          begin
            Jekyll::Utils.safe_load_yaml %(--- #{val})
          rescue ::StandardError, ::SyntaxError
            val = val.gsub '\'', '\'\'' if val.include? '\''
            Jekyll::Utils.safe_load_yaml %(--- \'#{val}\')
          end
        end
      end
    end
  end
end