class Opal::Nodes::XStringNode

Public Class Methods

single_line?(children) click to toggle source

Check if there’s only one child or if they’re all part of the same line (e.g. because of interpolations)

# File lib/opal/nodes/x_string.rb, line 48
def self.single_line?(children)
  (children.size == 1) || children.none? do |c|
    c.type == :str && c.loc.expression.source.end_with?("\n")
  end
end
strip_empty_children(children) click to toggle source

Will remove empty :str lines coming from cosmetic newlines in x-strings

@example

# this will generate two additional empty
# children before and after `foo()`
%x{
  foo()
}
# File lib/opal/nodes/x_string.rb, line 62
def self.strip_empty_children(children)
  children = children.dup
  empty_line = ->(child) { child.nil? || (child.type == :str && child.loc.expression.source.rstrip.empty?) }

  children.shift while children.any? && empty_line[children.first]
  children.pop while children.any? && empty_line[children.last]

  children
end

Public Instance Methods

compile() click to toggle source
# File lib/opal/nodes/x_string.rb, line 8
def compile
  if compiler.backtick_javascript_or_warn?
    compile_javascript
  else
    compile_send
  end
end
compile_javascript() click to toggle source
# File lib/opal/nodes/x_string.rb, line 21
def compile_javascript
  @should_add_semicolon = false
  unpacked_children = unpack_return(children)
  stripped_children = XStringNode.strip_empty_children(unpacked_children)

  if XStringNode.single_line?(stripped_children)
    # If it's a single line we'll try to:
    #
    # - strip empty lines
    # - remove a trailing `;`
    # - detect an embedded `return`
    # - prepend a `return` when needed
    # - append a `;` when needed
    # - warn the user not to use the semicolon in single-line x-strings
    compile_single_line(stripped_children)
  else
    # Here we leave to the user the responsibility to add
    # a return where it's due.
    unpacked_children.each { |c| compile_child(c) }
  end

  wrap '(', ')' if recv?
  push ';' if @should_add_semicolon
end
compile_send() click to toggle source
# File lib/opal/nodes/x_string.rb, line 16
def compile_send
  sexp = s(:send, nil, :`, s(:dstr, *children))
  push process(sexp, @level)
end

Private Instance Methods

compile_child(child) click to toggle source
# File lib/opal/nodes/x_string.rb, line 74
def compile_child(child)
  case child.type
  when :str
    value = child.loc.expression.source
    scope.self if value.include? 'self'
    push Fragment.new(value, scope, child)
  when :begin, :gvar, :ivar, :nil
    push expr(child)
  else
    raise "Unsupported xstr part: #{child.type}"
  end
end
compile_single_line(children) click to toggle source
# File lib/opal/nodes/x_string.rb, line 87
def compile_single_line(children)
  has_embeded_return = false

  first_child  = children.shift
  single_child = children.empty?

  first_child ||= s(:nil)

  if first_child.type == :str
    first_value = first_child.loc.expression.source.strip
    has_embeded_return = first_value =~ /^return\b/
  end

  push('return ') if @returning && !has_embeded_return

  last_child = children.pop || first_child
  last_value = extract_last_value(last_child) if last_child.type == :str

  unless single_child
    # assuming there's an interpolation somewhere (type != :str)
    @should_add_semicolon = false
    compile_child(first_child)
    children.each { |c| compile_child(c) }
  end

  if last_child.type == :str
    push Fragment.new(last_value, scope, last_child)
  else
    compile_child(last_child)
  end
end
extract_last_value(last_child) click to toggle source

Will drop the trailing semicolon if all conditions are met

# File lib/opal/nodes/x_string.rb, line 120
def extract_last_value(last_child)
  last_value = last_child.loc.expression.source.rstrip

  scope.self if last_value.include? 'self'

  if (@returning || expr?) && last_value.end_with?(';')
    compiler.warning(
      'Removed semicolon ending x-string expression, interpreted as unintentional',
      last_child.line,
    )
    last_value = last_value[0..-2]
  end

  @should_add_semicolon = true if @returning

  last_value
end
unpack_return(children) click to toggle source

A case for manually created :js_return statement in Compiler#returns Since we need to take original source of :str we have to use raw source so we need to combine “return” with “raw_source”

# File lib/opal/nodes/x_string.rb, line 141
def unpack_return(children)
  first_child = children.first
  @returning  = false

  if first_child.type == :js_return
    @returning = true
    children = first_child.children
  end

  children
end