theme-check 1.6.2 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +37 -0
  3. data/data/shopify_liquid/filters.yml +1 -0
  4. data/data/shopify_liquid/tags.yml +9 -9
  5. data/docs/checks/TEMPLATE.md.erb +24 -19
  6. data/exe/theme-check-language-server +0 -4
  7. data/lib/theme_check/analyzer.rb +29 -5
  8. data/lib/theme_check/checks/matching_schema_translations.rb +12 -5
  9. data/lib/theme_check/checks/required_layout_theme_object.rb +9 -4
  10. data/lib/theme_check/checks/translation_key_exists.rb +1 -13
  11. data/lib/theme_check/checks/unused_assign.rb +3 -2
  12. data/lib/theme_check/checks/unused_snippet.rb +1 -1
  13. data/lib/theme_check/corrector.rb +40 -3
  14. data/lib/theme_check/exceptions.rb +1 -0
  15. data/lib/theme_check/file_system_storage.rb +4 -0
  16. data/lib/theme_check/language_server/bridge.rb +142 -0
  17. data/lib/theme_check/language_server/channel.rb +69 -0
  18. data/lib/theme_check/language_server/completion_providers/tag_completion_provider.rb +3 -1
  19. data/lib/theme_check/language_server/diagnostics_engine.rb +125 -0
  20. data/lib/theme_check/language_server/handler.rb +24 -118
  21. data/lib/theme_check/language_server/io_messenger.rb +104 -0
  22. data/lib/theme_check/language_server/messenger.rb +27 -0
  23. data/lib/theme_check/language_server/protocol.rb +4 -0
  24. data/lib/theme_check/language_server/server.rb +111 -103
  25. data/lib/theme_check/language_server.rb +6 -1
  26. data/lib/theme_check/liquid_node.rb +33 -0
  27. data/lib/theme_check/locale_diff.rb +36 -10
  28. data/lib/theme_check/position.rb +4 -4
  29. data/lib/theme_check/shopify_liquid/system_translations.rb +35 -0
  30. data/lib/theme_check/shopify_liquid/tag.rb +19 -1
  31. data/lib/theme_check/shopify_liquid.rb +1 -0
  32. data/lib/theme_check/tags.rb +0 -1
  33. data/lib/theme_check/theme_file_rewriter.rb +13 -0
  34. data/lib/theme_check/version.rb +1 -1
  35. data/lib/theme_check.rb +4 -0
  36. metadata +8 -2
@@ -13,141 +13,149 @@ module ThemeCheck
13
13
  attr_reader :should_raise_errors
14
14
 
15
15
  def initialize(
16
- in_stream: STDIN,
17
- out_stream: STDOUT,
18
- err_stream: STDERR,
19
- should_raise_errors: false
16
+ messenger:,
17
+ should_raise_errors: false,
18
+ number_of_threads: 2
20
19
  )
21
- validate!([in_stream, out_stream, err_stream])
20
+ # This is what does the IO
21
+ @messenger = messenger
22
22
 
23
- @handler = Handler.new(self)
24
- @in = in_stream
25
- @out = out_stream
26
- @err = err_stream
23
+ # This is what you use to communicate with the language client
24
+ @bridge = Bridge.new(@messenger)
27
25
 
28
- # Because programming is fun,
29
- #
30
- # Ruby on Windows turns \n into \r\n. Which means that \r\n
31
- # gets turned into \r\r\n. Which means that the protocol
32
- # breaks on windows unless we turn STDOUT into binary mode.
33
- #
34
- # Hours wasted: 9.
35
- @out.binmode
26
+ # The handler handles messages from the language client
27
+ @handler = Handler.new(@bridge)
36
28
 
37
- @out.sync = true # do not buffer
38
- @err.sync = true # do not buffer
29
+ # The queue holds the JSON RPC messages
30
+ @queue = Queue.new
39
31
 
40
- @should_raise_errors = should_raise_errors
41
- end
32
+ # The JSON RPC thread pushes messages onto the queue
33
+ @json_rpc_thread = nil
42
34
 
43
- def listen
44
- loop do
45
- process_request
46
-
47
- # support ctrl+c and stuff
48
- rescue SignalException, DoneStreaming
49
- cleanup
50
- return 0
51
-
52
- rescue Exception => e # rubocop:disable Lint/RescueException
53
- raise e if should_raise_errors
54
- log(e)
55
- log(e.backtrace)
56
- return 1
57
- end
58
- end
35
+ # The handler threads read messages from the queue
36
+ @number_of_threads = number_of_threads
37
+ @handlers = []
59
38
 
60
- def send_response(response)
61
- response_body = JSON.dump(response)
62
- log(JSON.pretty_generate(response)) if $DEBUG
39
+ # The error queue holds blocks the main thread. When filled, we exit the program.
40
+ @error = SizedQueue.new(number_of_threads)
63
41
 
64
- @out.write("Content-Length: #{response_body.bytesize}\r\n")
65
- @out.write("\r\n")
66
- @out.write(response_body)
67
- @out.flush
42
+ @should_raise_errors = should_raise_errors
68
43
  end
69
44
 
70
- def log(message)
71
- @err.puts(message)
72
- @err.flush
45
+ def listen
46
+ start_handler_threads
47
+ start_json_rpc_thread
48
+ status_code = status_code_from_error(@error.pop)
49
+ cleanup(status_code)
50
+ rescue SignalException
51
+ 0
73
52
  end
74
53
 
75
- private
76
-
77
- def supported_io_classes
78
- [IO, StringIO]
54
+ def start_json_rpc_thread
55
+ @json_rpc_thread = Thread.new do
56
+ loop do
57
+ message = @bridge.read_message
58
+ if message['method'] == 'initialize'
59
+ handle_message(message)
60
+ elsif message.key?('result')
61
+ # Responses are handled on the main thread to prevent
62
+ # a potential deadlock caused by all handlers waiting
63
+ # for a responses.
64
+ handle_response(message)
65
+ else
66
+ @queue << message
67
+ end
68
+ rescue Exception => e # rubocop:disable Lint/RescueException
69
+ break @error << e
70
+ end
71
+ end
79
72
  end
80
73
 
81
- def validate!(streams = [])
82
- streams.each do |stream|
83
- unless supported_io_classes.find { |klass| stream.is_a?(klass) }
84
- raise IncompatibleStream, incompatible_stream_message
74
+ def start_handler_threads
75
+ @number_of_threads.times do
76
+ @handlers << Thread.new do
77
+ loop do
78
+ message = @queue.pop
79
+ break if @queue.closed? && @queue.empty?
80
+ handle_message(message)
81
+ rescue Exception => e # rubocop:disable Lint/RescueException
82
+ break @error << e
83
+ end
85
84
  end
86
85
  end
87
86
  end
88
87
 
89
- def incompatible_stream_message
90
- 'if provided, in_stream, out_stream, and err_stream must be a kind of '\
91
- "one of the following: #{supported_io_classes.join(', ')}"
88
+ def status_code_from_error(e)
89
+ raise e
90
+
91
+ # support ctrl+c and stuff
92
+ rescue SignalException, DoneStreaming
93
+ 0
94
+
95
+ rescue Exception => e # rubocop:disable Lint/RescueException
96
+ raise e if should_raise_errors
97
+ @bridge.log("#{e.class}: #{e.message}\n#{e.backtrace.join("\n")}")
98
+ 2
92
99
  end
93
100
 
94
- def process_request
95
- request_body = read_new_content
96
- request_json = JSON.parse(request_body)
97
- log(JSON.pretty_generate(request_json)) if $DEBUG
101
+ private
98
102
 
99
- id = request_json['id']
100
- method_name = request_json['method']
101
- params = request_json['params']
102
- method_name = "on_#{to_snake_case(method_name)}"
103
+ def handle_message(message)
104
+ id = message['id']
105
+ method_name = message['method']
106
+ method_name &&= "on_#{to_snake_case(method_name)}"
107
+ params = message['params']
103
108
 
104
109
  if @handler.respond_to?(method_name)
105
110
  @handler.send(method_name, id, params)
106
111
  end
107
- end
108
112
 
