Implement line numbers without the Liquid::Token class.

This commit is contained in:
Dylan Thacker-Smith
2015-07-08 19:21:59 -04:00
parent afda01adbb
commit cebf75b8d7
19 changed files with 186 additions and 194 deletions
+1 -1
View File
@@ -69,7 +69,7 @@ require 'liquid/standardfilters'
require 'liquid/condition'
require 'liquid/utils'
require 'liquid/tokenizer'
require 'liquid/token'
require 'liquid/parse_context'
# Load all the tags of the standard library
#
-12
View File
@@ -23,18 +23,6 @@ module Liquid
@body.nodelist
end
# warnings of this block and all sub-tags
def warnings
all_warnings = []
all_warnings.concat(@warnings) if @warnings
(nodelist || []).each do |node|
all_warnings.concat(node.warnings || []) if node.respond_to?(:warnings)
end
all_warnings
end
def unknown_tag(tag, _params, _tokens)
case tag
when 'else'.freeze
+13 -23
View File
@@ -12,8 +12,9 @@ module Liquid
@blank = true
end
def parse(tokens, options)
while token = tokens.shift
def parse(tokenizer, parse_context)
parse_context.line_number = tokenizer.line_number
while token = tokenizer.shift
begin
unless token.empty?
case
@@ -23,9 +24,7 @@ module Liquid
markup = $2
# fetch the tag from registered blocks
if tag = registered_tags[tag_name]
markup = token.child(markup) if token.is_a?(Token)
new_tag = tag.parse(tag_name, markup, tokens, options)
new_tag.line_number = token.line_number if token.is_a?(Token)
new_tag = tag.parse(tag_name, markup, tokenizer, parse_context)
@blank &&= new_tag.blank?
@nodelist << new_tag
else
@@ -34,12 +33,10 @@ module Liquid
return yield tag_name, markup
end
else
raise_missing_tag_terminator(token, options)
raise_missing_tag_terminator(token, parse_context)
end
when token.start_with?(VARSTART)
new_var = create_variable(token, options)
new_var.line_number = token.line_number if token.is_a?(Token)
@nodelist << new_var
@nodelist << create_variable(token, parse_context)
@blank = false
else
@nodelist << token
@@ -47,9 +44,10 @@ module Liquid
end
end
rescue SyntaxError => e
e.set_line_number_from_token(token)
e.line_number ||= parse_context.line_number
raise
end
parse_context.line_number = tokenizer.line_number
end
yield nil, nil
@@ -59,14 +57,6 @@ module Liquid
@blank
end
def warnings
all_warnings = []
nodelist.each do |node|
all_warnings.concat(node.warnings || []) if node.respond_to?(:warnings)
end
all_warnings
end
def render(context)
output = []
context.resource_limits.render_score += @nodelist.length
@@ -92,7 +82,7 @@ module Liquid
rescue MemoryError => e
raise e
rescue ::StandardError => e
output << context.handle_error(e, token)
output << context.handle_error(e, token.line_number)
end
end
@@ -112,12 +102,12 @@ module Liquid
node_output
end
def create_variable(token, options)
def create_variable(token, parse_context)
token.scan(ContentOfVariable) do |content|
markup = token.is_a?(Token) ? token.child(content.first) : content.first
return Variable.new(markup, options)
markup = content.first
return Variable.new(markup, parse_context)
end
raise_missing_variable_terminator(token, options)
raise_missing_variable_terminator(token, parse_context)
end
def raise_missing_tag_terminator(token, options)
+9 -5
View File
@@ -13,13 +13,14 @@ module Liquid
# context['bob'] #=> nil class Context
class Context
attr_reader :scopes, :errors, :registers, :environments, :resource_limits
attr_accessor :exception_handler, :template_name
attr_accessor :exception_handler, :template_name, :partial
def initialize(environments = {}, outer_scope = {}, registers = {}, rethrow_errors = false, resource_limits = nil)
@environments = [environments].flatten
@scopes = [(outer_scope || {})]
@registers = registers
@errors = []
@partial = false
@resource_limits = resource_limits || ResourceLimits.new(Template.default_resource_limits)
squash_instance_assigns_with_environments
@@ -66,10 +67,10 @@ module Liquid
@interrupts.pop
end
def handle_error(e, token = nil)
def handle_error(e, line_number = nil)
if e.is_a?(Liquid::Error)
e.template_name = template_name
e.set_line_number_from_token(token)
e.template_name ||= template_name
e.line_number ||= line_number
end
output = nil
@@ -79,7 +80,10 @@ module Liquid
case result
when Exception
e = result
e.set_line_number_from_token(token) if e.is_a?(Liquid::Error)
if e.is_a?(Liquid::Error)
e.template_name ||= template_name
e.line_number ||= line_number
end
when String
output = result
else
+1 -5
View File
@@ -1,12 +1,8 @@
module Liquid
class Document < BlockBody
DEFAULT_OPTIONS = {
locale: I18n.new
}
def self.parse(tokens, options)
doc = new
doc.parse(tokens, DEFAULT_OPTIONS.merge(options))
doc.parse(tokens, options)
doc
end
-6
View File
@@ -17,12 +17,6 @@ module Liquid
str
end
def set_line_number_from_token(token)
return unless token.respond_to?(:line_number)
return if line_number
self.line_number = token.line_number
end
def self.render(e)
if e.is_a?(Liquid::Error)
e.to_s
+39
View File
@@ -0,0 +1,39 @@
module Liquid
class ParseContext
attr_accessor :partial, :locale, :line_number
attr_reader :warnings, :error_mode
def initialize(options = {})
@template_options = options ? options.dup : {}
@locale = @template_options[:locale] ||= I18n.new
@warnings = []
self.partial = false
end
def [](option_key)
@options[option_key]
end
def partial=(value)
@partial = value
@options = value ? partial_options : @template_options
@error_mode = @options[:error_mode] || Template.error_mode
value
end
def partial_options
@partial_options ||= begin
dont_pass = @template_options[:include_options_blacklist]
if dont_pass == true
{ locale: locale }
elsif dont_pass.is_a?(Array)
opts = @template_options.dup
dont_pass.each { |o| opts.delete(o) }
opts
else
@template_options
end
end
end
end
end
+3 -4
View File
@@ -1,16 +1,15 @@
module Liquid
module ParserSwitching
def parse_with_selected_parser(markup)
case @options[:error_mode] || Template.error_mode
case @options.error_mode
when :strict then strict_parse_with_error_context(markup)
when :lax then lax_parse(markup)
when :warn
begin
return strict_parse_with_error_context(markup)
rescue SyntaxError => e
e.set_line_number_from_token(markup)
@warnings ||= []
@warnings << e
e.line_number = line_number
@options.warnings << e
return lax_parse(markup)
end
end
+7 -6
View File
@@ -1,23 +1,24 @@
module Liquid
class Tag
attr_accessor :options, :line_number
attr_reader :nodelist, :warnings, :tag_name
attr_accessor :options
attr_reader :nodelist, :tag_name, :line_number
include ParserSwitching
class << self
def parse(tag_name, markup, tokens, options)
def parse(tag_name, markup, tokenizer, options)
tag = new(tag_name, markup, options)
tag.parse(tokens)
tag.parse(tokenizer)
tag
end
private :new
end
def initialize(tag_name, markup, options)
def initialize(tag_name, markup, parse_context)
@tag_name = tag_name
@markup = markup
@options = options
@options = parse_context
@line_number = parse_context.line_number
end
def parse(_tokens)
-1
View File
@@ -15,7 +15,6 @@ module Liquid
if markup =~ Syntax
@to = $1
@from = Variable.new($2, options)
@from.line_number = line_number
else
raise SyntaxError.new options[:locale].t("errors.syntax.assign".freeze)
end
+12 -11
View File
@@ -53,8 +53,10 @@ module Liquid
end
old_template_name = context.template_name
old_partial = context.partial
begin
context.template_name = template_name
context.partial = true
context.stack do
@attributes.each do |key, value|
context[key] = context.evaluate(value)
@@ -72,11 +74,15 @@ module Liquid
end
ensure
context.template_name = old_template_name
context.partial = old_partial
end
end
private
alias_method :parse_context, :options
private :parse_context
def load_cached_partial(template_name, context)
cached_partials = context.registers[:cached_partials] || {}
@@ -84,7 +90,12 @@ module Liquid
return cached
end
source = read_template_from_file_system(context)
partial = Liquid::Template.parse(source, pass_options)
begin
parse_context.partial = true
partial = Liquid::Template.parse(source, parse_context)
ensure
parse_context.partial = false
end
cached_partials[template_name] = partial
context.registers[:cached_partials] = cached_partials
partial
@@ -95,16 +106,6 @@ module Liquid
file_system.read_template_file(context.evaluate(@template_name_expr))
end
def pass_options
dont_pass = @options[:include_options_blacklist]
return { locale: @options[:locale] } if dont_pass == true
opts = @options.merge(included: true, include_options_blacklist: false)
if dont_pass.is_a?(Array)
dont_pass.each { |o| opts.delete(o) }
end
opts
end
end
Template.register_tag('include'.freeze, Include)
+7 -11
View File
@@ -14,7 +14,7 @@ module Liquid
#
class Template
attr_accessor :root
attr_reader :resource_limits
attr_reader :resource_limits, :warnings
@@file_system = BlankFileSystem.new
@@ -116,16 +116,12 @@ module Liquid
@options = options
@profiling = options[:profile]
@line_numbers = options[:line_numbers] || @profiling
@root = Document.parse(tokenize(source), options)
@warnings = nil
parse_context = options.is_a?(ParseContext) ? options : ParseContext.new(options)
@root = Document.parse(tokenize(source), parse_context)
@warnings = parse_context.warnings
self
end
def warnings
return [] unless @root
@warnings ||= @root.warnings
end
def registers
@registers ||= {}
end
@@ -206,7 +202,7 @@ module Liquid
begin
# render the nodelist.
# for performance reasons we get an array back here. join will make a string out of it.
result = with_profiling do
result = with_profiling(context) do
@root.render(context)
end
result.respond_to?(:join) ? result.join : result
@@ -228,8 +224,8 @@ module Liquid
Tokenizer.new(source, @line_numbers)
end
def with_profiling
if @profiling && !@options[:included]
def with_profiling(context)
if @profiling && !context.partial
raise "Profiler not loaded, require 'liquid/profiler' first" unless defined?(Liquid::Profiler)
@profiler = Profiler.new
-18
View File
@@ -1,18 +0,0 @@
module Liquid
class Token < String
attr_reader :line_number
def initialize(content, line_number)
super(content)
@line_number = line_number
end
def raw
"<raw>"
end
def child(string)
Token.new(string, @line_number)
end
end
end
+6 -12
View File
@@ -1,13 +1,17 @@
module Liquid
class Tokenizer
attr_reader :line_number
def initialize(source, line_numbers = false)
@source = source
@line_numbers = line_numbers
@line_number = 1 if line_numbers
@tokens = tokenize
end
def shift
@tokens.shift
token = @tokens.shift
@line_number += token.count("\n") if @line_number && token
token
end
private
@@ -17,21 +21,11 @@ module Liquid
return [] if @source.to_s.empty?
tokens = @source.split(TemplateParser)
tokens = @line_numbers ? calculate_line_numbers(tokens) : tokens
# removes the rogue empty element at the beginning of the array
tokens.shift if tokens[0] && tokens[0].empty?
tokens
end
def calculate_line_numbers(tokens)
current_line = 1
tokens.map do |token|
Token.new(token, current_line).tap do
current_line += token.count("\n")
end
end
end
end
end
+4 -4
View File
@@ -11,14 +11,14 @@ module Liquid
#
class Variable
FilterParser = /(?:\s+|#{QuotedFragment}|#{ArgumentSeparator})+/o
attr_accessor :filters, :name, :warnings
attr_accessor :line_number
attr_accessor :filters, :name, :line_number
include ParserSwitching
def initialize(markup, options = {})
def initialize(markup, parse_context)
@markup = markup
@name = nil
@options = options || {}
@options = parse_context
@line_number = parse_context.line_number
parse_with_selected_parser(markup)
end