liquid 5.4.0 → 5.6.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/History.md +11 -0
- data/README.md +48 -6
- data/lib/liquid/block.rb +8 -4
- data/lib/liquid/block_body.rb +28 -10
- data/lib/liquid/condition.rb +9 -4
- data/lib/liquid/const.rb +8 -0
- data/lib/liquid/context.rb +24 -14
- data/lib/liquid/deprecations.rb +22 -0
- data/lib/liquid/drop.rb +4 -0
- data/lib/liquid/environment.rb +159 -0
- data/lib/liquid/errors.rb +16 -15
- data/lib/liquid/expression.rb +101 -22
- data/lib/liquid/forloop_drop.rb +2 -5
- data/lib/liquid/lexer.rb +155 -44
- data/lib/liquid/locales/en.yml +1 -0
- data/lib/liquid/parse_context.rb +29 -6
- data/lib/liquid/parse_tree_visitor.rb +1 -1
- data/lib/liquid/parser.rb +3 -3
- data/lib/liquid/partial_cache.rb +12 -3
- data/lib/liquid/range_lookup.rb +14 -4
- data/lib/liquid/standardfilters.rb +82 -21
- data/lib/liquid/tablerowloop_drop.rb +1 -1
- data/lib/liquid/tag/disabler.rb +0 -8
- data/lib/liquid/tag.rb +13 -3
- data/lib/liquid/tags/assign.rb +1 -3
- data/lib/liquid/tags/break.rb +1 -3
- data/lib/liquid/tags/capture.rb +0 -2
- data/lib/liquid/tags/case.rb +1 -3
- data/lib/liquid/tags/comment.rb +60 -3
- data/lib/liquid/tags/continue.rb +1 -3
- data/lib/liquid/tags/cycle.rb +14 -4
- data/lib/liquid/tags/decrement.rb +8 -7
- data/lib/liquid/tags/echo.rb +2 -4
- data/lib/liquid/tags/for.rb +6 -8
- data/lib/liquid/tags/if.rb +3 -5
- data/lib/liquid/tags/ifchanged.rb +0 -2
- data/lib/liquid/tags/include.rb +8 -8
- data/lib/liquid/tags/increment.rb +8 -7
- data/lib/liquid/tags/inline_comment.rb +0 -15
- data/lib/liquid/tags/raw.rb +2 -4
- data/lib/liquid/tags/render.rb +14 -12
- data/lib/liquid/tags/table_row.rb +18 -6
- data/lib/liquid/tags/unless.rb +3 -5
- data/lib/liquid/tags.rb +47 -0
- data/lib/liquid/template.rb +60 -57
- data/lib/liquid/tokenizer.rb +127 -11
- data/lib/liquid/variable.rb +14 -8
- data/lib/liquid/variable_lookup.rb +13 -5
- data/lib/liquid/version.rb +1 -1
- data/lib/liquid.rb +15 -16
- metadata +37 -10
- data/lib/liquid/strainer_factory.rb +0 -41
@@ -12,26 +12,27 @@ module Liquid
|
|
12
12
|
# or [section](/themes/architecture/sections) file that they're created in. However, the variable is shared across
|
13
13
|
# [snippets](/themes/architecture#snippets) included in the file.
|
14
14
|
#
|
15
|
-
# Similarly, variables that are created with `increment` are independent from those created with [`assign`](/api/liquid/tags
|
16
|
-
# and [`capture`](/api/liquid/tags
|
15
|
+
# Similarly, variables that are created with `increment` are independent from those created with [`assign`](/docs/api/liquid/tags/assign)
|
16
|
+
# and [`capture`](/docs/api/liquid/tags/capture). However, `increment` and [`decrement`](/docs/api/liquid/tags/decrement) share
|
17
17
|
# variables.
|
18
18
|
# @liquid_syntax
|
19
19
|
# {% increment variable_name %}
|
20
20
|
# @liquid_syntax_keyword variable_name The name of the variable being incremented.
|
21
21
|
class Increment < Tag
|
22
|
+
attr_reader :variable_name
|
23
|
+
|
22
24
|
def initialize(tag_name, markup, options)
|
23
25
|
super
|
24
|
-
@
|
26
|
+
@variable_name = markup.strip
|
25
27
|
end
|
26
28
|
|
27
29
|
def render_to_output_buffer(context, output)
|
28
|
-
|
29
|
-
|
30
|
+
counter_environment = context.environments.first
|
31
|
+
value = counter_environment[@variable_name] || 0
|
32
|
+
counter_environment[@variable_name] = value + 1
|
30
33
|
|
31
34
|
output << value.to_s
|
32
35
|
output
|
33
36
|
end
|
34
37
|
end
|
35
|
-
|
36
|
-
Template.register_tag('increment', Increment)
|
37
38
|
end
|
@@ -1,19 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Liquid
|
4
|
-
# @liquid_public_docs
|
5
|
-
# @liquid_type tag
|
6
|
-
# @liquid_category syntax
|
7
|
-
# @liquid_name inline_comment
|
8
|
-
# @liquid_summary
|
9
|
-
# Prevents an expression from being rendered or output.
|
10
|
-
# @liquid_description
|
11
|
-
# Any text inside an `inline_comment` tag won't be rendered or output.
|
12
|
-
#
|
13
|
-
# You can create multi-line inline comments. However, each line must begin with a `#`.
|
14
|
-
# @liquid_syntax
|
15
|
-
# {% # content %}
|
16
|
-
# @liquid_syntax_keyword content The content of the comment.
|
17
4
|
class InlineComment < Tag
|
18
5
|
def initialize(tag_name, markup, options)
|
19
6
|
super
|
@@ -38,6 +25,4 @@ module Liquid
|
|
38
25
|
true
|
39
26
|
end
|
40
27
|
end
|
41
|
-
|
42
|
-
Template.register_tag('#', InlineComment)
|
43
28
|
end
|
data/lib/liquid/tags/raw.rb
CHANGED
@@ -14,7 +14,6 @@ module Liquid
|
|
14
14
|
# @liquid_syntax_keyword expression The expression to be output without being rendered.
|
15
15
|
class Raw < Block
|
16
16
|
Syntax = /\A\s*\z/
|
17
|
-
FullTokenPossiblyInvalid = /\A(.*)#{TagStart}\s*(\w+)\s*(.*)?#{TagEnd}\z/om
|
18
17
|
|
19
18
|
def initialize(tag_name, markup, parse_context)
|
20
19
|
super
|
@@ -25,7 +24,8 @@ module Liquid
|
|
25
24
|
def parse(tokens)
|
26
25
|
@body = +''
|
27
26
|
while (token = tokens.shift)
|
28
|
-
if token =~ FullTokenPossiblyInvalid && block_delimiter == Regexp.last_match(2)
|
27
|
+
if token =~ BlockBody::FullTokenPossiblyInvalid && block_delimiter == Regexp.last_match(2)
|
28
|
+
parse_context.trim_whitespace = (token[-3] == WhitespaceControl)
|
29
29
|
@body << Regexp.last_match(1) if Regexp.last_match(1) != ""
|
30
30
|
return
|
31
31
|
end
|
@@ -56,6 +56,4 @@ module Liquid
|
|
56
56
|
end
|
57
57
|
end
|
58
58
|
end
|
59
|
-
|
60
|
-
Template.register_tag('raw', Raw)
|
61
59
|
end
|
data/lib/liquid/tags/render.rb
CHANGED
@@ -8,19 +8,19 @@ module Liquid
|
|
8
8
|
# @liquid_summary
|
9
9
|
# Renders a [snippet](/themes/architecture#snippets) or [app block](/themes/architecture/sections/section-schema#render-app-blocks).
|
10
10
|
# @liquid_description
|
11
|
-
# Inside snippets and app blocks, you can't directly access variables that are [created](/api/liquid/tags
|
12
|
-
# of the snippet or app block. However, you can [specify variables as parameters](/api/liquid/tags#render-passing-variables-to-
|
11
|
+
# Inside snippets and app blocks, you can't directly access variables that are [created](/docs/api/liquid/tags/variable-tags) outside
|
12
|
+
# of the snippet or app block. However, you can [specify variables as parameters](/docs/api/liquid/tags/render#render-passing-variables-to-a-snippet)
|
13
13
|
# to pass outside variables to snippets.
|
14
14
|
#
|
15
15
|
# While you can't directly access created variables, you can access global objects, as well as any objects that are
|
16
16
|
# directly accessible outside the snippet or app block. For example, a snippet or app block inside the [product template](/themes/architecture/templates/product)
|
17
|
-
# can access the [`product` object](/api/liquid/objects
|
18
|
-
# can access the [`section` object](/api/liquid/objects
|
17
|
+
# can access the [`product` object](/docs/api/liquid/objects/product), and a snippet or app block inside a [section](/themes/architecture/sections)
|
18
|
+
# can access the [`section` object](/docs/api/liquid/objects/section).
|
19
19
|
#
|
20
20
|
# Outside a snippet or app block, you can't access variables created inside the snippet or app block.
|
21
21
|
#
|
22
22
|
# > Note:
|
23
|
-
# > When you render a snippet using the `render` tag, you can't use the [`include` tag](/api/liquid/tags
|
23
|
+
# > When you render a snippet using the `render` tag, you can't use the [`include` tag](/docs/api/liquid/tags/include)
|
24
24
|
# > inside the snippet.
|
25
25
|
# @liquid_syntax
|
26
26
|
# {% render 'filename' %}
|
@@ -31,7 +31,7 @@ module Liquid
|
|
31
31
|
|
32
32
|
disable_tags "include"
|
33
33
|
|
34
|
-
attr_reader :template_name_expr, :variable_name_expr, :attributes
|
34
|
+
attr_reader :template_name_expr, :variable_name_expr, :attributes, :alias_name
|
35
35
|
|
36
36
|
def initialize(tag_name, markup, options)
|
37
37
|
super
|
@@ -45,7 +45,7 @@ module Liquid
|
|
45
45
|
@alias_name = Regexp.last_match(6)
|
46
46
|
@variable_name_expr = variable_name ? parse_expression(variable_name) : nil
|
47
47
|
@template_name_expr = parse_expression(template_name)
|
48
|
-
@
|
48
|
+
@is_for_loop = (with_or_for == FOR)
|
49
49
|
|
50
50
|
@attributes = {}
|
51
51
|
markup.scan(TagAttributes) do |key, value|
|
@@ -53,6 +53,10 @@ module Liquid
|
|
53
53
|
end
|
54
54
|
end
|
55
55
|
|
56
|
+
def for_loop?
|
57
|
+
@is_for_loop
|
58
|
+
end
|
59
|
+
|
56
60
|
def render_to_output_buffer(context, output)
|
57
61
|
render_tag(context, output)
|
58
62
|
end
|
@@ -65,14 +69,14 @@ module Liquid
|
|
65
69
|
partial = PartialCache.load(
|
66
70
|
template_name,
|
67
71
|
context: context,
|
68
|
-
parse_context: parse_context
|
72
|
+
parse_context: parse_context,
|
69
73
|
)
|
70
74
|
|
71
75
|
context_variable_name = @alias_name || template_name.split('/').last
|
72
76
|
|
73
77
|
render_partial_func = ->(var, forloop) {
|
74
78
|
inner_context = context.new_isolated_subcontext
|
75
|
-
inner_context.template_name =
|
79
|
+
inner_context.template_name = partial.name
|
76
80
|
inner_context.partial = true
|
77
81
|
inner_context['forloop'] = forloop if forloop
|
78
82
|
|
@@ -85,7 +89,7 @@ module Liquid
|
|
85
89
|
}
|
86
90
|
|
87
91
|
variable = @variable_name_expr ? context.evaluate(@variable_name_expr) : nil
|
88
|
-
if @
|
92
|
+
if @is_for_loop && variable.respond_to?(:each) && variable.respond_to?(:count)
|
89
93
|
forloop = Liquid::ForloopDrop.new(template_name, variable.count, nil)
|
90
94
|
variable.each { |var| render_partial_func.call(var, forloop) }
|
91
95
|
else
|
@@ -104,6 +108,4 @@ module Liquid
|
|
104
108
|
end
|
105
109
|
end
|
106
110
|
end
|
107
|
-
|
108
|
-
Template.register_tag('render', Render)
|
109
111
|
end
|
@@ -11,7 +11,7 @@ module Liquid
|
|
11
11
|
# The `tablerow` tag must be wrapped in HTML `<table>` and `</table>` tags.
|
12
12
|
#
|
13
13
|
# > Tip:
|
14
|
-
# > Every `tablerow` loop has an associated [`tablerowloop` object](/api/liquid/objects
|
14
|
+
# > Every `tablerow` loop has an associated [`tablerowloop` object](/docs/api/liquid/objects/tablerowloop) with information about the loop.
|
15
15
|
# @liquid_syntax
|
16
16
|
# {% tablerow variable in array %}
|
17
17
|
# expression
|
@@ -45,13 +45,13 @@ module Liquid
|
|
45
45
|
def render_to_output_buffer(context, output)
|
46
46
|
(collection = context.evaluate(@collection_name)) || (return '')
|
47
47
|
|
48
|
-
from = @attributes.key?('offset') ? context.evaluate(@attributes['offset'])
|
49
|
-
to
|
48
|
+
from = @attributes.key?('offset') ? to_integer(context.evaluate(@attributes['offset'])) : 0
|
49
|
+
to = @attributes.key?('limit') ? from + to_integer(context.evaluate(@attributes['limit'])) : nil
|
50
50
|
|
51
51
|
collection = Utils.slice_collection(collection, from, to)
|
52
52
|
length = collection.length
|
53
53
|
|
54
|
-
cols = context.evaluate(@attributes['cols'])
|
54
|
+
cols = @attributes.key?('cols') ? to_integer(context.evaluate(@attributes['cols'])) : length
|
55
55
|
|
56
56
|
output << "<tr class=\"row1\">\n"
|
57
57
|
context.stack do
|
@@ -65,6 +65,12 @@ module Liquid
|
|
65
65
|
super
|
66
66
|
output << '</td>'
|
67
67
|
|
68
|
+
# Handle any interrupts if they exist.
|
69
|
+
if context.interrupt?
|
70
|
+
interrupt = context.pop_interrupt
|
71
|
+
break if interrupt.is_a?(BreakInterrupt)
|
72
|
+
end
|
73
|
+
|
68
74
|
if tablerowloop.col_last && !tablerowloop.last
|
69
75
|
output << "</tr>\n<tr class=\"row#{tablerowloop.row + 1}\">"
|
70
76
|
end
|
@@ -82,7 +88,13 @@ module Liquid
|
|
82
88
|
super + @node.attributes.values + [@node.collection_name]
|
83
89
|
end
|
84
90
|
end
|
85
|
-
end
|
86
91
|
|
87
|
-
|
92
|
+
private
|
93
|
+
|
94
|
+
def to_integer(value)
|
95
|
+
value.to_i
|
96
|
+
rescue NoMethodError
|
97
|
+
raise Liquid::ArgumentError, "invalid integer"
|
98
|
+
end
|
99
|
+
end
|
88
100
|
end
|
data/lib/liquid/tags/unless.rb
CHANGED
@@ -11,7 +11,7 @@ module Liquid
|
|
11
11
|
# Renders an expression unless a specific condition is `true`.
|
12
12
|
# @liquid_description
|
13
13
|
# > Tip:
|
14
|
-
# > Similar to the [`if` tag](/api/liquid/tags
|
14
|
+
# > Similar to the [`if` tag](/docs/api/liquid/tags/if), you can use `elsif` to add more conditions to an `unless` tag.
|
15
15
|
# @liquid_syntax
|
16
16
|
# {% unless condition %}
|
17
17
|
# expression
|
@@ -23,7 +23,7 @@ module Liquid
|
|
23
23
|
# First condition is interpreted backwards ( if not )
|
24
24
|
first_block = @blocks.first
|
25
25
|
result = Liquid::Utils.to_liquid_value(
|
26
|
-
first_block.evaluate(context)
|
26
|
+
first_block.evaluate(context),
|
27
27
|
)
|
28
28
|
|
29
29
|
unless result
|
@@ -33,7 +33,7 @@ module Liquid
|
|
33
33
|
# After the first condition unless works just like if
|
34
34
|
@blocks[1..-1].each do |block|
|
35
35
|
result = Liquid::Utils.to_liquid_value(
|
36
|
-
block.evaluate(context)
|
36
|
+
block.evaluate(context),
|
37
37
|
)
|
38
38
|
|
39
39
|
if result
|
@@ -44,6 +44,4 @@ module Liquid
|
|
44
44
|
output
|
45
45
|
end
|
46
46
|
end
|
47
|
-
|
48
|
-
Template.register_tag('unless', Unless)
|
49
47
|
end
|
data/lib/liquid/tags.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "tags/table_row"
|
4
|
+
require_relative "tags/echo"
|
5
|
+
require_relative "tags/if"
|
6
|
+
require_relative "tags/break"
|
7
|
+
require_relative "tags/inline_comment"
|
8
|
+
require_relative "tags/for"
|
9
|
+
require_relative "tags/assign"
|
10
|
+
require_relative "tags/ifchanged"
|
11
|
+
require_relative "tags/case"
|
12
|
+
require_relative "tags/include"
|
13
|
+
require_relative "tags/continue"
|
14
|
+
require_relative "tags/capture"
|
15
|
+
require_relative "tags/decrement"
|
16
|
+
require_relative "tags/unless"
|
17
|
+
require_relative "tags/increment"
|
18
|
+
require_relative "tags/comment"
|
19
|
+
require_relative "tags/raw"
|
20
|
+
require_relative "tags/render"
|
21
|
+
require_relative "tags/cycle"
|
22
|
+
|
23
|
+
module Liquid
|
24
|
+
module Tags
|
25
|
+
STANDARD_TAGS = {
|
26
|
+
'cycle' => Cycle,
|
27
|
+
'render' => Render,
|
28
|
+
'raw' => Raw,
|
29
|
+
'comment' => Comment,
|
30
|
+
'increment' => Increment,
|
31
|
+
'unless' => Unless,
|
32
|
+
'decrement' => Decrement,
|
33
|
+
'capture' => Capture,
|
34
|
+
'continue' => Continue,
|
35
|
+
'include' => Include,
|
36
|
+
'case' => Case,
|
37
|
+
'ifchanged' => Ifchanged,
|
38
|
+
'assign' => Assign,
|
39
|
+
'for' => For,
|
40
|
+
'#' => InlineComment,
|
41
|
+
'break' => Break,
|
42
|
+
'if' => If,
|
43
|
+
'echo' => Echo,
|
44
|
+
'tablerow' => TableRow,
|
45
|
+
}.freeze
|
46
|
+
end
|
47
|
+
end
|
data/lib/liquid/template.rb
CHANGED
@@ -15,98 +15,93 @@ module Liquid
|
|
15
15
|
# template.render('user_name' => 'bob')
|
16
16
|
#
|
17
17
|
class Template
|
18
|
-
attr_accessor :root
|
18
|
+
attr_accessor :root, :name
|
19
19
|
attr_reader :resource_limits, :warnings
|
20
20
|
|
21
|
-
|
22
|
-
include Enumerable
|
21
|
+
attr_reader :profiler
|
23
22
|
|
24
|
-
|
25
|
-
|
26
|
-
|
23
|
+
class << self
|
24
|
+
# Sets how strict the parser should be.
|
25
|
+
# :lax acts like liquid 2.5 and silently ignores malformed tags in most cases.
|
26
|
+
# :warn is the default and will give deprecation warnings when invalid syntax is used.
|
27
|
+
# :strict will enforce correct syntax.
|
28
|
+
def error_mode=(mode)
|
29
|
+
Deprecations.warn("Template.error_mode=", "Environment#error_mode=")
|
30
|
+
Environment.default.error_mode = mode
|
27
31
|
end
|
28
32
|
|
29
|
-
def
|
30
|
-
|
31
|
-
return @cache[tag_name] if Liquid.cache_classes
|
32
|
-
|
33
|
-
lookup_class(@tags[tag_name]).tap { |o| @cache[tag_name] = o }
|
33
|
+
def error_mode
|
34
|
+
Environment.default.error_mode
|
34
35
|
end
|
35
36
|
|
36
|
-
def
|
37
|
-
|
38
|
-
|
37
|
+
def default_exception_renderer=(renderer)
|
38
|
+
Deprecations.warn("Template.default_exception_renderer=", "Environment#exception_renderer=")
|
39
|
+
Environment.default.exception_renderer = renderer
|
39
40
|
end
|
40
41
|
|
41
|
-
def
|
42
|
-
|
43
|
-
@cache.delete(tag_name)
|
42
|
+
def default_exception_renderer
|
43
|
+
Environment.default.exception_renderer
|
44
44
|
end
|
45
45
|
|
46
|
-
def
|
47
|
-
|
46
|
+
def file_system=(file_system)
|
47
|
+
Deprecations.warn("Template.file_system=", "Environment#file_system=")
|
48
|
+
Environment.default.file_system = file_system
|
48
49
|
end
|
49
50
|
|
50
|
-
|
51
|
-
|
52
|
-
def lookup_class(name)
|
53
|
-
Object.const_get(name)
|
51
|
+
def file_system
|
52
|
+
Environment.default.file_system
|
54
53
|
end
|
55
|
-
end
|
56
|
-
|
57
|
-
attr_reader :profiler
|
58
54
|
|
59
|
-
|
60
|
-
|
61
|
-
# :lax acts like liquid 2.5 and silently ignores malformed tags in most cases.
|
62
|
-
# :warn is the default and will give deprecation warnings when invalid syntax is used.
|
63
|
-
# :strict will enforce correct syntax.
|
64
|
-
attr_accessor :error_mode
|
65
|
-
Template.error_mode = :lax
|
66
|
-
|
67
|
-
attr_accessor :default_exception_renderer
|
68
|
-
Template.default_exception_renderer = lambda do |exception|
|
69
|
-
exception
|
55
|
+
def tags
|
56
|
+
Environment.default.tags
|
70
57
|
end
|
71
58
|
|
72
|
-
attr_accessor :file_system
|
73
|
-
Template.file_system = BlankFileSystem.new
|
74
|
-
|
75
|
-
attr_accessor :tags
|
76
|
-
Template.tags = TagRegistry.new
|
77
|
-
private :tags=
|
78
|
-
|
79
59
|
def register_tag(name, klass)
|
80
|
-
|
60
|
+
Deprecations.warn("Template.register_tag", "Environment#register_tag")
|
61
|
+
Environment.default.register_tag(name, klass)
|
81
62
|
end
|
82
63
|
|
83
64
|
# Pass a module with filter methods which should be available
|
84
65
|
# to all liquid views. Good for registering the standard library
|
85
66
|
def register_filter(mod)
|
86
|
-
|
67
|
+
Deprecations.warn("Template.register_filter", "Environment#register_filter")
|
68
|
+
Environment.default.register_filter(mod)
|
87
69
|
end
|
88
70
|
|
89
|
-
|
90
|
-
|
91
|
-
|
71
|
+
private def default_resource_limits=(limits)
|
72
|
+
Deprecations.warn("Template.default_resource_limits=", "Environment#default_resource_limits=")
|
73
|
+
Environment.default.default_resource_limits = limits
|
74
|
+
end
|
75
|
+
|
76
|
+
def default_resource_limits
|
77
|
+
Environment.default.default_resource_limits
|
78
|
+
end
|
92
79
|
|
93
80
|
# creates a new <tt>Template</tt> object from liquid source code
|
94
81
|
# To enable profiling, pass in <tt>profile: true</tt> as an option.
|
95
82
|
# See Liquid::Profiler for more information
|
96
83
|
def parse(source, options = {})
|
97
|
-
|
84
|
+
environment = options[:environment] || Environment.default
|
85
|
+
new(environment: environment).parse(source, options)
|
98
86
|
end
|
99
87
|
end
|
100
88
|
|
101
|
-
def initialize
|
89
|
+
def initialize(environment: Environment.default)
|
90
|
+
@environment = environment
|
102
91
|
@rethrow_errors = false
|
103
|
-
@resource_limits = ResourceLimits.new(
|
92
|
+
@resource_limits = ResourceLimits.new(environment.default_resource_limits)
|
104
93
|
end
|
105
94
|
|
106
95
|
# Parse source code.
|
107
96
|
# Returns self for easy chaining
|
108
97
|
def parse(source, options = {})
|
109
98
|
parse_context = configure_options(options)
|
99
|
+
source = source.to_s.to_str
|
100
|
+
|
101
|
+
unless source.valid_encoding?
|
102
|
+
raise TemplateEncodingError, parse_context.locale.t("errors.syntax.invalid_template_encoding")
|
103
|
+
end
|
104
|
+
|
110
105
|
tokenizer = parse_context.new_tokenizer(source, start_line_number: @line_numbers && 1)
|
111
106
|
@root = Document.parse(tokenizer, parse_context)
|
112
107
|
self
|
@@ -156,11 +151,11 @@ module Liquid
|
|
156
151
|
c
|
157
152
|
when Liquid::Drop
|
158
153
|
drop = args.shift
|
159
|
-
drop.context = Context.new([drop, assigns], instance_assigns, registers, @rethrow_errors, @resource_limits)
|
154
|
+
drop.context = Context.new([drop, assigns], instance_assigns, registers, @rethrow_errors, @resource_limits, {}, @environment)
|
160
155
|
when Hash
|
161
|
-
Context.new([args.shift, assigns], instance_assigns, registers, @rethrow_errors, @resource_limits)
|
156
|
+
Context.new([args.shift, assigns], instance_assigns, registers, @rethrow_errors, @resource_limits, {}, @environment)
|
162
157
|
when nil
|
163
|
-
Context.new(assigns, instance_assigns, registers, @rethrow_errors, @resource_limits)
|
158
|
+
Context.new(assigns, instance_assigns, registers, @rethrow_errors, @resource_limits, {}, @environment)
|
164
159
|
else
|
165
160
|
raise ArgumentError, "Expected Hash or Liquid::Context as parameter"
|
166
161
|
end
|
@@ -189,6 +184,8 @@ module Liquid
|
|
189
184
|
@profiler = context.profiler = Liquid::Profiler.new
|
190
185
|
end
|
191
186
|
|
187
|
+
context.template_name ||= name
|
188
|
+
|
192
189
|
begin
|
193
190
|
# render the nodelist.
|
194
191
|
@root.render_to_output_buffer(context, output || +'')
|
@@ -218,8 +215,14 @@ module Liquid
|
|
218
215
|
@options = options
|
219
216
|
@profiling = profiling
|
220
217
|
@line_numbers = options[:line_numbers] || @profiling
|
221
|
-
parse_context = options.is_a?(ParseContext)
|
222
|
-
|
218
|
+
parse_context = if options.is_a?(ParseContext)
|
219
|
+
options
|
220
|
+
else
|
221
|
+
opts = options.key?(:environment) ? options : options.merge(environment: @environment)
|
222
|
+
ParseContext.new(opts)
|
223
|
+
end
|
224
|
+
|
225
|
+
@warnings = parse_context.warnings
|
223
226
|
parse_context
|
224
227
|
end
|
225
228
|
|
data/lib/liquid/tokenizer.rb
CHANGED
@@ -1,18 +1,45 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "strscan"
|
4
|
+
|
3
5
|
module Liquid
|
4
6
|
class Tokenizer
|
5
7
|
attr_reader :line_number, :for_liquid_tag
|
6
8
|
|
7
|
-
|
8
|
-
|
9
|
-
|
9
|
+
TAG_END = /%\}/
|
10
|
+
TAG_OR_VARIABLE_START = /\{[\{\%]/
|
11
|
+
NEWLINE = /\n/
|
12
|
+
|
13
|
+
OPEN_CURLEY = "{".ord
|
14
|
+
CLOSE_CURLEY = "}".ord
|
15
|
+
PERCENTAGE = "%".ord
|
16
|
+
|
17
|
+
def initialize(
|
18
|
+
source:,
|
19
|
+
string_scanner:,
|
20
|
+
line_numbers: false,
|
21
|
+
line_number: nil,
|
22
|
+
for_liquid_tag: false
|
23
|
+
)
|
24
|
+
@line_number = line_number || (line_numbers ? 1 : nil)
|
10
25
|
@for_liquid_tag = for_liquid_tag
|
11
|
-
@
|
26
|
+
@source = source.to_s.to_str
|
27
|
+
@offset = 0
|
28
|
+
@tokens = []
|
29
|
+
|
30
|
+
if @source
|
31
|
+
@ss = string_scanner
|
32
|
+
@ss.string = @source
|
33
|
+
tokenize
|
34
|
+
end
|
12
35
|
end
|
13
36
|
|
14
37
|
def shift
|
15
|
-
|
38
|
+
token = @tokens[@offset]
|
39
|
+
|
40
|
+
return unless token
|
41
|
+
|
42
|
+
@offset += 1
|
16
43
|
|
17
44
|
if @line_number
|
18
45
|
@line_number += @for_liquid_tag ? 1 : token.count("\n")
|
@@ -24,16 +51,105 @@ module Liquid
|
|
24
51
|
private
|
25
52
|
|
26
53
|
def tokenize
|
27
|
-
|
54
|
+
if @for_liquid_tag
|
55
|
+
@tokens = @source.split("\n")
|
56
|
+
else
|
57
|
+
@tokens << shift_normal until @ss.eos?
|
58
|
+
end
|
59
|
+
|
60
|
+
@source = nil
|
61
|
+
@ss = nil
|
62
|
+
end
|
28
63
|
|
29
|
-
|
64
|
+
def shift_normal
|
65
|
+
token = next_token
|
30
66
|
|
31
|
-
|
67
|
+
return unless token
|
32
68
|
|
33
|
-
|
34
|
-
|
69
|
+
token
|
70
|
+
end
|
71
|
+
|
72
|
+
def next_token
|
73
|
+
# possible states: :text, :tag, :variable
|
74
|
+
byte_a = @ss.peek_byte
|
75
|
+
|
76
|
+
if byte_a == OPEN_CURLEY
|
77
|
+
@ss.scan_byte
|
78
|
+
|
79
|
+
byte_b = @ss.peek_byte
|
80
|
+
|
81
|
+
if byte_b == PERCENTAGE
|
82
|
+
@ss.scan_byte
|
83
|
+
return next_tag_token
|
84
|
+
elsif byte_b == OPEN_CURLEY
|
85
|
+
@ss.scan_byte
|
86
|
+
return next_variable_token
|
87
|
+
end
|
88
|
+
|
89
|
+
@ss.pos -= 1
|
90
|
+
end
|
91
|
+
|
92
|
+
next_text_token
|
93
|
+
end
|
94
|
+
|
95
|
+
def next_text_token
|
96
|
+
start = @ss.pos
|
97
|
+
|
98
|
+
unless @ss.skip_until(TAG_OR_VARIABLE_START)
|
99
|
+
token = @ss.rest
|
100
|
+
@ss.terminate
|
101
|
+
return token
|
102
|
+
end
|
103
|
+
|
104
|
+
pos = @ss.pos -= 2
|
105
|
+
@source.byteslice(start, pos - start)
|
106
|
+
end
|
107
|
+
|
108
|
+
def next_variable_token
|
109
|
+
start = @ss.pos - 2
|
110
|
+
|
111
|
+
byte_a = byte_b = @ss.scan_byte
|
112
|
+
|
113
|
+
while byte_b
|
114
|
+
byte_a = @ss.scan_byte while byte_a && (byte_a != CLOSE_CURLEY && byte_a != OPEN_CURLEY)
|
115
|
+
|
116
|
+
break unless byte_a
|
117
|
+
|
118
|
+
if @ss.eos?
|
119
|
+
return byte_a == CLOSE_CURLEY ? @source.byteslice(start, @ss.pos - start) : "{{"
|
120
|
+
end
|
121
|
+
|
122
|
+
byte_b = @ss.scan_byte
|
123
|
+
|
124
|
+
if byte_a == CLOSE_CURLEY
|
125
|
+
if byte_b == CLOSE_CURLEY
|
126
|
+
return @source.byteslice(start, @ss.pos - start)
|
127
|
+
elsif byte_b != CLOSE_CURLEY
|
128
|
+
@ss.pos -= 1
|
129
|
+
return @source.byteslice(start, @ss.pos - start)
|
130
|
+
end
|
131
|
+
elsif byte_a == OPEN_CURLEY && byte_b == PERCENTAGE
|
132
|
+
return next_tag_token_with_start(start)
|
133
|
+
end
|
134
|
+
|
135
|
+
byte_a = byte_b
|
136
|
+
end
|
137
|
+
|
138
|
+
"{{"
|
139
|
+
end
|
140
|
+
|
141
|
+
def next_tag_token
|
142
|
+
start = @ss.pos - 2
|
143
|
+
if (len = @ss.skip_until(TAG_END))
|
144
|
+
@source.byteslice(start, len + 2)
|
145
|
+
else
|
146
|
+
"{%"
|
147
|
+
end
|
148
|
+
end
|
35
149
|
|
36
|
-
|
150
|
+
def next_tag_token_with_start(start)
|
151
|
+
@ss.skip_until(TAG_END)
|
152
|
+
@source.byteslice(start, @ss.pos - start)
|
37
153
|
end
|
38
154
|
end
|
39
155
|
end
|