theme-check 1.6.0 → 1.7.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +27 -0
  3. data/data/shopify_liquid/tags.yml +9 -9
  4. data/docs/api/html_check.md +7 -7
  5. data/docs/api/liquid_check.md +10 -10
  6. data/docs/checks/convert_include_to_render.md +1 -1
  7. data/docs/checks/missing_enable_comment.md +1 -1
  8. data/lib/theme_check/analyzer.rb +41 -17
  9. data/lib/theme_check/asset_file.rb +1 -1
  10. data/lib/theme_check/check.rb +2 -2
  11. data/lib/theme_check/checks/html_parsing_error.rb +2 -2
  12. data/lib/theme_check/checks/matching_translations.rb +1 -1
  13. data/lib/theme_check/checks/missing_template.rb +6 -6
  14. data/lib/theme_check/checks/nested_snippet.rb +2 -2
  15. data/lib/theme_check/checks/required_layout_theme_object.rb +2 -2
  16. data/lib/theme_check/checks/syntax_error.rb +5 -5
  17. data/lib/theme_check/checks/template_length.rb +2 -2
  18. data/lib/theme_check/checks/translation_key_exists.rb +1 -13
  19. data/lib/theme_check/checks/undefined_object.rb +7 -7
  20. data/lib/theme_check/checks/unused_assign.rb +4 -4
  21. data/lib/theme_check/checks/unused_snippet.rb +7 -7
  22. data/lib/theme_check/checks/valid_json.rb +1 -1
  23. data/lib/theme_check/checks.rb +4 -2
  24. data/lib/theme_check/cli.rb +1 -1
  25. data/lib/theme_check/corrector.rb +6 -6
  26. data/lib/theme_check/disabled_check.rb +3 -3
  27. data/lib/theme_check/disabled_checks.rb +9 -9
  28. data/lib/theme_check/exceptions.rb +1 -0
  29. data/lib/theme_check/file_system_storage.rb +4 -0
  30. data/lib/theme_check/html_node.rb +36 -28
  31. data/lib/theme_check/html_visitor.rb +6 -6
  32. data/lib/theme_check/in_memory_storage.rb +1 -1
  33. data/lib/theme_check/json_check.rb +2 -2
  34. data/lib/theme_check/language_server/bridge.rb +128 -0
  35. data/lib/theme_check/language_server/channel.rb +69 -0
  36. data/lib/theme_check/language_server/completion_providers/tag_completion_provider.rb +3 -1
  37. data/lib/theme_check/language_server/diagnostics_engine.rb +125 -0
  38. data/lib/theme_check/language_server/diagnostics_tracker.rb +8 -8
  39. data/lib/theme_check/language_server/handler.rb +20 -117
  40. data/lib/theme_check/language_server/io_messenger.rb +97 -0
  41. data/lib/theme_check/language_server/messenger.rb +27 -0
  42. data/lib/theme_check/language_server/server.rb +95 -104
  43. data/lib/theme_check/language_server.rb +6 -1
  44. data/lib/theme_check/{template.rb → liquid_file.rb} +2 -2
  45. data/lib/theme_check/liquid_node.rb +291 -0
  46. data/lib/theme_check/{visitor.rb → liquid_visitor.rb} +4 -4
  47. data/lib/theme_check/locale_diff.rb +14 -7
  48. data/lib/theme_check/node.rb +12 -225
  49. data/lib/theme_check/offense.rb +15 -15
  50. data/lib/theme_check/position.rb +1 -1
  51. data/lib/theme_check/shopify_liquid/system_translations.rb +35 -0
  52. data/lib/theme_check/shopify_liquid/tag.rb +19 -1
  53. data/lib/theme_check/shopify_liquid.rb +1 -0
  54. data/lib/theme_check/theme.rb +1 -1
  55. data/lib/theme_check/{template_rewriter.rb → theme_file_rewriter.rb} +1 -1
  56. data/lib/theme_check/version.rb +1 -1
  57. data/lib/theme_check.rb +11 -10
  58. data/theme-check.gemspec +1 -1
  59. metadata +14 -7
@@ -0,0 +1,291 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ThemeCheck
4
+ # A node from the Liquid AST, the result of parsing a liquid file.
5
+ class LiquidNode < Node
6
+ attr_reader :value, :parent, :theme_file
7
+
8
+ def initialize(value, parent, theme_file)
9
+ raise ArgumentError, "Expected a Liquid AST Node" if value.is_a?(LiquidNode)
10
+ @value = value
11
+ @parent = parent
12
+ @theme_file = theme_file
13
+ @tag_markup = nil
14
+ @line_number_offset = 0
15
+ end
16
+
17
+ # Array of children nodes.
18
+ def children
19
+ @children ||= begin
20
+ nodes =
21
+ if comment?
22
+ []
23
+ elsif defined?(@value.class::ParseTreeVisitor)
24
+ @value.class::ParseTreeVisitor.new(@value, {}).children
25
+ elsif @value.respond_to?(:nodelist)
26
+ Array(@value.nodelist)
27
+ else
28
+ []
29
+ end
30
+ # Work around a bug in Liquid::Variable::ParseTreeVisitor that doesn't return
31
+ # the args in a hash as children nodes.
32
+ nodes = nodes.flat_map do |node|
33
+ case node
34
+ when Hash
35
+ node.values
36
+ else
37
+ node
38
+ end
39
+ end
40
+ nodes.map { |node| LiquidNode.new(node, self, @theme_file) }
41
+ end
42
+ end
43
+
44
+ # The original source code of the node. Doesn't contain wrapping braces.
45
+ def markup
46
+ if tag?
47
+ tag_markup
48
+ elsif @value.instance_variable_defined?(:@markup)
49
+ @value.instance_variable_get(:@markup)
50
+ end
51
+ end
52
+
53
+ def markup=(markup)
54
+ if @value.instance_variable_defined?(:@markup)
55
+ @value.instance_variable_set(:@markup, markup)
56
+ end
57
+ end
58
+
59
+ # Most nodes have a line number, but it's not guaranteed.
60
+ def line_number
61
+ if tag? && @value.respond_to?(:line_number)
62
+ markup # initialize the line_number_offset
63
+ @value.line_number - @line_number_offset
64
+ elsif @value.respond_to?(:line_number)
65
+ @value.line_number
66
+ end
67
+ end
68
+
69
+ def start_index
70
+ position.start_index
71
+ end
72
+
73
+ def end_index
74
+ position.end_index
75
+ end
76
+
77
+ # Literals are hard-coded values in the liquid file.
78
+ def literal?
79
+ @value.is_a?(String) || @value.is_a?(Integer)
80
+ end
81
+
82
+ # A {% tag %} node?
83
+ def tag?
84
+ @value.is_a?(Liquid::Tag)
85
+ end
86
+
87
+ def variable?
88
+ @value.is_a?(Liquid::Variable)
89
+ end
90
+
91
+ # A {% comment %} block node?
92
+ def comment?
93
+ @value.is_a?(Liquid::Comment)
94
+ end
95
+
96
+ # Top level node of every liquid_file.
97
+ def document?
98
+ @value.is_a?(Liquid::Document)
99
+ end
100
+ alias_method :root?, :document?
101
+
102
+ # A {% tag %}...{% endtag %} node?
103
+ def block_tag?
104
+ @value.is_a?(Liquid::Block)
105
+ end
106
+
107
+ # The body of blocks
108
+ def block_body?
109
+ @value.is_a?(Liquid::BlockBody)
110
+ end
111
+
112
+ # A block of type of node?
113
+ def block?
114
+ block_tag? || block_body? || document?
115
+ end
116
+
117
+ # The `:under_score_name` of this type of node. Used to dispatch to the `on_<type_name>`
118
+ # and `after_<type_name>` check methods.
119
+ def type_name
120
+ @type_name ||= StringHelpers.underscore(StringHelpers.demodulize(@value.class.name)).to_sym
121
+ end
122
+
123
+ def source
124
+ theme_file&.source
125
+ end
126
+
127
+ WHITESPACE = /\s/
128
+
129
+ # Is this node inside a `{% liquid ... %}` block?
130
+ def inside_liquid_tag?
131
+ # What we're doing here is starting at the start of the tag and
132
+ # backtrack on all the whitespace until we land on something. If
133
+ # that something is {% or %-, then we can safely assume that
134
+ # we're inside a full tag and not a liquid tag.
135
+ @inside_liquid_tag ||= if tag? && start_index && source
136
+ i = 1
137
+ i += 1 while source[start_index - i] =~ WHITESPACE && i < start_index
138
+ first_two_backtracked_characters = source[(start_index - i - 1)..(start_index - i)]
139
+ first_two_backtracked_characters != "{%" && first_two_backtracked_characters != "%-"
140
+ else
141
+ false
142
+ end
143
+ end
144
+
145
+ # Is this node inside a tag or variable that starts by removing whitespace. i.e. {%- or {{-
146
+ def whitespace_trimmed_start?
147
+ @whitespace_trimmed_start ||= if start_index && source && !inside_liquid_tag?
148
+ i = 1
149
+ i += 1 while source[start_index - i] =~ WHITESPACE && i < start_index
150
+ source[start_index - i] == "-"
151
+ else
152
+ false
153
+ end
154
+ end
155
+
156
+ # Is this node inside a tag or variable ends starts by removing whitespace. i.e. -%} or -}}
157
+ def whitespace_trimmed_end?
158
+ @whitespace_trimmed_end ||= if end_index && source && !inside_liquid_tag?
159
+ i = 0
160
+ i += 1 while source[end_index + i] =~ WHITESPACE && i < source.size
161
+ source[end_index + i] == "-"
162
+ else
163
+ false
164
+ end
165
+ end
166
+
167
+ def start_token
168
+ return "" if inside_liquid_tag?
169
+ output = ""
170
+ output += "{{" if variable?
171
+ output += "{%" if tag?
172
+ output += "-" if whitespace_trimmed_start?
173
+ output
174
+ end
175
+
176
+ def end_token
177
+ return "" if inside_liquid_tag?
178
+ output = ""
179
+ output += "-" if whitespace_trimmed_end?
180
+ output += "}}" if variable?
181
+ output += "%}" if tag?
182
+ output
183
+ end
184
+
185
+ private
186
+
187
+ def position
188
+ @position ||= Position.new(
189
+ markup,
190
+ theme_file&.source,
191
+ line_number_1_indexed: line_number
192
+ )
193
+ end
194
+
195
+ # Here we're hacking around a glorious bug in Liquid that makes it so the
196
+ # line_number and markup of a tag is wrong if there's whitespace
197
+ # between the tag_name and the markup of the tag.
198
+ #
199
+ # {%
200
+ # render
201
+ # 'foo'
202
+ # %}
203
+ #
204
+ # Returns a raw value of "render 'foo'\n".
205
+ # The "\n " between render and 'foo' got replaced by a single space.
206
+ #
207
+ # And the line number is the one of 'foo'\n%}. Yay!
208
+ #
209
+ # This breaks any kind of position logic we have since that string
210
+ # does not exist in the theme_file.
211
+ def tag_markup
212
+ return @tag_markup if @tag_markup
213
+
214
+ l = 1
215
+ scanner = StringScanner.new(source)
216
+ scanner.scan_until(/\n/) while l < @value.line_number && (l += 1)
217
+ start = scanner.charpos
218
+
219
+ tag_name = @value.tag_name
220
+ tag_markup = @value.instance_variable_get('@markup')
221
+
222
+ # This is tricky, if the tag_markup is empty, then the tag could
223
+ # either start on a previous line, or the tag could start on the
224
+ # same line.
225
+ #
226
+ # Consider this:
227
+ # 1 {%
228
+ # 2 comment
229
+ # 3 %}{% endcomment %}{%comment%}
230
+ #
231
+ # Both comments would markup == "" AND line_number == 3
232
+ #
233
+ # There's no way to determine which one is the correct one, but
234
+ # we'll try our best to at least give you one.
235
+ #
236
+ # To screw with you even more, the name of the tag could be
237
+ # outside of a tag on the same line :) But I won't do anything
238
+ # about that (yet?).
239
+ #
240
+ # {% comment
241
+ # %}comment{% endcomment %}
242
+ if tag_markup.empty?
243
+ eol = source.index("\n", start) || source.size
244
+
245
+ # OK here I'm trying one of two things. Either tag_start is on
246
+ # the same line OR tag_start is on a previous line. The line
247
+ # number would be at the end of the whitespace after tag_name.
248
+ unless (tag_start = source.index(tag_name, start)) && tag_start < eol
249
+ tag_start = start
250
+ tag_start -= 1 while source[tag_start - 1] =~ WHITESPACE
251
+ tag_start -= @value.tag_name.size
252
+
253
+ # keep track of the error in line_number
254
+ @line_number_offset = source[tag_start...start].count("\n")
255
+ end
256
+ tag_end = tag_start + tag_name.size
257
+ tag_end += 1 while source[tag_end] =~ WHITESPACE
258
+
259
+ # return the real raw content
260
+ @tag_markup = source[tag_start...tag_end]
261
+ return @tag_markup
262
+
263
+ # See https://github.com/Shopify/theme-check/pull/423/files#r701936559 for a detailed explanation
264
+ # of why we're doing the check below.
265
+ #
266
+ # TL;DR it's because line_numbers are not enough to accurately
267
+ # determine the position of the raw markup and because that
268
+ # markup could be present on the same line outside of a Tag. e.g.
269
+ #
270
+ # uhoh {% if uhoh %}
271
+ elsif (match = /#{tag_name} +#{Regexp.escape(tag_markup)}/.match(source, start))
272
+ return @tag_markup = match[0]
273
+ end
274
+
275
+ # find the markup
276
+ markup_start = source.index(tag_markup, start)
277
+ markup_end = markup_start + tag_markup.size
278
+
279
+ # go back until you find the tag_name
280
+ tag_start = markup_start
281
+ tag_start -= 1 while source[tag_start - 1] =~ WHITESPACE
282
+ tag_start -= tag_name.size
283
+
284
+ # keep track of the error in line_number
285
+ @line_number_offset = source[tag_start...markup_start].count("\n")
286
+
287
+ # return the real raw content
288
+ @tag_markup = source[tag_start...markup_end]
289
+ end
290
+ end
291
+ end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  module ThemeCheck
3
- class Visitor
3
+ class LiquidVisitor
4
4
  attr_reader :checks
5
5
 
6
6
  def initialize(checks, disabled_checks)
@@ -8,10 +8,10 @@ module ThemeCheck
8
8
  @disabled_checks = disabled_checks
9
9
  end
10
10
 
11
- def visit_template(template)
12
- visit(Node.new(template.root, nil, template))
11
+ def visit_liquid_file(liquid_file)
12
+ visit(LiquidNode.new(liquid_file.root, nil, liquid_file))
13
13
  rescue Liquid::Error => exception
14
- exception.template_name = template.name
14
+ exception.template_name = liquid_file.name
15
15
  call_checks(:on_error, exception)
16
16
  end
17
17
 
@@ -14,26 +14,26 @@ module ThemeCheck
14
14
  visit_object(@default, @other, [])
15
15
  end
16
16
 
17
- def add_as_offenses(check, key_prefix: [], node: nil, template: nil)
17
+ def add_as_offenses(check, key_prefix: [], node: nil, theme_file: nil)
18
18
  if extra_keys.any?
19
19
  add_keys_offense(check, "Extra translation keys", extra_keys,
20
- key_prefix: key_prefix, node: node, template: template)
20
+ key_prefix: key_prefix, node: node, theme_file: theme_file)
21
21
  end
