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
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
|
# frozen_string_literal: true
module Jekyll
class Excerpt
extend Forwardable
attr_accessor :content, :doc, :ext
attr_writer :output
def_delegators :@doc,
:site, :name, :ext, :extname,
:collection, :related_posts, :type,
:coffeescript_file?, :yaml_file?,
:url, :next_doc, :previous_doc
private :coffeescript_file?, :yaml_file?
# Initialize this Excerpt instance.
#
# doc - The Document.
#
# Returns the new Excerpt.
def initialize(doc)
self.doc = doc
self.content = extract_excerpt(doc.content)
end
# Fetch YAML front-matter data from related doc, without layout key
#
# Returns Hash of doc data
def data
@data ||= doc.data.dup
@data.delete("layout")
@data.delete("excerpt")
@data
end
def trigger_hooks(*); end
# 'Path' of the excerpt.
#
# Returns the path for the doc this excerpt belongs to with #excerpt appended
def path
File.join(doc.path, "#excerpt")
end
# 'Relative Path' of the excerpt.
#
# Returns the relative_path for the doc this excerpt belongs to with #excerpt appended
def relative_path
@relative_path ||= File.join(doc.relative_path, "#excerpt")
end
# Check if excerpt includes a string
#
# Returns true if the string passed in
def include?(something)
output&.include?(something) || content.include?(something)
end
# The UID for this doc (useful in feeds).
# e.g. /2008/11/05/my-awesome-doc
#
# Returns the String UID.
def id
"#{doc.id}#excerpt"
end
def to_s
output || content
end
def to_liquid
Jekyll::Drops::ExcerptDrop.new(self)
end
# Returns the shorthand String identifier of this doc.
def inspect
"<#{self.class} id=#{id}>"
end
def output
@output ||= Renderer.new(doc.site, self, site.site_payload).run
end
def place_in_layout?
false
end
def render_with_liquid?
return false if data["render_with_liquid"] == false
!(coffeescript_file? || yaml_file? || !Utils.has_liquid_construct?(content))
end
protected
# Internal: Extract excerpt from the content
#
# By default excerpt is your first paragraph of a doc: everything before
# the first two new lines:
#
# ---
# title: Example
# ---
#
# First paragraph with [link][1].
#
# Second paragraph.
#
# [1]: http://example.com/
#
# This is fairly good option for Markdown and Textile files. But might cause
# problems for HTML docs (which is quite unusual for Jekyll). If default
# excerpt delimiter is not good for you, you might want to set your own via
# configuration option `excerpt_separator`. For example, following is a good
# alternative for HTML docs:
#
# # file: _config.yml
# excerpt_separator: "<!-- more -->"
#
# Notice that all markdown-style link references will be appended to the
# excerpt. So the example doc above will have this excerpt source:
#
# First paragraph with [link][1].
#
# [1]: http://example.com/
#
# Excerpts are rendered same time as content is rendered.
#
# Returns excerpt String
LIQUID_TAG_REGEX = %r!{%-?\s*(\w+)\s*.*?-?%}!m.freeze
MKDWN_LINK_REF_REGEX = %r!^ {0,3}(?:(\[[^\]]+\])(:.+))$!.freeze
def extract_excerpt(doc_content)
head, _, tail = doc_content.to_s.partition(doc.excerpt_separator)
return head if tail.empty?
head = sanctify_liquid_tags(head) if head.include?("{%")
definitions = extract_markdown_link_reference_definitions(head, tail)
return head if definitions.empty?
head << "\n\n" << definitions.join("\n")
end
private
# append appropriate closing tag(s) (for each Liquid block), to the `head` if the
# partitioning resulted in leaving the closing tag somewhere in the `tail` partition.
def sanctify_liquid_tags(head)
modified = false
tag_names = head.scan(LIQUID_TAG_REGEX)
tag_names.flatten!
tag_names.reverse_each do |tag_name|
next unless liquid_block?(tag_name)
next if endtag_regex_stash(tag_name).match?(head)
modified = true
head << "\n{% end#{tag_name} %}"
end
print_build_warning if modified
head
end
def extract_markdown_link_reference_definitions(head, tail)
[].tap do |definitions|
tail.scan(MKDWN_LINK_REF_REGEX).each do |segments|
definitions << segments.join if head.include?(segments[0])
end
end
end
def endtag_regex_stash(tag_name)
@endtag_regex_stash ||= {}
@endtag_regex_stash[tag_name] ||= %r!{%-?\s*end#{tag_name}.*?\s*-?%}!m
end
def liquid_block?(tag_name)
return false unless tag_name.is_a?(String)
return false unless Liquid::Template.tags[tag_name]
Liquid::Template.tags[tag_name].ancestors.include?(Liquid::Block)
rescue NoMethodError
Jekyll.logger.error "Error:",
"A Liquid tag in the excerpt of #{doc.relative_path} couldn't be parsed."
raise
end
def print_build_warning
Jekyll.logger.warn "Warning:", "Excerpt modified in #{doc.relative_path}!"
Jekyll.logger.warn "", "Found a Liquid block containing the excerpt separator " \
"#{doc.excerpt_separator.inspect}."
Jekyll.logger.warn "", "The block has been modified with the appropriate closing tag."
Jekyll.logger.warn "", "Feel free to define a custom excerpt or excerpt_separator in the"
Jekyll.logger.warn "", "document's Front Matter if the generated excerpt is unsatisfactory."
end
end
end
|