# frozen_string_literal: truerequire"strscan"require"active_support/core_ext/erb/util"moduleActionViewclassTemplatemoduleHandlersclassERBautoload:Erubi,"action_view/template/handlers/erb/erubi"# Specify trim mode for the ERB compiler. Defaults to '-'.# See ERB documentation for suitable values.class_attribute:erb_trim_mode,default: "-"# Default implementation used.class_attribute:erb_implementation,default: Erubi# Do not escape templates of these mime types.class_attribute:escape_ignore_list,default: ["text/plain"]# Strip trailing newlines from rendered outputclass_attribute:strip_trailing_newlines,default: falseENCODING_TAG=Regexp.new("\\A(<%#{ENCODING_FLAG}-?%>)[ \\t]*")LocationParsingError=Class.new(StandardError)# :nodoc:defself.call(template,source)new.call(template,source)enddefsupports_streaming?trueenddefhandles_encoding?trueend# Translate an error location returned by ErrorHighlight to the correct# source location inside the template.deftranslate_location(spot,backtrace_location,source)# Tokenize the source linesource_lines=source.linesreturnnilifsource_lines.size<backtrace_location.linenotokens=::ERB::Util.tokenize(source_lines[backtrace_location.lineno-1])new_first_column=find_offset(spot[:snippet],tokens,spot[:first_column])lineno_delta=spot[:first_lineno]-backtrace_location.linenospot[:first_lineno]-=lineno_deltaspot[:last_lineno]-=lineno_deltacolumn_delta=spot[:first_column]-new_first_columnspot[:first_column]-=column_deltaspot[:last_column]-=column_deltaspot[:script_lines]=source_linesspotrescueNotImplementedError,LocationParsingErrornilenddefcall(template,source)# First, convert to BINARY, so in case the encoding is# wrong, we can still find an encoding tag# (<%# encoding %>) inside the String using a regular# expressiontemplate_source=source.berb=template_source.gsub(ENCODING_TAG,"")encoding=$2erb.force_encodingvalid_encoding(source.dup,encoding)# Always make sure we return a String in the default_internalerb.encode!# Strip trailing newlines from the template if enablederb.chomp!ifstrip_trailing_newlinesoptions={escape: (self.class.escape_ignore_list.include?template.type),trim: (self.class.erb_trim_mode=="-")}ifActionView::Base.annotate_rendered_view_with_filenames&&template.format==:htmloptions[:preamble]="@output_buffer.safe_append='<!-- BEGIN #{template.short_identifier} -->';"options[:postamble]="@output_buffer.safe_append='<!-- END #{template.short_identifier} -->';@output_buffer"endself.class.erb_implementation.new(erb,options).srcendprivatedefvalid_encoding(string,encoding)# If a magic encoding comment was found, tag the# String with this encoding. This is for a case# where the original String was assumed to be,# for instance, UTF-8, but a magic comment# proved otherwisestring.force_encoding(encoding)ifencoding# If the String is valid, return the encoding we foundreturnstring.encodingifstring.valid_encoding?# Otherwise, raise an exceptionraiseWrongEncodingError.new(string,string.encoding)end# Find which token in the source template spans the byte range that# contains the error_column, then return the offset compared to the# original source template.## Iterate consecutive pairs of CODE or TEXT tokens, requiring# a match of the first token before matching either token.## For example, if we want to find tokens A, B, C, we do the following:# 1. Find a match for A: test error_column or advance scanner.# 2. Find a match for B or A:# a. If B: start over with next token set (B, C).# b. If A: test error_column or advance scanner.# c. Otherwise: Advance 1 byte## Prioritize matching the next token over the current token once# a match for the current token has been found. This is to prevent# the current token from looping past the next token if they both# match (i.e. if the current token is a single space character).deffind_offset(compiled,source_tokens,error_column)compiled=StringScanner.new(compiled)offset_source_tokens(source_tokens).each_cons(2)do|(name,str,offset),(_,next_str,_)|matched_str=falseuntilcompiled.eos?ifmatched_str&&next_str&&compiled.match?(next_str)breakelsifcompiled.match?(str)matched_str=trueifname==:CODE&&compiled.pos<=error_column&&compiled.pos+str.bytesize>=error_columnreturnerror_column-compiled.pos+offsetendcompiled.pos+=str.bytesizeelsecompiled.pos+=1endendendraiseLocationParsingError,"Couldn't find code snippet"enddefoffset_source_tokens(source_tokens)source_offset=0with_offset=source_tokens.filter_mapdo|(name,str)|result=[name,str,source_offset]ifname==:CODE||name==:TEXTsource_offset+=str.bytesizeresultendwith_offset<<[:EOS,nil,source_offset]endendendendend