22
22
 
23
23
  if missing_keys.any?
24
24
  add_keys_offense(check, "Missing translation keys", missing_keys,
25
- key_prefix: key_prefix, node: node, template: template)
25
+ key_prefix: key_prefix, node: node, theme_file: theme_file)
26
26
  end
27
27
  end
28
28
 
29
29
  private
30
30
 
31
- def add_keys_offense(check, cause, keys, key_prefix:, node: nil, template: nil)
31
+ def add_keys_offense(check, cause, keys, key_prefix:, node: nil, theme_file: nil)
32
32
  message = "#{cause}: #{format_keys(key_prefix, keys)}"
33
33
  if node
34
34
  check.add_offense(message, node: node)
35
35
  else
36
- check.add_offense(message, template: template)
36
+ check.add_offense(message, theme_file: theme_file)
37
37
  end
38
38
  end
39
39
 
@@ -46,10 +46,12 @@ module ThemeCheck
46
46
  other = {} unless other.is_a?(Hash)
47
47
  return if pluralization?(default) && pluralization?(other)
48
48
 
49
- @extra_keys += (other.keys - default.keys).map { |key| path + [key] }
49
+ shopify_translations = system_translations(path)
50
+
51
+ @extra_keys += (other.keys - default.keys - shopify_translations.keys).map { |key| path + [key] }
50
52
 
