platformos-check 0.0.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 +7 -0
- data/.dockerignore +2 -0
- data/.gitignore +22 -0
- data/.rubocop.yml +555 -0
- data/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/CONTRIBUTING.md +209 -0
- data/Gemfile +33 -0
- data/Guardfile +7 -0
- data/LICENSE.md +10 -0
- data/Makefile +18 -0
- data/README.md +189 -0
- data/RELEASING.md +35 -0
- data/Rakefile +83 -0
- data/TROUBLESHOOTING.md +35 -0
- data/bin/platformos-check +29 -0
- data/bin/platformos-check-language-server +29 -0
- data/config/default.yml +98 -0
- data/config/nothing.yml +11 -0
- data/data/platformos_liquid/built_in_liquid_objects.json +66 -0
- data/data/platformos_liquid/deprecated_filters.json +22 -0
- data/data/platformos_liquid/documentation/filters.json +6 -0
- data/data/platformos_liquid/documentation/latest.json +2 -0
- data/data/platformos_liquid/documentation/objects.json +6 -0
- data/data/platformos_liquid/documentation/tags.json +6 -0
- data/docker/check.Dockerfile +3 -0
- data/docker/lsp.Dockerfile +21 -0
- data/docs/api/check.md +15 -0
- data/docs/api/html_check.md +46 -0
- data/docs/api/liquid_check.md +115 -0
- data/docs/api/yaml_check.md +19 -0
- data/docs/checks/TEMPLATE.md.erb +52 -0
- data/docs/checks/convert_include_to_render.md +48 -0
- data/docs/checks/deprecated_filter.md +30 -0
- data/docs/checks/html_parsing_error.md +50 -0
- data/docs/checks/img_lazy_loading.md +63 -0
- data/docs/checks/img_width_and_height.md +79 -0
- data/docs/checks/invalid_args.md +56 -0
- data/docs/checks/liquid_tag.md +65 -0
- data/docs/checks/missing_enable_comment.md +50 -0
- data/docs/checks/missing_template.md +65 -0
- data/docs/checks/parse_json_format.md +76 -0
- data/docs/checks/parser_blocking_javascript.md +97 -0
- data/docs/checks/required_layout_object.md +28 -0
- data/docs/checks/space_inside_braces.md +89 -0
- data/docs/checks/syntax_error.md +49 -0
- data/docs/checks/template_length.md +45 -0
- data/docs/checks/undefined_object.md +71 -0
- data/docs/checks/unknown_filter.md +46 -0
- data/docs/checks/unused_assign.md +63 -0
- data/docs/checks/unused_partial.md +32 -0
- data/docs/checks/valid_yaml.md +48 -0
- data/docs/flamegraph.svg +18488 -0
- data/docs/language_server/code-action-command-palette.png +0 -0
- data/docs/language_server/code-action-flow.png +0 -0
- data/docs/language_server/code-action-keyboard.png +0 -0
- data/docs/language_server/code-action-light-bulb.png +0 -0
- data/docs/language_server/code-action-problem.png +0 -0
- data/docs/language_server/code-action-quickfix.png +0 -0
- data/docs/language_server/how_to_correct_code_with_code_actions_and_execute_command.md +197 -0
- data/docs/preview.png +0 -0
- data/exe/platformos-check +6 -0
- data/exe/platformos-check-language-server +7 -0
- data/lib/platformos_check/analyzer.rb +178 -0
- data/lib/platformos_check/api_call_file.rb +9 -0
- data/lib/platformos_check/app.rb +138 -0
- data/lib/platformos_check/app_file.rb +89 -0
- data/lib/platformos_check/app_file_rewriter.rb +79 -0
- data/lib/platformos_check/asset_file.rb +34 -0
- data/lib/platformos_check/bug.rb +23 -0
- data/lib/platformos_check/check.rb +163 -0
- data/lib/platformos_check/checks/TEMPLATE.rb.erb +11 -0
- data/lib/platformos_check/checks/convert_include_to_render.rb +17 -0
- data/lib/platformos_check/checks/deprecated_filter.rb +123 -0
- data/lib/platformos_check/checks/html_parsing_error.rb +13 -0
- data/lib/platformos_check/checks/img_lazy_loading.rb +18 -0
- data/lib/platformos_check/checks/img_width_and_height.rb +46 -0
- data/lib/platformos_check/checks/invalid_args.rb +81 -0
- data/lib/platformos_check/checks/liquid_tag.rb +47 -0
- data/lib/platformos_check/checks/missing_enable_comment.rb +37 -0
- data/lib/platformos_check/checks/missing_template.rb +107 -0
- data/lib/platformos_check/checks/parse_json_format.rb +31 -0
- data/lib/platformos_check/checks/parser_blocking_javascript.rb +17 -0
- data/lib/platformos_check/checks/required_layout_object.rb +41 -0
- data/lib/platformos_check/checks/space_inside_braces.rb +150 -0
- data/lib/platformos_check/checks/syntax_error.rb +31 -0
- data/lib/platformos_check/checks/template_length.rb +20 -0
- data/lib/platformos_check/checks/undefined_object.rb +206 -0
- data/lib/platformos_check/checks/unknown_filter.rb +27 -0
- data/lib/platformos_check/checks/unused_assign.rb +101 -0
- data/lib/platformos_check/checks/unused_partial.rb +93 -0
- data/lib/platformos_check/checks/valid_yaml.rb +16 -0
- data/lib/platformos_check/checks.rb +73 -0
- data/lib/platformos_check/checks_tracking.rb +9 -0
- data/lib/platformos_check/cli.rb +239 -0
- data/lib/platformos_check/config.rb +219 -0
- data/lib/platformos_check/config_file.rb +6 -0
- data/lib/platformos_check/corrector.rb +68 -0
- data/lib/platformos_check/disabled_check.rb +44 -0
- data/lib/platformos_check/disabled_checks.rb +96 -0
- data/lib/platformos_check/email_file.rb +9 -0
- data/lib/platformos_check/exceptions.rb +36 -0
- data/lib/platformos_check/file_system_storage.rb +93 -0
- data/lib/platformos_check/graphql_file.rb +68 -0
- data/lib/platformos_check/html_check.rb +8 -0
- data/lib/platformos_check/html_node.rb +210 -0
- data/lib/platformos_check/html_visitor.rb +36 -0
- data/lib/platformos_check/in_memory_storage.rb +68 -0
- data/lib/platformos_check/json_file.rb +57 -0
- data/lib/platformos_check/json_helper.rb +73 -0
- data/lib/platformos_check/json_helpers.rb +24 -0
- data/lib/platformos_check/json_printer.rb +32 -0
- data/lib/platformos_check/language_server/bridge.rb +167 -0
- data/lib/platformos_check/language_server/channel.rb +69 -0
- data/lib/platformos_check/language_server/client_capabilities.rb +27 -0
- data/lib/platformos_check/language_server/code_action_engine.rb +32 -0
- data/lib/platformos_check/language_server/code_action_provider.rb +41 -0
- data/lib/platformos_check/language_server/code_action_providers/quickfix_code_action_provider.rb +85 -0
- data/lib/platformos_check/language_server/code_action_providers/source_fix_all_code_action_provider.rb +41 -0
- data/lib/platformos_check/language_server/completion_context.rb +52 -0
- data/lib/platformos_check/language_server/completion_engine.rb +32 -0
- data/lib/platformos_check/language_server/completion_helper.rb +26 -0
- data/lib/platformos_check/language_server/completion_provider.rb +53 -0
- data/lib/platformos_check/language_server/completion_providers/assignments_completion_provider.rb +40 -0
- data/lib/platformos_check/language_server/completion_providers/filter_completion_provider.rb +102 -0
- data/lib/platformos_check/language_server/completion_providers/object_attribute_completion_provider.rb +48 -0
- data/lib/platformos_check/language_server/completion_providers/object_completion_provider.rb +38 -0
- data/lib/platformos_check/language_server/completion_providers/render_snippet_completion_provider.rb +50 -0
- data/lib/platformos_check/language_server/completion_providers/tag_completion_provider.rb +41 -0
- data/lib/platformos_check/language_server/configuration.rb +89 -0
- data/lib/platformos_check/language_server/constants.rb +29 -0
- data/lib/platformos_check/language_server/diagnostic.rb +129 -0
- data/lib/platformos_check/language_server/diagnostics_engine.rb +131 -0
- data/lib/platformos_check/language_server/diagnostics_manager.rb +184 -0
- data/lib/platformos_check/language_server/document_change_corrector.rb +271 -0
- data/lib/platformos_check/language_server/document_link_engine.rb +21 -0
- data/lib/platformos_check/language_server/document_link_provider.rb +71 -0
- data/lib/platformos_check/language_server/document_link_providers/asset_document_link_provider.rb +11 -0
- data/lib/platformos_check/language_server/document_link_providers/include_document_link_provider.rb +11 -0
- data/lib/platformos_check/language_server/document_link_providers/render_document_link_provider.rb +11 -0
- data/lib/platformos_check/language_server/document_link_providers/section_document_link_provider.rb +11 -0
- data/lib/platformos_check/language_server/execute_command_engine.rb +19 -0
- data/lib/platformos_check/language_server/execute_command_provider.rb +30 -0
- data/lib/platformos_check/language_server/execute_command_providers/correction_execute_command_provider.rb +48 -0
- data/lib/platformos_check/language_server/execute_command_providers/run_checks_execute_command_provider.rb +28 -0
- data/lib/platformos_check/language_server/handler.rb +310 -0
- data/lib/platformos_check/language_server/hover_engine.rb +32 -0
- data/lib/platformos_check/language_server/hover_provider.rb +53 -0
- data/lib/platformos_check/language_server/hover_providers/filter_hover_provider.rb +113 -0
- data/lib/platformos_check/language_server/io_messenger.rb +109 -0
- data/lib/platformos_check/language_server/messenger.rb +27 -0
- data/lib/platformos_check/language_server/protocol.rb +55 -0
- data/lib/platformos_check/language_server/server.rb +188 -0
- data/lib/platformos_check/language_server/tokens.rb +55 -0
- data/lib/platformos_check/language_server/type_helper.rb +22 -0
- data/lib/platformos_check/language_server/uri_helper.rb +39 -0
- data/lib/platformos_check/language_server/variable_lookup_finder/assignments_finder/node_handler.rb +87 -0
- data/lib/platformos_check/language_server/variable_lookup_finder/assignments_finder/scope.rb +60 -0
- data/lib/platformos_check/language_server/variable_lookup_finder/assignments_finder/scope_visitor.rb +44 -0
- data/lib/platformos_check/language_server/variable_lookup_finder/assignments_finder.rb +76 -0
- data/lib/platformos_check/language_server/variable_lookup_finder/constants.rb +44 -0
- data/lib/platformos_check/language_server/variable_lookup_finder/liquid_fixer.rb +103 -0
- data/lib/platformos_check/language_server/variable_lookup_finder/potential_lookup.rb +10 -0
- data/lib/platformos_check/language_server/variable_lookup_finder/tolerant_parser.rb +94 -0
- data/lib/platformos_check/language_server/variable_lookup_finder.rb +262 -0
- data/lib/platformos_check/language_server/variable_lookup_traverser.rb +70 -0
- data/lib/platformos_check/language_server/versioned_in_memory_storage.rb +84 -0
- data/lib/platformos_check/language_server.rb +71 -0
- data/lib/platformos_check/layout_file.rb +15 -0
- data/lib/platformos_check/liquid_check.rb +10 -0
- data/lib/platformos_check/liquid_file.rb +102 -0
- data/lib/platformos_check/liquid_node.rb +570 -0
- data/lib/platformos_check/liquid_visitor.rb +39 -0
- data/lib/platformos_check/migration_file.rb +9 -0
- data/lib/platformos_check/node.rb +53 -0
- data/lib/platformos_check/offense.rb +228 -0
- data/lib/platformos_check/page_file.rb +9 -0
- data/lib/platformos_check/parsing_helpers.rb +21 -0
- data/lib/platformos_check/partial_file.rb +15 -0
- data/lib/platformos_check/platformos_liquid/deprecated_filter.rb +31 -0
- data/lib/platformos_check/platformos_liquid/documentation/markdown_template.rb +51 -0
- data/lib/platformos_check/platformos_liquid/documentation.rb +45 -0
- data/lib/platformos_check/platformos_liquid/filter.rb +19 -0
- data/lib/platformos_check/platformos_liquid/object.rb +15 -0
- data/lib/platformos_check/platformos_liquid/source_index/base_entry.rb +66 -0
- data/lib/platformos_check/platformos_liquid/source_index/base_state.rb +23 -0
- data/lib/platformos_check/platformos_liquid/source_index/filter_entry.rb +26 -0
- data/lib/platformos_check/platformos_liquid/source_index/filter_state.rb +11 -0
- data/lib/platformos_check/platformos_liquid/source_index/object_entry.rb +20 -0
- data/lib/platformos_check/platformos_liquid/source_index/object_state.rb +11 -0
- data/lib/platformos_check/platformos_liquid/source_index/parameter_entry.rb +25 -0
- data/lib/platformos_check/platformos_liquid/source_index/property_entry.rb +21 -0
- data/lib/platformos_check/platformos_liquid/source_index/return_type_entry.rb +41 -0
- data/lib/platformos_check/platformos_liquid/source_index/tag_entry.rb +24 -0
- data/lib/platformos_check/platformos_liquid/source_index/tag_state.rb +11 -0
- data/lib/platformos_check/platformos_liquid/source_index.rb +79 -0
- data/lib/platformos_check/platformos_liquid/source_manager.rb +116 -0
- data/lib/platformos_check/platformos_liquid/tag.rb +59 -0
- data/lib/platformos_check/platformos_liquid.rb +21 -0
- data/lib/platformos_check/position.rb +180 -0
- data/lib/platformos_check/position_helper.rb +57 -0
- data/lib/platformos_check/printer.rb +87 -0
- data/lib/platformos_check/regex_helpers.rb +21 -0
- data/lib/platformos_check/releaser.rb +43 -0
- data/lib/platformos_check/schema_file.rb +6 -0
- data/lib/platformos_check/sms_file.rb +9 -0
- data/lib/platformos_check/storage.rb +29 -0
- data/lib/platformos_check/string_helpers.rb +48 -0
- data/lib/platformos_check/tags/background.rb +67 -0
- data/lib/platformos_check/tags/base.rb +14 -0
- data/lib/platformos_check/tags/base_block.rb +14 -0
- data/lib/platformos_check/tags/base_tag_methods.rb +59 -0
- data/lib/platformos_check/tags/cache.rb +13 -0
- data/lib/platformos_check/tags/export.rb +30 -0
- data/lib/platformos_check/tags/form.rb +19 -0
- data/lib/platformos_check/tags/function.rb +58 -0
- data/lib/platformos_check/tags/graphql.rb +70 -0
- data/lib/platformos_check/tags/hash_assign.rb +75 -0
- data/lib/platformos_check/tags/log.rb +15 -0
- data/lib/platformos_check/tags/parse_json.rb +24 -0
- data/lib/platformos_check/tags/print.rb +20 -0
- data/lib/platformos_check/tags/redirect_to.rb +15 -0
- data/lib/platformos_check/tags/render.rb +60 -0
- data/lib/platformos_check/tags/response_headers.rb +20 -0
- data/lib/platformos_check/tags/response_status.rb +20 -0
- data/lib/platformos_check/tags/return.rb +20 -0
- data/lib/platformos_check/tags/session.rb +27 -0
- data/lib/platformos_check/tags/sign_in.rb +27 -0
- data/lib/platformos_check/tags/spam_protection.rb +15 -0
- data/lib/platformos_check/tags/theme_render.rb +58 -0
- data/lib/platformos_check/tags/try.rb +59 -0
- data/lib/platformos_check/tags.rb +65 -0
- data/lib/platformos_check/translation_file.rb +6 -0
- data/lib/platformos_check/user_schema_file.rb +6 -0
- data/lib/platformos_check/version.rb +5 -0
- data/lib/platformos_check/yaml_check.rb +11 -0
- data/lib/platformos_check/yaml_file.rb +57 -0
- data/lib/platformos_check.rb +106 -0
- data/platformos-check.gemspec +34 -0
- metadata +329 -0
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'json'
|
|
4
|
+
require 'stringio'
|
|
5
|
+
require 'timeout'
|
|
6
|
+
|
|
7
|
+
module PlatformosCheck
|
|
8
|
+
module LanguageServer
|
|
9
|
+
class DoneStreaming < StandardError; end
|
|
10
|
+
|
|
11
|
+
class IncompatibleStream < StandardError; end
|
|
12
|
+
|
|
13
|
+
class Server
|
|
14
|
+
attr_reader :handler, :should_raise_errors
|
|
15
|
+
|
|
16
|
+
def initialize(
|
|
17
|
+
messenger:,
|
|
18
|
+
should_raise_errors: false,
|
|
19
|
+
number_of_threads: 2
|
|
20
|
+
)
|
|
21
|
+
# This is what does the IO
|
|
22
|
+
@messenger = messenger
|
|
23
|
+
|
|
24
|
+
# This is what you use to communicate with the language client
|
|
25
|
+
@bridge = Bridge.new(@messenger)
|
|
26
|
+
|
|
27
|
+
# The handler handles messages from the language client
|
|
28
|
+
@handler = Handler.new(@bridge)
|
|
29
|
+
|
|
30
|
+
# The queue holds the JSON RPC messages
|
|
31
|
+
@queue = Queue.new
|
|
32
|
+
|
|
33
|
+
# The JSON RPC thread pushes messages onto the queue
|
|
34
|
+
@json_rpc_thread = nil
|
|
35
|
+
|
|
36
|
+
# The handler threads read messages from the queue
|
|
37
|
+
@number_of_threads = number_of_threads
|
|
38
|
+
@handlers = []
|
|
39
|
+
|
|
40
|
+
# The error queue holds blocks the main thread. When filled, we exit the program.
|
|
41
|
+
@error = SizedQueue.new(number_of_threads)
|
|
42
|
+
|
|
43
|
+
@should_raise_errors = should_raise_errors
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def listen
|
|
47
|
+
start_handler_threads
|
|
48
|
+
start_json_rpc_thread
|
|
49
|
+
err = @error.pop
|
|
50
|
+
status_code = status_code_from_error(err)
|
|
51
|
+
|
|
52
|
+
if status_code > 0
|
|
53
|
+
# For a reason I can't comprehend, this hangs but prints
|
|
54
|
+
# anyway. So it's wrapped in this ugly timeout...
|
|
55
|
+
Timeout.timeout(1) do
|
|
56
|
+
warn err.full_message
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Warn user of error, otherwise server might restart
|
|
60
|
+
# without telling you.
|
|
61
|
+
@bridge.send_notification("window/showMessage", {
|
|
62
|
+
type: 1,
|
|
63
|
+
message: "A platformos-check-language-server error has occurred, search OUTPUT logs for details."
|
|
64
|
+
})
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
cleanup(status_code)
|
|
68
|
+
rescue SignalException
|
|
69
|
+
0
|
|
70
|
+
rescue StandardError
|
|
71
|
+
2
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def start_json_rpc_thread
|
|
75
|
+
@json_rpc_thread = Thread.new do
|
|
76
|
+
loop do
|
|
77
|
+
message = @bridge.read_message
|
|
78
|
+
if message[:method] == 'initialize'
|
|
79
|
+
handle_message(message)
|
|
80
|
+
elsif message.key?(:result)
|
|
81
|
+
# Responses are handled on the main thread to prevent
|
|
82
|
+
# a potential deadlock caused by all handlers waiting
|
|
83
|
+
# for a responses.
|
|
84
|
+
handle_response(message)
|
|
85
|
+
else
|
|
86
|
+
@queue << message
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
|
90
|
+
@bridge.log("rescuing #{e.class} in jsonrpc thread")
|
|
91
|
+
@error << e
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def start_handler_threads
|
|
96
|
+
@number_of_threads.times do
|
|
97
|
+
@handlers << Thread.new do
|
|
98
|
+
handle_messages
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def handle_messages
|
|
104
|
+
loop do
|
|
105
|
+
message = @queue.pop
|
|
106
|
+
return if @queue.closed? && @queue.empty?
|
|
107
|
+
|
|
108
|
+
handle_message(message)
|
|
109
|
+
end
|
|
110
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
|
111
|
+
@bridge.log("rescuing #{e} in handler thread")
|
|
112
|
+
@error << e
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def status_code_from_error(e)
|
|
116
|
+
raise e
|
|
117
|
+
|
|
118
|
+
# support ctrl+c and stuff
|
|
119
|
+
rescue SignalException, DoneStreaming
|
|
120
|
+
0
|
|
121
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
|
122
|
+
raise e if should_raise_errors
|
|
123
|
+
|
|
124
|
+
@bridge.log("Fatal #{e.class}")
|
|
125
|
+
2
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
private
|
|
129
|
+
|
|
130
|
+
def handle_message(message)
|
|
131
|
+
id = message[:id]
|
|
132
|
+
method_name = message[:method]
|
|
133
|
+
method_name &&= "on_#{to_snake_case(method_name)}"
|
|
134
|
+
params = message[:params]
|
|
135
|
+
|
|
136
|
+
@handler.send(method_name, id, params) if @handler.respond_to?(method_name)
|
|
137
|
+
rescue DoneStreaming => e
|
|
138
|
+
raise e
|
|
139
|
+
rescue StandardError => e
|
|
140
|
+
is_request = id
|
|
141
|
+
raise e unless is_request
|
|
142
|
+
|
|
143
|
+
# Errors obtained in request handlers should be sent
|
|
144
|
+
# back as internal errors instead of closing the program.
|
|
145
|
+
@bridge.send_internal_error(id, e)
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def handle_response(message)
|
|
149
|
+
id = message[:id]
|
|
150
|
+
result = message[:result]
|
|
151
|
+
@bridge.receive_response(id, result)
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def to_snake_case(method_name)
|
|
155
|
+
StringHelpers.underscore(method_name.gsub(/[^\w]/, '_'))
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def cleanup(status_code)
|
|
159
|
+
@bridge.log("Closing server... status code = #{status_code}")
|
|
160
|
+
# Stop listenting to RPC calls
|
|
161
|
+
@messenger.close_input
|
|
162
|
+
# Wait for rpc loop to close
|
|
163
|
+
@json_rpc_thread&.join if @json_rpc_thread&.alive?
|
|
164
|
+
# Close the queue
|
|
165
|
+
@queue.close unless @queue.closed?
|
|
166
|
+
# Give 10 seconds for the handlers to wrap up what they were
|
|
167
|
+
# doing/emptying the queue. 👀 unit tests.
|
|
168
|
+
@handlers.each { |thread| thread.join(10) if thread.alive? }
|
|
169
|
+
|
|
170
|
+
# Hijack the status_code if an error occurred while cleaning up.
|
|
171
|
+
# 👀 unit tests.
|
|
172
|
+
until @error.empty?
|
|
173
|
+
code = status_code_from_error(@error.pop)
|
|
174
|
+
# Promote the status_code to ERROR if one of the threads
|
|
175
|
+
# resulted in an error, otherwise leave the status_code as
|
|
176
|
+
# is. That's because one thread could end successfully in a
|
|
177
|
+
# DoneStreaming error while the other failed with an
|
|
178
|
+
# internal error. If we had an internal error, we should
|
|
179
|
+
# return with a status_code that fits.
|
|
180
|
+
status_code = code if code > status_code
|
|
181
|
+
end
|
|
182
|
+
status_code
|
|
183
|
+
ensure
|
|
184
|
+
@messenger.close_output
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PlatformosCheck
|
|
4
|
+
Token = Struct.new(
|
|
5
|
+
:content,
|
|
6
|
+
:start, # inclusive
|
|
7
|
+
:end # exclusive
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
TAG_START = Liquid::TagStart
|
|
11
|
+
TAG_END = Liquid::TagEnd
|
|
12
|
+
VARIABLE_START = Liquid::VariableStart
|
|
13
|
+
VARIABLE_END = Liquid::VariableEnd
|
|
14
|
+
SPLITTER = %r{
|
|
15
|
+
(?=(?:#{TAG_START}|#{VARIABLE_START}))| # positive lookahead on tag/variable start
|
|
16
|
+
(?<=(?:#{TAG_END}|#{VARIABLE_END})) # positive lookbehind on tag/variable end
|
|
17
|
+
}xom
|
|
18
|
+
|
|
19
|
+
# Implemented as an Enumerable so we stop iterating on the find once
|
|
20
|
+
# we have what we want. Kind of a perf thing.
|
|
21
|
+
class Tokens
|
|
22
|
+
include Enumerable
|
|
23
|
+
|
|
24
|
+
def initialize(buffer)
|
|
25
|
+
@buffer = buffer
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def each(&block)
|
|
29
|
+
return to_enum(:each) unless block
|
|
30
|
+
|
|
31
|
+
chunks = @buffer.split(SPLITTER)
|
|
32
|
+
chunks.shift if chunks[0] && chunks[0].empty?
|
|
33
|
+
|
|
34
|
+
prev = Token.new('', 0, 0)
|
|
35
|
+
curr = Token.new('', 0, 0)
|
|
36
|
+
|
|
37
|
+
while (content = chunks.shift)
|
|
38
|
+
|
|
39
|
+
curr.start = prev.end
|
|
40
|
+
curr.end = curr.start + content.size
|
|
41
|
+
|
|
42
|
+
yield(Token.new(
|
|
43
|
+
content,
|
|
44
|
+
curr.start,
|
|
45
|
+
curr.end
|
|
46
|
+
))
|
|
47
|
+
|
|
48
|
+
# recycling structs
|
|
49
|
+
tmp = prev
|
|
50
|
+
prev = curr
|
|
51
|
+
curr = tmp
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PlatformosCheck
|
|
4
|
+
module LanguageServer
|
|
5
|
+
module TypeHelper
|
|
6
|
+
def input_type_of(literal)
|
|
7
|
+
case literal
|
|
8
|
+
when String
|
|
9
|
+
'string'
|
|
10
|
+
when Numeric
|
|
11
|
+
'number'
|
|
12
|
+
when TrueClass, FalseClass
|
|
13
|
+
'boolean'
|
|
14
|
+
when NilClass
|
|
15
|
+
'nil'
|
|
16
|
+
else
|
|
17
|
+
'untyped'
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "benchmark"
|
|
4
|
+
require "uri"
|
|
5
|
+
require "cgi"
|
|
6
|
+
|
|
7
|
+
module PlatformosCheck
|
|
8
|
+
module LanguageServer
|
|
9
|
+
module URIHelper
|
|
10
|
+
# Will URI.encode a string the same way VS Code would. There are two things
|
|
11
|
+
# to watch out for:
|
|
12
|
+
#
|
|
13
|
+
# 1. VS Code still uses the outdated '%20' for spaces
|
|
14
|
+
# 2. VS Code prefixes Windows paths with / (so /C:/Users/... is expected)
|
|
15
|
+
#
|
|
16
|
+
# Exists because of https://github.com/Platform-OS/platformos-lsp/issues/360
|
|
17
|
+
def file_uri(absolute_path)
|
|
18
|
+
return if absolute_path.nil?
|
|
19
|
+
|
|
20
|
+
"file://" + absolute_path
|
|
21
|
+
.to_s
|
|
22
|
+
.split('/')
|
|
23
|
+
.map { |x| CGI.escape(x).gsub('+', '%20') }
|
|
24
|
+
.join('/')
|
|
25
|
+
.sub(%r{^/?}, '/') # Windows paths should be prefixed by /c:
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def file_path(uri_string)
|
|
29
|
+
return if uri_string.nil?
|
|
30
|
+
|
|
31
|
+
uri = URI(uri_string)
|
|
32
|
+
path = CGI.unescape(uri.path)
|
|
33
|
+
# On Windows, VS Code sends the URLs as file:///C:/...
|
|
34
|
+
# /C:/1234 is not a valid path in ruby. So we strip the slash.
|
|
35
|
+
path.sub(%r{^/([a-z]:/)}i, '\1')
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
data/lib/platformos_check/language_server/variable_lookup_finder/assignments_finder/node_handler.rb
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PlatformosCheck
|
|
4
|
+
module LanguageServer
|
|
5
|
+
module VariableLookupFinder
|
|
6
|
+
class AssignmentsFinder
|
|
7
|
+
class NodeHandler
|
|
8
|
+
def on_assign(node, scope)
|
|
9
|
+
# When a variable is redefined in a new scope we
|
|
10
|
+
# no longer can guarantee the type in the global scope
|
|
11
|
+
#
|
|
12
|
+
# Example:
|
|
13
|
+
# ```liquid
|
|
14
|
+
# {%- liquid
|
|
15
|
+
# assign var1 = some_value
|
|
16
|
+
#
|
|
17
|
+
# if condition
|
|
18
|
+
# assign var1 = another_value
|
|
19
|
+
# ^^^^ from here we no longer can guarantee
|
|
20
|
+
# the type of `var1` in the global scope
|
|
21
|
+
# -%}
|
|
22
|
+
# ```
|
|
23
|
+
p_scope = scope
|
|
24
|
+
while (p_scope = p_scope.parent)
|
|
25
|
+
p_scope.variables.delete(node.value.to)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
scope << node
|
|
29
|
+
scope
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def on_function(node, scope)
|
|
33
|
+
# When a variable is redefined in a new scope we
|
|
34
|
+
# no longer can guarantee the type in the global scope
|
|
35
|
+
#
|
|
36
|
+
# Example:
|
|
37
|
+
# ```liquid
|
|
38
|
+
# {%- liquid
|
|
39
|
+
# assign var1 = some_value
|
|
40
|
+
#
|
|
41
|
+
# if condition
|
|
42
|
+
# function var1 = 'another_value'
|
|
43
|
+
# ^^^^ from here we no longer can guarantee
|
|
44
|
+
# the type of `var1` in the global scope
|
|
45
|
+
# -%}
|
|
46
|
+
# ```
|
|
47
|
+
p_scope = scope
|
|
48
|
+
while (p_scope = p_scope.parent)
|
|
49
|
+
p_scope.variables.delete(node.value.to)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
scope << node
|
|
53
|
+
scope
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
##
|
|
57
|
+
# Table row tags do not rely on blocks to define scopes,
|
|
58
|
+
# so we index their value here
|
|
59
|
+
def on_table_row(node, scope)
|
|
60
|
+
scope = scope.new_child
|
|
61
|
+
|
|
62
|
+
scope << node
|
|
63
|
+
scope
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
##
|
|
67
|
+
# Define a new scope every time a new block is created
|
|
68
|
+
def on_block_body(node, scope)
|
|
69
|
+
scope = scope.new_child
|
|
70
|
+
|
|
71
|
+
##
|
|
72
|
+
# 'for' tags handle blocks flattenly and differently
|
|
73
|
+
# than the other tags (if, unless, case).
|
|
74
|
+
#
|
|
75
|
+
# The scope of 'for' tags exists only in the first
|
|
76
|
+
# block, as the following one refers to the else
|
|
77
|
+
# statement of the iteration.
|
|
78
|
+
parent = node.parent
|
|
79
|
+
|
|
80
|
+
scope << parent if parent.type_name == :for && parent.children.first == node
|
|
81
|
+
scope
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PlatformosCheck
|
|
4
|
+
module LanguageServer
|
|
5
|
+
module VariableLookupFinder
|
|
6
|
+
class AssignmentsFinder
|
|
7
|
+
class Scope < Struct.new(:variables, :parent)
|
|
8
|
+
include TypeHelper
|
|
9
|
+
|
|
10
|
+
def new_child
|
|
11
|
+
child_scope = dup
|
|
12
|
+
child_scope.variables = variables.dup
|
|
13
|
+
child_scope.parent = self
|
|
14
|
+
child_scope
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def <<(node)
|
|
18
|
+
tag = node.value
|
|
19
|
+
|
|
20
|
+
case tag
|
|
21
|
+
when Liquid::Assign
|
|
22
|
+
variable_name = tag.to
|
|
23
|
+
variables[variable_name] = as_potential_lookup(tag.from.name)
|
|
24
|
+
when Liquid::For, Liquid::TableRow
|
|
25
|
+
variable_name = tag.variable_name
|
|
26
|
+
variables[variable_name] = as_potential_lookup(tag.collection_name, ['first'])
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def as_potential_lookup(variable_lookup, default_lookups = [])
|
|
33
|
+
case variable_lookup
|
|
34
|
+
when Liquid::VariableLookup
|
|
35
|
+
potential_lookup(variable_lookup, default_lookups)
|
|
36
|
+
when Liquid::RangeLookup
|
|
37
|
+
as_potential_lookup(variable_lookup.start_obj)
|
|
38
|
+
when Enumerable
|
|
39
|
+
as_potential_lookup(variable_lookup.first)
|
|
40
|
+
else
|
|
41
|
+
literal_lookup(variable_lookup)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def literal_lookup(variable_lookup)
|
|
46
|
+
name = input_type_of(variable_lookup)
|
|
47
|
+
PotentialLookup.new(name, [], variables)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def potential_lookup(variable_lookup, default_lookups)
|
|
51
|
+
name = variable_lookup.name
|
|
52
|
+
lookups = variable_lookup.lookups.concat(default_lookups)
|
|
53
|
+
|
|
54
|
+
PotentialLookup.new(name, lookups, variables)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
data/lib/platformos_check/language_server/variable_lookup_finder/assignments_finder/scope_visitor.rb
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PlatformosCheck
|
|
4
|
+
module LanguageServer
|
|
5
|
+
module VariableLookupFinder
|
|
6
|
+
class AssignmentsFinder
|
|
7
|
+
class ScopeVisitor
|
|
8
|
+
SCOPE_UNAWARE_NODES = %i[range range_lookup variable variable_lookup]
|
|
9
|
+
|
|
10
|
+
attr_reader :global_scope, :current_scope
|
|
11
|
+
|
|
12
|
+
def initialize
|
|
13
|
+
@node_handler = NodeHandler.new
|
|
14
|
+
@global_scope = Scope.new({})
|
|
15
|
+
@current_scope = Scope.new({})
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def visit_template(template)
|
|
19
|
+
return unless template
|
|
20
|
+
|
|
21
|
+
visit(liquid_node(template), global_scope)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def visit(node, scope)
|
|
27
|
+
return if SCOPE_UNAWARE_NODES.include?(node.type_name)
|
|
28
|
+
|
|
29
|
+
method = :"on_#{node.type_name}"
|
|
30
|
+
scope = @node_handler.send(method, node, scope) if @node_handler.respond_to?(method)
|
|
31
|
+
|
|
32
|
+
@current_scope = scope
|
|
33
|
+
|
|
34
|
+
node.children.each { |child| visit(child, scope) }
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def liquid_node(template)
|
|
38
|
+
LiquidNode.new(template.root, nil, template)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PlatformosCheck
|
|
4
|
+
module LanguageServer
|
|
5
|
+
module VariableLookupFinder
|
|
6
|
+
class AssignmentsFinder
|
|
7
|
+
include RegexHelpers
|
|
8
|
+
|
|
9
|
+
attr_reader :content, :scope_visitor
|
|
10
|
+
|
|
11
|
+
def initialize(content)
|
|
12
|
+
@content = close_tag(content)
|
|
13
|
+
@scope_visitor = ScopeVisitor.new
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def find!
|
|
17
|
+
template = parse(content)
|
|
18
|
+
|
|
19
|
+
if template
|
|
20
|
+
visit_template(template)
|
|
21
|
+
return
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
liquid_tags.each do |tag|
|
|
25
|
+
visit_template(last_line_parse(tag))
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def assignments
|
|
30
|
+
current_scope = scope_visitor.current_scope
|
|
31
|
+
current_scope.variables
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
def visit_template(template)
|
|
37
|
+
scope_visitor.visit_template(template)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def liquid_tags
|
|
41
|
+
matches(content, LIQUID_TAG_OR_VARIABLE)
|
|
42
|
+
.flat_map { |match| match[0] }
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def parse(content)
|
|
46
|
+
regular_parse(content) || tolerant_parse(content)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def regular_parse(content)
|
|
50
|
+
Liquid::Template.parse(content)
|
|
51
|
+
rescue Liquid::SyntaxError
|
|
52
|
+
# Ignore syntax errors at the regular parse phase
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def tolerant_parse(content)
|
|
56
|
+
TolerantParser::Template.parse(content)
|
|
57
|
+
rescue StandardError
|
|
58
|
+
# Ignore any error at the tolerant parse phase
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def last_line_parse(content)
|
|
62
|
+
parsable_content = LiquidFixer.new(content).parsable
|
|
63
|
+
|
|
64
|
+
regular_parse(parsable_content)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def close_tag(content)
|
|
68
|
+
lines = content.lines
|
|
69
|
+
end_tag = VARIABLE_START.match?(lines.last) ? ' }}' : ' %}'
|
|
70
|
+
|
|
71
|
+
content + end_tag
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PlatformosCheck
|
|
4
|
+
module LanguageServer
|
|
5
|
+
module VariableLookupFinder
|
|
6
|
+
module Constants
|
|
7
|
+
ANY_STARTING_TAG = /\s*#{Liquid::AnyStartingTag}/
|
|
8
|
+
ANY_ENDING_TAG = /#{Liquid::TagEnd}|#{Liquid::VariableEnd}\s*^/om
|
|
9
|
+
|
|
10
|
+
UNCLOSED_SQUARE_BRACKET = /\[[^\]]*\Z/
|
|
11
|
+
ENDS_IN_BRACKET_POSITION_THAT_CANT_BE_COMPLETED = /
|
|
12
|
+
(
|
|
13
|
+
# quotes not preceded by a [
|
|
14
|
+
(?<!\[)['"]|
|
|
15
|
+
# closing ]
|
|
16
|
+
\]|
|
|
17
|
+
# opening [
|
|
18
|
+
\[
|
|
19
|
+
)$
|
|
20
|
+
/x
|
|
21
|
+
|
|
22
|
+
VARIABLE_START = /\s*#{Liquid::VariableStart}/
|
|
23
|
+
VARIABLE_LOOKUP_CHARACTERS = /[a-z0-9_.'"\]\[]/i
|
|
24
|
+
VARIABLE_LOOKUP = /#{VARIABLE_LOOKUP_CHARACTERS}+/o
|
|
25
|
+
SYMBOLS_PRECEDING_POTENTIAL_LOOKUPS = /
|
|
26
|
+
(?:
|
|
27
|
+
\s(?:
|
|
28
|
+
if|elsif|unless|and|or|#{Liquid::Condition.operators.keys.join("|")}
|
|
29
|
+
|echo
|
|
30
|
+
|paginate
|
|
31
|
+
|case|when
|
|
32
|
+
|cycle
|
|
33
|
+
|in
|
|
34
|
+
)
|
|
35
|
+
|[:,=]
|
|
36
|
+
)
|
|
37
|
+
\s+
|
|
38
|
+
/omix
|
|
39
|
+
ENDS_WITH_BLANK_POTENTIAL_LOOKUP = /#{SYMBOLS_PRECEDING_POTENTIAL_LOOKUPS}$/oimx
|
|
40
|
+
ENDS_WITH_POTENTIAL_LOOKUP = /#{SYMBOLS_PRECEDING_POTENTIAL_LOOKUPS}#{VARIABLE_LOOKUP}$/oimx
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|