109
- def to_snake_case(method_name)
110
- StringHelpers.underscore(method_name.gsub(/[^\w]/, '_'))
113
+ rescue DoneStreaming => e
114
+ raise e
115
+ rescue StandardError => e
116
+ is_request = id
117
+ raise e unless is_request
118
+ # Errors obtained in request handlers should be sent
119
+ # back as internal errors instead of closing the program.
120
+ @bridge.send_internal_error(id, e)
111
121
  end
112
122
 
113
- def initial_line
114
- # Scanning for lines that fit the protocol.
115
- while true
116
- initial_line = @in.gets
117
- # gets returning nil means the stream was closed.
118
- raise DoneStreaming if initial_line.nil?
119
-
120
- if initial_line.match(/Content-Length: (\d+)/)
121
- break
122
- end
123
- end
124
- initial_line
123
+ def handle_response(message)
124
+ id = message['id']
125
+ result = message['result']
126
+ @bridge.receive_response(id, result)
125
127
  end
126
128
 
127
- def read_new_content
128
- length = initial_line.match(/Content-Length: (\d+)/)[1].to_i
129
- content = ''
130
- while content.length < length + 2
131
- begin
132
- # Why + 2? Because \r\n
133
- content += @in.read(length + 2)
134
- rescue => e
135
- log(e)
136
- log(e.backtrace)
137
- # We have almost certainly been disconnected from the server
138
- cleanup
139
- raise DoneStreaming
140
- end
141
- end
142
-
143
- content
129
+ def to_snake_case(method_name)
130
+ StringHelpers.underscore(method_name.gsub(/[^\w]/, '_'))
144
131
  end
145
132
 
146
- def cleanup
147
- @err.close
148
- @out.close
149
- rescue
150
- # I did my best
133
+ def cleanup(status_code)
134
+ # Stop listenting to RPC calls
135
+ @messenger.close_input
136
+ # Wait for rpc loop to close
137
+ @json_rpc_thread&.join if @json_rpc_thread&.alive?
138
+ # Close the queue
139
+ @queue.close unless @queue.closed?
140
+ # Give 10 seconds for the handlers to wrap up what they were
141
+ # doing/emptying the queue. 👀 unit tests.
142
+ @handlers.each { |thread| thread.join(10) if thread.alive? }
143
+
144
+ # Hijack the status_code if an error occurred while cleaning up.
145
+ # 👀 unit tests.
146
+ until @error.empty?
147
+ code = status_code_from_error(@error.pop)
148
+ # Promote the status_code to ERROR if one of the threads
149
+ # resulted in an error, otherwise leave the status_code as
150
+ # is. That's because one thread could end successfully in a
151
+ # DoneStreaming error while the other failed with an
152
+ # internal error. If we had an internal error, we should
153
+ # return with a status_code that fits.
154
+ status_code = code if code > status_code
155
+ end
156
+ status_code
157
+ ensure
158
+ @messenger.close_output
151
159
  end
152
160
  end
153
161
  end
@@ -1,6 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
  require_relative "language_server/protocol"
3
3
  require_relative "language_server/constants"
4
+ require_relative "language_server/channel"
5
+ require_relative "language_server/messenger"
6
+ require_relative "language_server/io_messenger"
7
+ require_relative "language_server/bridge"
4
8
  require_relative "language_server/uri_helper"
5
9
  require_relative "language_server/handler"
6
10
  require_relative "language_server/server"
@@ -12,6 +16,7 @@ require_relative "language_server/completion_engine"
12
16
  require_relative "language_server/document_link_provider"
13
17
  require_relative "language_server/document_link_engine"
14
18
  require_relative "language_server/diagnostics_tracker"
19
+ require_relative "language_server/diagnostics_engine"
15
20
 
16
21
  Dir[__dir__ + "/language_server/completion_providers/*.rb"].each do |file|
17
22
  require file
@@ -24,7 +29,7 @@ end
24
29
  module ThemeCheck
25
30
  module LanguageServer
26
31
  def self.start
27
- Server.new.listen
32
+ Server.new(messenger: IOMessenger.new).listen
28
33
  end
29
34
  end
30
35
  end
@@ -74,6 +74,34 @@ module ThemeCheck
74
74
  position.end_index
75
75
  end
76
76
 
77
+ def start_token_index
78
+ return position.start_index if inside_liquid_tag?
79
+ position.start_index - (start_token.length + 1)
80
+ end
81
+
82
+ def end_token_index
83
+ return position.end_index if inside_liquid_tag?
84
+ position.end_index + end_token.length
85
+ end
86
+
87
+ def render_start_tag
88
+ "#{start_token} #{@value.raw}#{end_token}"
89
+ end
90
+
91
+ def render_end_tag
92
+ "#{start_token} #{@value.block_delimiter} #{end_token}"
93
+ end
94
+
95
+ def block_body_start_index
96
+ return unless block_tag?
97
+ block_regex.begin(:body)
98
+ end
99
+
100
+ def block_body_end_index
101
+ return unless block_tag?
102
+ block_regex.end(:body)
103
+ end
104
+
77
105
  # Literals are hard-coded values in the liquid file.
78
106
  def literal?
79
107
  @value.is_a?(String) || @value.is_a?(Integer)
@@ -184,6 +212,11 @@ module ThemeCheck
184
212
 
185
213
  private
186
214
 
215
+ def block_regex
216
+ return unless block_tag?
217
+ /(?<start_token>#{render_start_tag})(?<body>.*)(?<end_token>#{render_end_tag})/m.match(source)
218
+ end
219
+
187
220
  def position
188
221
  @position ||= Position.new(
189
222
  markup,
@@ -14,24 +14,43 @@ module ThemeCheck
14
14
  visit_object(@default, @other, [])
15
15
  end
16
16
 
17
- def add_as_offenses(check, key_prefix: [], node: nil, theme_file: nil)
17
+ def add_as_offenses(check, key_prefix: [], node: nil, theme_file: nil, schema: {})
18
18
  if extra_keys.any?
19
- add_keys_offense(check, "Extra translation keys", extra_keys,
20
- key_prefix: key_prefix, node: node, theme_file: theme_file)
19
+ remove_extra_keys_offense(check, "Extra translation keys", extra_keys,
20
+ key_prefix: key_prefix, node: node, theme_file: theme_file, schema: schema)
21
21
  end
22
22
 
23
23
  if missing_keys.any?
24
- add_keys_offense(check, "Missing translation keys", missing_keys,
25
- key_prefix: key_prefix, node: node, theme_file: theme_file)
24
+ add_missing_keys_offense(check, "Missing translation keys", missing_keys,
25
+ key_prefix: key_prefix, node: node, theme_file: theme_file, schema: schema)
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, theme_file: nil)
32
- message = "#{cause}: #{format_keys(key_prefix, keys)}"
31
+ def remove_extra_keys_offense(check, cause, extra_keys, key_prefix:, node: nil, theme_file: nil, schema: {})
32
+ message = "#{cause}: #{format_keys(key_prefix, extra_keys)}"
33
33
  if node
34
- check.add_offense(message, node: node)
34
+ check.add_offense(message, node: node) do |corrector|
35
+ extra_keys.each do |k|
36
+ corrector.remove_key(schema, key_prefix + k)
37
+ end
38
+ corrector.replace_block_body(node, schema)
39
+ end
40
+ else
41
+ check.add_offense(message, theme_file: theme_file)
42
+ end
43
+ end
44
+
45
+ def add_missing_keys_offense(check, cause, missing_keys, key_prefix:, node: nil, theme_file: nil, schema: {})
46
+ message = "#{cause}: #{format_keys(key_prefix, missing_keys)}"
47
+ if node
48
+ check.add_offense(message, node: node) do |corrector|
49
+ missing_keys.each do |k|
50
+ corrector.add_key(schema, key_prefix + k, "TODO")
51
+ end
52
+ corrector.replace_block_body(node, schema)
53
+ end
35
54
  else
36
55
  check.add_offense(message, theme_file: theme_file)
37
56
  end
@@ -46,10 +65,12 @@ module ThemeCheck
46
65
  other = {} unless other.is_a?(Hash)
47
66
  return if pluralization?(default) && pluralization?(other)
48
67
 
49
- @extra_keys += (other.keys - default.keys).map { |key| path + [key] }
68
+ shopify_translations = system_translations(path)
69
+
70
+ @extra_keys += (other.keys - default.keys - shopify_translations.keys).map { |key| path + [key] }
50
71
 
51
72
  default.each do |key, default_value|
52
- translated_value = other[key]
73
+ translated_value = other[key] || shopify_translations[key]
53
74
  new_path = path + [key]
54
75
 
55
76
  if translated_value.nil?
@@ -65,5 +86,10 @@ module ThemeCheck
65
86
  PLURALIZATION_KEYS.include?(key) && !value.is_a?(Hash)
66
87
  end
67
88
  end
89
+
90
+ def system_translations(path)
91
+ return ShopifyLiquid::SystemTranslations.translations_hash if path.empty?
92
+ ShopifyLiquid::SystemTranslations.translations_hash.dig(*path) || {}
93
+ end
68
94
  end
69
95
  end
@@ -64,6 +64,10 @@ module ThemeCheck
64
64
  strict_position.end_column
65
65
  end
66
66
 
67
+ def content_line_count
68
+ @content_line_count ||= contents.count("\n")
69
+ end
70
+
67
71
  private
68
72
 
69
73
  def compute_start_offset
@@ -78,10 +82,6 @@ module ThemeCheck
78
82
  @contents
79
83
  end
80
84
 
81
- def content_line_count
82
- @content_line_count ||= contents.count("\n")
83
- end
84
-
85
85
  def line_number
86
86
  return 0 if @line_number_1_indexed.nil?
87
87
  bounded(0, @line_number_1_indexed - 1, content_line_count)
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+ module ThemeCheck
3
+ module ShopifyLiquid
4
+ module SystemTranslations
5
+ extend self
6
+
7
+ def translations
8
+ @translations ||= YAML.load(File.read("#{__dir__}/../../../data/shopify_translation_keys.yml")).to_set
9
+ end
10
+
11
+ def translations_hash
12
+ @translations_hash ||= translations.reduce({}) do |acc, k|
13
+ dig_set(acc, k.split('.'), "")
14
+ end
15
+ end
16
+
17
+ def include?(key)
18
+ translations.include?(key)
19
+ end
20
+
21
+ private
22
+
23
+ def dig_set(obj, keys, value)
24
+ key = keys.first
25
+ if keys.length == 1
26
+ obj[key] = value
27
+ else
28
+ obj[key] = {} unless obj[key]
29
+ dig_set(obj[key], keys.slice(1..-1), value)
30
+ end
31
+ obj
32
+ end
33
+ end
34
+ end
35
+ end
@@ -7,10 +7,17 @@ module ThemeCheck
7
7
  extend self
8
8
 
9
9
  def labels
10
- @tags ||= YAML.load(File.read("#{__dir__}/../../../data/shopify_liquid/tags.yml"))
10
+ @labels ||= tags_file_contents
11
+ .map { |x| to_label(x) }
11
12
  .to_set
12
13
  end
13
14
 
15
+ def end_labels
16
+ @end_labels ||= tags_file_contents
17
+ .select { |x| x.is_a?(Hash) }
18
+ .map { |x| x.values[0] }
19
+ end
20
+
14
21
  def tag_regex(tag)
15
22
  return unless labels.include?(tag)
16
23
  @tag_regexes ||= {}
@@ -22,6 +29,17 @@ module ThemeCheck
22
29
  @tag_liquid_regexes ||= {}
23
30
  @tag_liquid_regexes[tag] ||= /^\s*#{tag}/m
24
31
  end
32
+
33
+ private
34
+
35
+ def to_label(label)
36
+ return label if label.is_a?(String)
37
+ label.keys[0]
38
+ end
39
+
40
+ def tags_file_contents
41
+ @tags_file_contents ||= YAML.load(File.read("#{__dir__}/../../../data/shopify_liquid/tags.yml"))
42
+ end
25
43
  end
26
44
  end
27
45
  end
@@ -3,3 +3,4 @@ require_relative 'shopify_liquid/deprecated_filter'
3
3
  require_relative 'shopify_liquid/filter'
4
4
  require_relative 'shopify_liquid/object'
5
5
  require_relative 'shopify_liquid/tag'
6
+ require_relative 'shopify_liquid/system_translations'
@@ -66,7 +66,6 @@ module ThemeCheck
66
66
 
67
67
  def initialize(tag_name, markup, options)
68
68
  super
69
-
70
69
  if (matches = markup.match(SYNTAX))
71
70
  @liquid_variable_name = matches[:liquid_variable_name]
72
71
  @page_size = parse_expression(matches[:page_size])
@@ -25,6 +25,12 @@ module ThemeCheck
25
25
  )