51
53
  default.each do |key, default_value|
52
- translated_value = other[key]
54
+ translated_value = other[key] || shopify_translations[key]
53
55
  new_path = path + [key]
54
56
 
55
57
  if translated_value.nil?
@@ -65,5 +67,10 @@ module ThemeCheck
65
67
  PLURALIZATION_KEYS.include?(key) && !value.is_a?(Hash)
66
68
  end
67
69
  end
70
+
71
+ def system_translations(path)
72
+ return ShopifyLiquid::SystemTranslations.translations_hash if path.empty?
73
+ ShopifyLiquid::SystemTranslations.translations_hash.dig(*path) || {}
74
+ end
68
75
  end
69
76
  end
@@ -1,250 +1,37 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ThemeCheck
4
- # A node from the Liquid AST, the result of parsing a template.
5
4
  class Node
6
- attr_reader :value, :parent, :template
7
-
8
- def initialize(value, parent, template)
9
- raise ArgumentError, "Expected a Liquid AST Node" if value.is_a?(Node)
10
- @value = value
11
- @parent = parent
12
- @template = template
13
- @tag_markup = nil
14
- @line_number_offset = 0
5
+ def parent
6
+ raise NotImplementedError
15
7
  end
16
8
 
17
- # The original source code of the node. Doesn't contain wrapping braces.
18
- def markup
19
- if tag?
20
- tag_markup
21
- elsif @value.instance_variable_defined?(:@markup)
22
- @value.instance_variable_get(:@markup)
23
- end
9
+ def theme_file
10
+ raise NotImplementedError
24
11
  end
