Skip to content

Instantly share code, notes, and snippets.

@luikore
Created June 30, 2020 09:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save luikore/da28915e4ca1c15761e41d89f1848c16 to your computer and use it in GitHub Desktop.
Save luikore/da28915e4ca1c15761e41d89f1848c16 to your computer and use it in GitHub Desktop.
Enable some shortcuts for stimulus: @controller, @target, @click
# enable the template engine shortcuts
Slim::Engine.set_options shortcut: {'&' => {tag: 'input', attr: 'type'}, '#' => {attr: 'id'}, '.' => {attr: 'class'}}, sort_attrs: false
# enable stimulus shortcuts:
# - @controller="foo"
# - @target="bar"
# - @click...="#action"
module Slim
class Parser
# this stack works in sync with tag stack, but tracks controller context
class ContextStack < Array
def initialize elements
super elements
@stimulus_controller = []
end
def pop
mark, _ = @stimulus_controller.last
if mark == (size - 1)
@stimulus_controller.pop
end
super
end
def delete_at i
deleted = false
@stimulus_controller.delete_if do |obj|
if deleted
# carefully modify the markers afterwards to ensure sync
obj[0] -= 1
false
else
should = (obj[0] == i)
deleted = true if should
should
end
end
super i
end
def push_controller c
@stimulus_controller.push [size, c]
end
def curr_controller
_, c = @stimulus_controller.last
c
end
end
def reset(lines = nil, stacks = nil)
@indents = []
@stacks = (ContextStack.new(stacks) if stacks)
@lineno = 0
@lines = lines
@line = @orig_line = nil
end
def parse_attributes(attributes)
# Check to see if there is a delimiter right after the tag name
delimiter = nil
if @line =~ @attr_list_delims_re
delimiter = @attr_list_delims[$1]
@line = $'
end
if delimiter
boolean_attr_re = /#{@attr_name}(?=(\s|#{Regexp.escape delimiter}|\Z))/
end_re = /\A\s*#{Regexp.escape delimiter}/
end
while true
case @line
when @splat_attrs_regexp
# Splat attribute
@line = $'
attributes << [:slim, :splat, parse_ruby_code(delimiter)]
when /\A\s*@(\w+)\s*=\s*(["'])([\w\.\-\>\#]+)\2/
attr_value = $3
attr_name = $1
line_rest = $'
if (attr_name == 'target' and !attr_value['.'])
if c = @stacks.curr_controller
attr_value = "#{c}.#{attr_value}"
else
syntax_error!('Using abbreviated data-target without declaring @controller')
end
end
if attr_name != 'target' and attr_name != 'controller'
case attr_value
when /\w+\#\w+/
# OK, not abbreviated
when /\#\w+/
if c = @stacks.curr_controller
attr_value = "#{c}#{attr_value}"
else
syntax_error!('Using abbreviated data-target without declaring @controller')
end
else
syntax_error!('Invalid action format, should be like `controller#action_name` or just `#action_name`')
end
end
if attr_value['#']
@line = line_rest
attr_value = "#{attr_name}->#{attr_value}"
# look for existing data-action, if there is one, append to it
have_data_action = false
attributes.each do |(h, a, aname, rest)|
if [h, a, aname] == [:html, :attr, 'data-action']
rest[2] << " #{attr_value}"
have_data_action = true
break
end
end
if !have_data_action
attributes << [:html, :attr, 'data-action', [:slim, :interpolate, attr_value]]
end
else
case attr_name
when 'controller'
@line = line_rest
@stacks.push_controller attr_value
attributes << [:html, :attr, 'data-controller', [:slim, :interpolate, attr_value]]
when 'target'
@line = line_rest
attributes << [:html, :attr, 'data-target', [:slim, :interpolate, attr_value]]
else
syntax_error!('Must use "@controller=" or "@target=" or assign "controller#action"')
end
end
when /\A\s*@/
syntax_error!('Expect @controller or @target or event names')
when @quoted_attr_re
# Value is quoted (static)
@line = $'
attributes << [:html, :attr, $1,
[:escape, $2.empty?, [:slim, :interpolate, parse_quoted_attribute($3)]]]
when @code_attr_re
# Value is ruby code
@line = $'
name = $1
escape = $2.empty?
value = parse_ruby_code(delimiter)
syntax_error!('Invalid empty attribute') if value.empty?
attributes << [:html, :attr, name, [:slim, :attrvalue, escape, value]]
else
break unless delimiter
case @line
when boolean_attr_re
# Boolean attribute
@line = $'
attributes << [:html, :attr, $1, [:multi]]
when end_re
# Find ending delimiter
@line = $'
break
else
# Found something where an attribute should be
@line.lstrip!
syntax_error!('Expected attribute') unless @line.empty?
# Attributes span multiple lines
@stacks.last << [:newline]
syntax_error!("Expected closing delimiter #{delimiter}") if @lines.empty?
next_line
end
end
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment