theme-check 1.6.0 → 1.7.1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +27 -0
- data/data/shopify_liquid/tags.yml +9 -9
- data/docs/api/html_check.md +7 -7
- data/docs/api/liquid_check.md +10 -10
- data/docs/checks/convert_include_to_render.md +1 -1
- data/docs/checks/missing_enable_comment.md +1 -1
- data/lib/theme_check/analyzer.rb +41 -17
- data/lib/theme_check/asset_file.rb +1 -1
- data/lib/theme_check/check.rb +2 -2
- data/lib/theme_check/checks/html_parsing_error.rb +2 -2
- data/lib/theme_check/checks/matching_translations.rb +1 -1
- data/lib/theme_check/checks/missing_template.rb +6 -6
- data/lib/theme_check/checks/nested_snippet.rb +2 -2
- data/lib/theme_check/checks/required_layout_theme_object.rb +2 -2
- data/lib/theme_check/checks/syntax_error.rb +5 -5
- data/lib/theme_check/checks/template_length.rb +2 -2
- data/lib/theme_check/checks/translation_key_exists.rb +1 -13
- data/lib/theme_check/checks/undefined_object.rb +7 -7
- data/lib/theme_check/checks/unused_assign.rb +4 -4
- data/lib/theme_check/checks/unused_snippet.rb +7 -7
- data/lib/theme_check/checks/valid_json.rb +1 -1
- data/lib/theme_check/checks.rb +4 -2
- data/lib/theme_check/cli.rb +1 -1
- data/lib/theme_check/corrector.rb +6 -6
- data/lib/theme_check/disabled_check.rb +3 -3
- data/lib/theme_check/disabled_checks.rb +9 -9
- data/lib/theme_check/exceptions.rb +1 -0
- data/lib/theme_check/file_system_storage.rb +4 -0
- data/lib/theme_check/html_node.rb +36 -28
- data/lib/theme_check/html_visitor.rb +6 -6
- data/lib/theme_check/in_memory_storage.rb +1 -1
- data/lib/theme_check/json_check.rb +2 -2
- data/lib/theme_check/language_server/bridge.rb +128 -0
- data/lib/theme_check/language_server/channel.rb +69 -0
- data/lib/theme_check/language_server/completion_providers/tag_completion_provider.rb +3 -1
- data/lib/theme_check/language_server/diagnostics_engine.rb +125 -0
- data/lib/theme_check/language_server/diagnostics_tracker.rb +8 -8
- data/lib/theme_check/language_server/handler.rb +20 -117
- data/lib/theme_check/language_server/io_messenger.rb +97 -0
- data/lib/theme_check/language_server/messenger.rb +27 -0
- data/lib/theme_check/language_server/server.rb +95 -104
- data/lib/theme_check/language_server.rb +6 -1
- data/lib/theme_check/{template.rb → liquid_file.rb} +2 -2
- data/lib/theme_check/liquid_node.rb +291 -0
- data/lib/theme_check/{visitor.rb → liquid_visitor.rb} +4 -4
- data/lib/theme_check/locale_diff.rb +14 -7
- data/lib/theme_check/node.rb +12 -225
- data/lib/theme_check/offense.rb +15 -15
- data/lib/theme_check/position.rb +1 -1
- data/lib/theme_check/shopify_liquid/system_translations.rb +35 -0
- data/lib/theme_check/shopify_liquid/tag.rb +19 -1
- data/lib/theme_check/shopify_liquid.rb +1 -0
- data/lib/theme_check/theme.rb +1 -1
- data/lib/theme_check/{template_rewriter.rb → theme_file_rewriter.rb} +1 -1
- data/lib/theme_check/version.rb +1 -1
- data/lib/theme_check.rb +11 -10
- data/theme-check.gemspec +1 -1
- metadata +14 -7
@@ -7,6 +7,11 @@ module ThemeCheck
|
|
7
7
|
class Handler
|
8
8
|
include URIHelper
|
9
9
|
|
10
|
+
SERVER_INFO = {
|
11
|
+
name: $PROGRAM_NAME,
|
12
|
+
version: ThemeCheck::VERSION,
|
13
|
+
}
|
14
|
+
|
10
15
|
CAPABILITIES = {
|
11
16
|
completionProvider: {
|
12
17
|
triggerCharacters: ['.', '{{ ', '{% '],
|
@@ -21,22 +26,24 @@ module ThemeCheck
|
|
21
26
|
},
|
22
27
|
}
|
23
28
|
|
24
|
-
def initialize(
|
25
|
-
@
|
26
|
-
@diagnostics_tracker = DiagnosticsTracker.new
|
29
|
+
def initialize(bridge)
|
30
|
+
@bridge = bridge
|
27
31
|
end
|
28
32
|
|
29
33
|
def on_initialize(id, params)
|
30
34
|
@root_path = root_path_from_params(params)
|
31
35
|
|
32
36
|
# Tell the client we don't support anything if there's no rootPath
|
33
|
-
return send_response(id, { capabilities: {} }) if @root_path.nil?
|
37
|
+
return @bridge.send_response(id, { capabilities: {} }) if @root_path.nil?
|
38
|
+
|
39
|
+
@bridge.supports_work_done_progress = params.dig('capabilities', 'window', 'workDoneProgress') || false
|
34
40
|
@storage = in_memory_storage(@root_path)
|
35
41
|
@completion_engine = CompletionEngine.new(@storage)
|
36
42
|
@document_link_engine = DocumentLinkEngine.new(@storage)
|
37
|
-
|
38
|
-
send_response(id, {
|
43
|
+
@diagnostics_engine = DiagnosticsEngine.new(@bridge)
|
44
|
+
@bridge.send_response(id, {
|
39
45
|
capabilities: CAPABILITIES,
|
46
|
+
serverInfo: SERVER_INFO,
|
40
47
|
})
|
41
48
|
end
|
42
49
|
|
@@ -58,7 +65,7 @@ module ThemeCheck
|
|
58
65
|
def on_text_document_did_open(_id, params)
|
59
66
|
relative_path = relative_path_from_text_document_uri(params)
|
60
67
|
@storage.write(relative_path, text_document_text(params))
|
61
|
-
analyze_and_send_offenses(text_document_uri(params)) if @
|
68
|
+
analyze_and_send_offenses(text_document_uri(params)) if @diagnostics_engine.first_run?
|
62
69
|
end
|
63
70
|
|
64
71
|
def on_text_document_did_save(_id, params)
|
@@ -67,14 +74,14 @@ module ThemeCheck
|
|
67
74
|
|
68
75
|
def on_text_document_document_link(id, params)
|
69
76
|
relative_path = relative_path_from_text_document_uri(params)
|
70
|
-
send_response(id, document_links(relative_path))
|
77
|
+
@bridge.send_response(id, document_links(relative_path))
|
71
78
|
end
|
72
79
|
|
73
80
|
def on_text_document_completion(id, params)
|
74
81
|
relative_path = relative_path_from_text_document_uri(params)
|
75
82
|
line = params.dig('position', 'line')
|
76
83
|
col = params.dig('position', 'character')
|
77
|
-
send_response(id, completions(relative_path, line, col))
|
84
|
+
@bridge.send_response(id, completions(relative_path, line, col))
|
78
85
|
end
|
79
86
|
|
80
87
|
private
|
@@ -128,38 +135,10 @@ module ThemeCheck
|
|
128
135
|
end
|
129
136
|
|
130
137
|
def analyze_and_send_offenses(absolute_path)
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
ignored_patterns: config.ignored_patterns
|
138
|
+
@diagnostics_engine.analyze_and_send_offenses(
|
139
|
+
absolute_path,
|
140
|
+
config_for_path(absolute_path)
|
135
141
|
)
|
136
|
-
theme = ThemeCheck::Theme.new(storage)
|
137
|
-
analyzer = ThemeCheck::Analyzer.new(theme, config.enabled_checks)
|
138
|
-
|
139
|
-
if @diagnostics_tracker.first_run?
|
140
|
-
# Analyze the full theme on first run
|
141
|
-
log("Checking #{config.root}")
|
142
|
-
offenses = nil
|
143
|
-
time = Benchmark.measure do
|
144
|
-
offenses = analyzer.analyze_theme
|
145
|
-
end
|
146
|
-
log("Found #{offenses.size} offenses in #{format("%0.2f", time.real)}s")
|
147
|
-
send_diagnostics(offenses)
|
148
|
-
else
|
149
|
-
# Analyze selected files
|
150
|
-
relative_path = Pathname.new(@storage.relative_path(absolute_path))
|
151
|
-
file = theme[relative_path]
|
152
|
-
# Skip if not a theme file
|
153
|
-
if file
|
154
|
-
log("Checking #{relative_path}")
|
155
|
-
offenses = nil
|
156
|
-
time = Benchmark.measure do
|
157
|
-
offenses = analyzer.analyze_files([file])
|
158
|
-
end
|
159
|
-
log("Found #{offenses.size} new offenses in #{format("%0.2f", time.real)}s")
|
160
|
-
send_diagnostics(offenses, [absolute_path])
|
161
|
-
end
|
162
|
-
end
|
163
142
|
end
|
164
143
|
|
165
144
|
def completions(relative_path, line, col)
|
@@ -170,84 +149,8 @@ module ThemeCheck
|
|
170
149
|
@document_link_engine.document_links(relative_path)
|
171
150
|
end
|
172
151
|
|
173
|
-
def send_diagnostics(offenses, analyzed_files = nil)
|
174
|
-
@diagnostics_tracker.build_diagnostics(offenses, analyzed_files: analyzed_files) do |path, diagnostic_offenses|
|
175
|
-
send_diagnostic(path, diagnostic_offenses)
|
176
|
-
end
|
177
|
-
end
|
178
|
-
|
179
|
-
def send_diagnostic(path, offenses)
|
180
|
-
# https://microsoft.github.io/language-server-protocol/specifications/specification-current/#notificationMessage
|
181
|
-
send_notification('textDocument/publishDiagnostics', {
|
182
|
-
uri: file_uri(path),
|
183
|
-
diagnostics: offenses.map { |offense| offense_to_diagnostic(offense) },
|
184
|
-
})
|
185
|
-
end
|
186
|
-
|
187
|
-
def offense_to_diagnostic(offense)
|
188
|
-
diagnostic = {
|
189
|
-
code: offense.code_name,
|
190
|
-
message: offense.message,
|
191
|
-
range: range(offense),
|
192
|
-
severity: severity(offense),
|
193
|
-
source: "theme-check",
|
194
|
-
}
|
195
|
-
diagnostic["codeDescription"] = code_description(offense) unless offense.doc.nil?
|
196
|
-
diagnostic
|
197
|
-
end
|
198
|
-
|
199
|
-
def code_description(offense)
|
200
|
-
{
|
201
|
-
href: offense.doc,
|
202
|
-
}
|
203
|
-
end
|
204
|
-
|
205
|
-
def severity(offense)
|
206
|
-
case offense.severity
|
207
|
-
when :error
|
208
|
-
1
|
209
|
-
when :suggestion
|
210
|
-
2
|
211
|
-
when :style
|
212
|
-
3
|
213
|
-
else
|
214
|
-
4
|
215
|
-
end
|
216
|
-
end
|
217
|
-
|
218
|
-
def range(offense)
|
219
|
-
{
|
220
|
-
start: {
|
221
|
-
line: offense.start_line,
|
222
|
-
character: offense.start_column,
|
223
|
-
},
|
224
|
-
end: {
|
225
|
-
line: offense.end_line,
|
226
|
-
character: offense.end_column,
|
227
|
-
},
|
228
|
-
}
|
229
|
-
end
|
230
|
-
|
231
|
-
def send_message(message)
|
232
|
-
message[:jsonrpc] = '2.0'
|
233
|
-
@server.send_response(message)
|
234
|
-
end
|
235
|
-
|
236
|
-
def send_response(id, result = nil, error = nil)
|
237
|
-
message = { id: id }
|
238
|
-
message[:result] = result if result
|
239
|
-
message[:error] = error if error
|
240
|
-
send_message(message)
|
241
|
-
end
|
242
|
-
|
243
|
-
def send_notification(method, params)
|
244
|
-
message = { method: method }
|
245
|
-
message[:params] = params
|
246
|
-
send_message(message)
|
247
|
-
end
|
248
|
-
|
249
152
|
def log(message)
|
250
|
-
@
|
153
|
+
@bridge.log(message)
|
251
154
|
end
|
252
155
|
|
253
156
|
def close!
|
@@ -0,0 +1,97 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ThemeCheck
|
4
|
+
module LanguageServer
|
5
|
+
class IOMessenger < Messenger
|
6
|
+
def initialize(
|
7
|
+
in_stream: STDIN,
|
8
|
+
out_stream: STDOUT,
|
9
|
+
err_stream: STDERR
|
10
|
+
)
|
11
|
+
validate!([in_stream, out_stream, err_stream])
|
12
|
+
|
13
|
+
@in = in_stream
|
14
|
+
@out = out_stream
|
15
|
+
@err = err_stream
|
16
|
+
|
17
|
+
# Because programming is fun,
|
18
|
+
#
|
19
|
+
# Ruby on Windows turns \n into \r\n. Which means that \r\n
|
20
|
+
# gets turned into \r\r\n. Which means that the protocol
|
21
|
+
# breaks on windows unless we turn STDOUT into binary mode.
|
22
|
+
#
|
23
|
+
# Hours wasted: 9.
|
24
|
+
@out.binmode
|
25
|
+
|
26
|
+
@out.sync = true # do not buffer
|
27
|
+
@err.sync = true # do not buffer
|
28
|
+
end
|
29
|
+
|
30
|
+
def read_message
|
31
|
+
length = initial_line.match(/Content-Length: (\d+)/)[1].to_i
|
32
|
+
content = ''
|
33
|
+
length_to_read = 2 + length # 2 is the empty line length (\r\n)
|
34
|
+
while content.length < length_to_read
|
35
|
+
chunk = @in.read(length_to_read - content.length)
|
36
|
+
raise DoneStreaming if chunk.nil?
|
37
|
+
content += chunk
|
38
|
+
end
|
39
|
+
content.lstrip!
|
40
|
+
end
|
41
|
+
|
42
|
+
def send_message(message_body)
|
43
|
+
@out.write("Content-Length: #{message_body.bytesize}\r\n")
|
44
|
+
@out.write("\r\n")
|
45
|
+
@out.write(message_body)
|
46
|
+
@out.flush
|
47
|
+
end
|
48
|
+
|
49
|
+
def log(message)
|
50
|
+
@err.puts(message)
|
51
|
+
@err.flush
|
52
|
+
end
|
53
|
+
|
54
|
+
def close_input
|
55
|
+
@in.close unless @in.closed?
|
56
|
+
end
|
57
|
+
|
58
|
+
def close_output
|
59
|
+
@err.close
|
60
|
+
@out.close
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def initial_line
|
66
|
+
# Scanning for lines that fit the protocol.
|
67
|
+
while true
|
68
|
+
initial_line = @in.gets
|
69
|
+
# gets returning nil means the stream was closed.
|
70
|
+
raise DoneStreaming if initial_line.nil?
|
71
|
+
|
72
|
+
if initial_line.match(/Content-Length: (\d+)/)
|
73
|
+
break
|
74
|
+
end
|
75
|
+
end
|
76
|
+
initial_line
|
77
|
+
end
|
78
|
+
|
79
|
+
def supported_io_classes
|
80
|
+
[IO, StringIO]
|
81
|
+
end
|
82
|
+
|
83
|
+
def validate!(streams = [])
|
84
|
+
streams.each do |stream|
|
85
|
+
unless supported_io_classes.find { |klass| stream.is_a?(klass) }
|
86
|
+
raise IncompatibleStream, incompatible_stream_message
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def incompatible_stream_message
|
92
|
+
'if provided, in_stream, out_stream, and err_stream must be a kind of '\
|
93
|
+
"one of the following: #{supported_io_classes.join(', ')}"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ThemeCheck
|
4
|
+
module LanguageServer
|
5
|
+
class Messenger
|
6
|
+
def send_message
|
7
|
+
raise NotImplementedError
|
8
|
+
end
|
9
|
+
|
10
|
+
def read_message
|
11
|
+
raise NotImplementedError
|
12
|
+
end
|
13
|
+
|
14
|
+
def log
|
15
|
+
raise NotImplementedError
|
16
|
+
end
|
17
|
+
|
18
|
+
def close_input
|
19
|
+
raise NotImplementedError
|
20
|
+
end
|
21
|
+
|
22
|
+
def close_output
|
23
|
+
raise NotImplementedError
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -13,141 +13,132 @@ module ThemeCheck
|
|
13
13
|
attr_reader :should_raise_errors
|
14
14
|
|
15
15
|
def initialize(
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
should_raise_errors: false
|
16
|
+
messenger:,
|
17
|
+
should_raise_errors: false,
|
18
|
+
number_of_threads: 2
|
20
19
|
)
|
21
|
-
|
20
|
+
# This is what does the IO
|
21
|
+
@messenger = messenger
|
22
22
|
|
23
|
-
|
24
|
-
@
|
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
|
-
#
|
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
|
-
|
38
|
-
@
|
29
|
+
# The queue holds the JSON RPC messages
|
30
|
+
@queue = Queue.new
|
39
31
|
|
40
|
-
|
41
|
-
|
32
|
+
# The JSON RPC thread pushes messages onto the queue
|
33
|
+
@json_rpc_thread = nil
|
42
34
|
|
43
|
-
|
44
|
-
|
45
|
-
|
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
|
-
|
61
|
-
|
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(1)
|
63
41
|
|
64
|
-
@
|
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
|
71
|
-
|
72
|
-
|
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
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
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
|
82
|
-
|
83
|
-
|
84
|
-
|
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
|
90
|
-
|
91
|
-
|
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)
|
98
|
+
@bridge.log(e.backtrace)
|
99
|
+
2
|
92
100
|
end
|
93
101
|
|
94
|
-
|
95
|
-
request_body = read_new_content
|
96
|
-
request_json = JSON.parse(request_body)
|
97
|
-
log(JSON.pretty_generate(request_json)) if $DEBUG
|
102
|
+
private
|
98
103
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
method_name
|
104
|
+
def handle_message(message)
|
105
|
+
id = message['id']
|
106
|
+
method_name = message['method']
|
107
|
+
method_name &&= "on_#{to_snake_case(method_name)}"
|
108
|
+
params = message['params']
|
103
109
|
|
104
110
|
if @handler.respond_to?(method_name)
|
105
111
|
@handler.send(method_name, id, params)
|
106
112
|
end
|
107
113
|
end
|
108
114
|
|
109
|
-
def
|
110
|
-
|
115
|
+
def handle_response(message)
|
116
|
+
id = message['id']
|
117
|
+
result = message['result']
|
118
|
+
@bridge.receive_response(id, result)
|
111
119
|
end
|
112
120
|
|
113
|
-
def
|
114
|
-
|
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
|
125
|
-
end
|
126
|
-
|
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
|
121
|
+
def to_snake_case(method_name)
|
122
|
+
StringHelpers.underscore(method_name.gsub(/[^\w]/, '_'))
|
144
123
|
end
|
145
124
|
|
146
|
-
def cleanup
|
147
|
-
|
148
|
-
@
|
149
|
-
|
150
|
-
|
125
|
+
def cleanup(status_code)
|
126
|
+
# Stop listenting to RPC calls
|
127
|
+
@messenger.close_input
|
128
|
+
# Wait for rpc loop to close
|
129
|
+
@json_rpc_thread&.join if @json_rpc_thread&.alive?
|
130
|
+
# Close the queue
|
131
|
+
@queue.close unless @queue.closed?
|
132
|
+
# Give 10 seconds for the handlers to wrap up what they were
|
133
|
+
# doing/emptying the queue. 👀 unit tests.
|
134
|
+
@handlers.each { |thread| thread.join(10) if thread.alive? }
|
135
|
+
|
136
|
+
# Hijack the status_code if an error occurred while cleaning up.
|
137
|
+
# 👀 unit tests.
|
138
|
+
return status_code_from_error(@error.pop) unless @error.empty?
|
139
|
+
status_code
|
140
|
+
ensure
|
141
|
+
@messenger.close_output
|
151
142
|
end
|
152
143
|
end
|
153
144
|
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
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ThemeCheck
|
4
|
-
class
|
4
|
+
class LiquidFile < ThemeFile
|
5
5
|
def write
|
6
6
|
content = rewriter.to_s
|
7
7
|
if source != content
|
@@ -28,7 +28,7 @@ module ThemeCheck
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def rewriter
|
31
|
-
@rewriter ||=
|
31
|
+
@rewriter ||= ThemeFileRewriter.new(@relative_path, source)
|
32
32
|
end
|
33
33
|
|
34
34
|
def source_excerpt(line)
|