Skip to content

Commit

Permalink
Introduce support to range iterations and SFR tags (#685)
Browse files Browse the repository at this point in the history
  • Loading branch information
karreiro committed Dec 14, 2022
1 parent b587f7d commit 2328269
Show file tree
Hide file tree
Showing 6 changed files with 160 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,10 @@ def completions(context)

finder = VariableLookupFinder::AssignmentsFinder.new(content)
finder.find!

finder.assignments.map do |label, potential_lookup|
object, _property = VariableLookupTraverser.lookup_object_and_property(potential_lookup)
object_to_completion(label, object.name)
end
finder
.assignments
.map { |label, potential_lookup| object_to_completion(label, object_name(potential_lookup)) }
.compact
end

private
Expand All @@ -31,6 +30,11 @@ def object_to_completion(label, object)
**doc_hash(content),
}
end

def object_name(potential_lookup)
object, _property = VariableLookupTraverser.lookup_object_and_property(potential_lookup)
object&.name
end
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -20,33 +20,36 @@ def <<(node)
case tag
when Liquid::Assign
variable_name = tag.to
variables[variable_name] = assign_tag_as_potential_lookup(tag)
variables[variable_name] = as_potential_lookup(tag.from.name)
when Liquid::For, Liquid::TableRow
variable_name = tag.variable_name
variables[variable_name] = iteration_tag_as_potential_lookup(tag)
variables[variable_name] = as_potential_lookup(tag.collection_name, ['first'])
end
end

private

def assign_tag_as_potential_lookup(tag)
variable_lookup = tag.from.name

unless variable_lookup.is_a?(Liquid::VariableLookup)
return PotentialLookup.new(input_type_of(variable_lookup), [], variables)
def as_potential_lookup(variable_lookup, default_lookups = [])
case variable_lookup
when Liquid::VariableLookup
potential_lookup(variable_lookup, default_lookups)
when Liquid::RangeLookup
as_potential_lookup(variable_lookup.start_obj)
when Enumerable
as_potential_lookup(variable_lookup.first)
else
literal_lookup(variable_lookup)
end

name = variable_lookup.name
lookups = variable_lookup.lookups

PotentialLookup.new(name, lookups, variables)
end

def iteration_tag_as_potential_lookup(tag)
variable_lookup = tag.collection_name
def literal_lookup(variable_lookup)
name = input_type_of(variable_lookup)
PotentialLookup.new(name, [], variables)
end

def potential_lookup(variable_lookup, default_lookups)
name = variable_lookup.name
lookups = [*variable_lookup.lookups, 'first']
lookups = variable_lookup.lookups.concat(default_lookups)

PotentialLookup.new(name, lookups, variables)
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ module LanguageServer
module VariableLookupFinder
class AssignmentsFinder
class ScopeVisitor
SCOPE_UNAWARE_NODES = %i(range range_lookup variable variable_lookup)

attr_reader :global_scope, :current_scope

def initialize
Expand All @@ -22,7 +24,7 @@ def visit_template(template)
private

def visit(node, scope)
return if node.type_name == :variable_lookup
return if SCOPE_UNAWARE_NODES.include?(node.type_name)

method = :"on_#{node.type_name}"
scope = @node_handler.send(method, node, scope) if @node_handler.respond_to?(method)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,12 +73,32 @@ class Unless < Liquid::Unless
include TolerantBlockBody
end

class Paginate < Liquid::Tag
include TolerantBlockBody
end

class Form < Liquid::Tag
include TolerantBlockBody
end

class Style < Liquid::Tag
include TolerantBlockBody
end

class Stylesheet < Liquid::Tag
include TolerantBlockBody
end

def initialize(standard_tags)
@standard_tags = standard_tags
@tolerant_tags = {
'case' => Case,
'for' => For,
'form' => Form,
'if' => If,
'paginate' => Paginate,
'style' => Style,
'stylesheet' => Stylesheet,
'tablerow' => TableRow,
'unless' => Unless,
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,23 @@ def setup
{%- liquid
assign target = cart
assign product_2 = product
assign columns_mobile_int = section.settings.columns_mobile_int
assign show_mobile_slider = false
%}
{{
LIQUID
end

def test_suggests_assigned_variables
ShopifyLiquid::Documentation.expects(:object_doc).with("cart")
ShopifyLiquid::Documentation.expects(:object_doc).with("product")
ShopifyLiquid::Documentation.stubs(:object_doc).with("cart")
ShopifyLiquid::Documentation.stubs(:object_doc).with("boolean")
ShopifyLiquid::Documentation.stubs(:object_doc).with("product")
ShopifyLiquid::Documentation.stubs(:object_doc).with(nil)

assert_can_complete_with(@provider, @token, 'target')
assert_can_complete_with(@provider, @token, 'product_2')
assert_can_complete_with(@provider, @token, 'columns_mobile_int')
assert_can_complete_with(@provider, @token, 'show_mobile_slider')
end

def test_does_not_suggest_global_objects
Expand Down
101 changes: 101 additions & 0 deletions test/language_server/variable_lookup_finder/assignments_finder_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,107 @@ def test_assignments_finder_with_for_statements
})
end

def test_assignments_finder_with_for_statements_and_ranges
template = <<~LIQUID
{%- liquid
assign var1 = product
-%}
{%- for var2 in (1..4) -%}
{% echo█
LIQUID

assert_assignments_finder(template, {
'var1' => 'product',
'var2' => 'number',
})
end

def test_assignments_finder_with_for_statements_and_variable_based_ranges
template = <<~LIQUID
{%- liquid
assign var1 = product
-%}
{% assign var2 = 1 %}
{% assign var3 = 4 %}
{%- for var4 in (var2..var3) -%}
{% echo█
LIQUID

assert_assignments_finder(template, {
'var1' => 'product',
'var2' => 'number',
'var3' => 'number',
'var4' => 'var2',
})
end

def test_assignments_finder_with_for_statements_and_variable_and_literal_ranges
template = <<~LIQUID
{%- liquid
assign var1 = product
-%}
{% assign var2 = 1 %}
{%- for var3 in (1..var2) -%}
{% echo█
LIQUID

assert_assignments_finder(template, {
'var1' => 'product',
'var2' => 'number',
'var3' => 'number',
})
end

def test_assignments_finder_with_form_tag
template = <<~LIQUID
{%- form 'localization', id: 'FooterLanguageFormNoScript', class: 'localization-form' -%}
{%- for language in localization.available_languages -%}
<option value="{{ language.█
LIQUID

assert_assignments_finder(template, {
'language' => 'localization',
})
end

def test_assignments_finder_with_paginate_tag
template = <<~LIQUID
{% paginate collection.products by 5 %}
{% for var1 in collection.products -%}
{% echo █
LIQUID

assert_assignments_finder(template, {
'var1' => 'collection',
})
end

def test_assignments_finder_with_style_tag
template = <<~LIQUID
{% style %}
.h1 {
{%- for language in localization.available_languages -%}
<option value="{{ language.█
LIQUID

assert_assignments_finder(template, {
'language' => 'localization',
})
end

def test_assignments_finder_with_stylesheet_tag
template = <<~LIQUID
{% stylesheet %}
.h1 {
{%- for language in localization.available_languages -%}
<option value="{{ language.█
LIQUID

assert_assignments_finder(template, {
'language' => 'localization',
})
end

def test_assignments_finder_with_for_and_else_statements
template = <<~LIQUID
{%- liquid
Expand Down

0 comments on commit 2328269

Please sign in to comment.