25
12
 
26
- def markup=(markup)
27
- if @value.instance_variable_defined?(:@markup)
28
- @value.instance_variable_set(:@markup, markup)
29
- end
13
+ def value
14
+ raise NotImplementedError
30
15
  end
31
16
 
32
- # Array of children nodes.
33
17
  def children
34
- @children ||= begin
35
- nodes =
36
- if comment?
37
- []
38
- elsif defined?(@value.class::ParseTreeVisitor)
39
- @value.class::ParseTreeVisitor.new(@value, {}).children
40
- elsif @value.respond_to?(:nodelist)
41
- Array(@value.nodelist)
42
- else
43
- []
44
- end
45
- # Work around a bug in Liquid::Variable::ParseTreeVisitor that doesn't return
46
- # the args in a hash as children nodes.
47
- nodes = nodes.flat_map do |node|
48
- case node
49
- when Hash
50
- node.values
51
- else
52
- node
53
- end
54
- end
55
- nodes.map { |node| Node.new(node, self, @template) }
56
- end
57
- end
58
-
59
- # Literals are hard-coded values in the template.
60
- def literal?
61
- @value.is_a?(String) || @value.is_a?(Integer)
62
- end
63
-
64
- # A {% tag %} node?
65
- def tag?
66
- @value.is_a?(Liquid::Tag)
67
- end
68
-
69
- def variable?
70
- @value.is_a?(Liquid::Variable)
71
- end
72
-
73
- # A {% comment %} block node?
74
- def comment?
75
- @value.is_a?(Liquid::Comment)
76
- end
77
-
78
- # Top level node of every template.
79
- def document?
80
- @value.is_a?(Liquid::Document)
81
- end
82
- alias_method :root?, :document?
83
-
84
- # A {% tag %}...{% endtag %} node?
85
- def block_tag?
86
- @value.is_a?(Liquid::Block)
18
+ raise NotImplementedError
87
19
  end
88
20
 
89
- # The body of blocks
90
- def block_body?
91
- @value.is_a?(Liquid::BlockBody)
92
- end
93
-
94
- # A block of type of node?
95
- def block?
96
- block_tag? || block_body? || document?
21
+ def markup
22
+ raise NotImplementedError
97
23
  end
98
24
 
99
- # Most nodes have a line number, but it's not guaranteed.
100
25
  def line_number