26
26
  end
27
27
 
28
+ def remove(node)
29
+ @rewriter.remove(
30
+ range(node.start_token_index, node.end_token_index)
31
+ )
32
+ end
33
+
28
34
  def replace(node, content)
29
35
  @rewriter.replace(
30
36
  range(node.start_index, node.end_index),
@@ -32,6 +38,13 @@ module ThemeCheck
32
38
  )
33
39
  end
34
40
 
41
+ def replace_body(node, content)
42
+ @rewriter.replace(
43
+ range(node.block_body_start_index, node.block_body_end_index),
44
+ content
45
+ )
46
+ end
47
+
35
48
  def wrap(node, insert_before, insert_after)
36
49
  @rewriter.wrap(
37
50
  range(node.start_index, node.end_index),
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module ThemeCheck
3
- VERSION = "1.6.2"
3
+ VERSION = "1.8.0"
4
4
  end
data/lib/theme_check.rb CHANGED
@@ -51,6 +51,10 @@ Encoding.default_external = Encoding::UTF_8
51
51
  Encoding.default_internal = Encoding::UTF_8
52
52
 
53
53
  module ThemeCheck
54
+ def self.debug?
55
+ ENV["THEME_CHECK_DEBUG"] == "true"
56
+ end
57
+
54
58
  def self.with_liquid_c_disabled
55
59
  if defined?(Liquid::C)
56
60
  was_enabled = Liquid::C.enabled
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: theme-check
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.6.2
4
+ version: 1.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marc-André Cournoyer
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-09-16 00:00:00.000000000 Z
11
+ date: 2021-11-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: liquid
@@ -201,6 +201,8 @@ files:
201
201
  - lib/theme_check/json_helpers.rb
202
202
  - lib/theme_check/json_printer.rb
203
203
  - lib/theme_check/language_server.rb
204
+ - lib/theme_check/language_server/bridge.rb
205
+ - lib/theme_check/language_server/channel.rb
204
206
  - lib/theme_check/language_server/completion_engine.rb
205
207
  - lib/theme_check/language_server/completion_helper.rb
206
208
  - lib/theme_check/language_server/completion_provider.rb
@@ -209,6 +211,7 @@ files:
209
211
  - lib/theme_check/language_server/completion_providers/render_snippet_completion_provider.rb
210
212
  - lib/theme_check/language_server/completion_providers/tag_completion_provider.rb
211
213
  - lib/theme_check/language_server/constants.rb
214
+ - lib/theme_check/language_server/diagnostics_engine.rb
212
215
  - lib/theme_check/language_server/diagnostics_tracker.rb
213
216
  - lib/theme_check/language_server/document_link_engine.rb
214
217
  - lib/theme_check/language_server/document_link_provider.rb
@@ -217,6 +220,8 @@ files:
217
220
  - lib/theme_check/language_server/document_link_providers/render_document_link_provider.rb
218
221
  - lib/theme_check/language_server/document_link_providers/section_document_link_provider.rb
219
222
  - lib/theme_check/language_server/handler.rb
223
+ - lib/theme_check/language_server/io_messenger.rb
224
+ - lib/theme_check/language_server/messenger.rb
220
225
  - lib/theme_check/language_server/protocol.rb
221
226
  - lib/theme_check/language_server/server.rb
222
227
  - lib/theme_check/language_server/tokens.rb
@@ -241,6 +246,7 @@ files:
241
246
  - lib/theme_check/shopify_liquid/deprecated_filter.rb
242
247
  - lib/theme_check/shopify_liquid/filter.rb
243
248
  - lib/theme_check/shopify_liquid/object.rb
249
+ - lib/theme_check/shopify_liquid/system_translations.rb
244
250
  - lib/theme_check/shopify_liquid/tag.rb
245
251
  - lib/theme_check/storage.rb
246
252
  - lib/theme_check/string_helpers.rb