101
- if tag? && @value.respond_to?(:line_number)
102
- markup # initialize the line_number_offset
103
- @value.line_number - @line_number_offset
104
- elsif @value.respond_to?(:line_number)
105
- @value.line_number
106
- end
107
- end
108
-
109
- # The `:under_score_name` of this type of node. Used to dispatch to the `on_<type_name>`
110
- # and `after_<type_name>` check methods.
111
- def type_name
112
- @type_name ||= StringHelpers.underscore(StringHelpers.demodulize(@value.class.name)).to_sym
113
- end
114
-
115
- def source
116
- template&.source
117
- end
118
-
119
- WHITESPACE = /\s/
120
-
121
- # Is this node inside a `{% liquid ... %}` block?
122
- def inside_liquid_tag?
123
- # What we're doing here is starting at the start of the tag and
124
- # backtrack on all the whitespace until we land on something. If
125
- # that something is {% or %-, then we can safely assume that
126
- # we're inside a full tag and not a liquid tag.
127
- @inside_liquid_tag ||= if tag? && line_number && source
128
- i = 1
129
- i += 1 while source[start_index - i] =~ WHITESPACE && i < start_index
130
- first_two_backtracked_characters = source[(start_index - i - 1)..(start_index - i)]
131
- first_two_backtracked_characters != "{%" && first_two_backtracked_characters != "%-"
132
- else
133
- false
134
- end
135
- end
136
-
137
- # Is this node inside a tag or variable that starts by removing whitespace. i.e. {%- or {{-
138
- def whitespace_trimmed_start?
139
- @whitespace_trimmed_start ||= if line_number && source && !inside_liquid_tag?
140
- i = 1
141
- i += 1 while source[start_index - i] =~ WHITESPACE && i < start_index
142
- source[start_index - i] == "-"
143
- else
144
- false
145
- end
146
- end
147
-
148
- # Is this node inside a tag or variable ends starts by removing whitespace. i.e. -%} or -}}
149
- def whitespace_trimmed_end?
150
- @whitespace_trimmed_end ||= if line_number && source && !inside_liquid_tag?
151
- i = 0
152
- i += 1 while source[end_index + i] =~ WHITESPACE && i < source.size
153
- source[end_index + i] == "-"
154
- else
155
- false
156
- end
157
- end
158
-
159
- def position
160
- @position ||= Position.new(
161
- markup,
162
- template&.source,
163
- line_number_1_indexed: line_number
164
- )
26
+ raise NotImplementedError
165
27
  end
166
28
 
167
29
  def start_index
168
- position.start_index
30
+ raise NotImplementedError
169
31
  end
170
32
 
171
33
  def end_index
172
- position.end_index
173
- end
174
-
175
- def start_token
176
- return "" if inside_liquid_tag?
177
- output = ""
178
- output += "{{" if variable?
179
- output += "{%" if tag?
180
- output += "-" if whitespace_trimmed_start?
181
- output
182
- end
183
-
184
- def end_token
185
- return "" if inside_liquid_tag?
186
- output = ""
187
- output += "-" if whitespace_trimmed_end?
188
- output += "}}" if variable?
189
- output += "%}" if tag?
190
- output
191
- end
192
-
193
- private
194
-
195
- # Here we're hacking around a glorious bug in Liquid that makes it so the
196
- # line_number and markup of a tag is wrong if there's whitespace
197
- # between the tag_name and the markup of the tag.
198
- #
199
- # {%
200
- # render
201
- # 'foo'
202
- # %}
203
- #
204
- # Returns a raw value of "render 'foo'\n".
205
- # The "\n " between render and 'foo' got replaced by a single space.
206
- #
207
- # And the line number is the one of 'foo'\n%}. Yay!
208
- #
209
- # This breaks any kind of position logic we have since that string
210
- # does not exist in the template.
211
- def tag_markup
212
- return @value.raw if @value.instance_variable_get('@markup').empty?
213
- return @tag_markup if @tag_markup
214
-
215
- l = 1
216
- scanner = StringScanner.new(source)
217
- scanner.scan_until(/\n/) while l < @value.line_number && (l += 1)
218
- start = scanner.charpos
219
-
220
- tag_markup = @value.instance_variable_get('@markup')
221
-
222
- # See https://github.com/Shopify/theme-check/pull/423/files#r701936559 for a detailed explanation
223
- # of why we're doing the check below.
224
- #
225
- # TL;DR it's because line_numbers are not enough to accurately
226
- # determine the position of the raw markup and because that
227
- # markup could be present on the same line outside of a Tag. e.g.
228
- #
229
- # uhoh {% if uhoh %}
230
- if (match = /#{@value.tag_name} +#{Regexp.escape(tag_markup)}/.match(source, start))
231
- return @tag_markup = match[0]
232
- end
233
-
234
- # find the markup
235
- markup_start = source.index(tag_markup, start)
236
- markup_end = markup_start + tag_markup.size
237
-
238
- # go back until you find the tag_name
239
- tag_start = markup_start
240
- tag_start -= 1 while source[tag_start - 1] =~ WHITESPACE
241
- tag_start -= @value.tag_name.size
242
-
243
- # keep track of the error in line_number
244
- @line_number_offset = source[tag_start...markup_start].count("\n")
245
-
246
- # return the real raw content
247
- @tag_markup = source[tag_start...markup_end]
34
+ raise NotImplementedError
248
35
  end
249
36
  end
250
37
  end