platformos-check 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PlatformosCheck
|
4
|
+
module LanguageServer
|
5
|
+
# How you'd use this class:
|
6
|
+
#
|
7
|
+
# In thread #1:
|
8
|
+
# def foo
|
9
|
+
# chan = Channel.create
|
10
|
+
# send_request(chan.id, ...)
|
11
|
+
# result = chan.pop
|
12
|
+
# do_stuff_with_result(result)
|
13
|
+
# ensure
|
14
|
+
# chan.close
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# In thread #2:
|
18
|
+
# Channel.by_id(id) << result
|
19
|
+
class Channel
|
20
|
+
MUTEX = Mutex.new
|
21
|
+
CHANNELS = {}
|
22
|
+
|
23
|
+
class << self
|
24
|
+
def create
|
25
|
+
id = new_id
|
26
|
+
CHANNELS[id] = new(id)
|
27
|
+
CHANNELS[id]
|
28
|
+
end
|
29
|
+
|
30
|
+
def by_id(id)
|
31
|
+
CHANNELS[id]
|
32
|
+
end
|
33
|
+
|
34
|
+
def close(id)
|
35
|
+
CHANNELS.delete(id)
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def new_id
|
41
|
+
MUTEX.synchronize do
|
42
|
+
@id ||= 0
|
43
|
+
@id += 1
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
attr_reader :id
|
49
|
+
|
50
|
+
def initialize(id)
|
51
|
+
@id = id
|
52
|
+
@response = SizedQueue.new(1)
|
53
|
+
end
|
54
|
+
|
55
|
+
def pop
|
56
|
+
@response.pop
|
57
|
+
end
|
58
|
+
|
59
|
+
def <<(value)
|
60
|
+
@response << value
|
61
|
+
end
|
62
|
+
|
63
|
+
def close
|
64
|
+
@response.close
|
65
|
+
Channel.close(id)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PlatformosCheck
|
4
|
+
module LanguageServer
|
5
|
+
class ClientCapabilities
|
6
|
+
def initialize(capabilities)
|
7
|
+
@capabilities = capabilities
|
8
|
+
end
|
9
|
+
|
10
|
+
def supports_work_done_progress?
|
11
|
+
@capabilities.dig(:window, :workDoneProgress) || false
|
12
|
+
end
|
13
|
+
|
14
|
+
def supports_workspace_configuration?
|
15
|
+
@capabilities.dig(:workspace, :configuration) || false
|
16
|
+
end
|
17
|
+
|
18
|
+
def supports_workspace_did_change_configuration_dynamic_registration?
|
19
|
+
@capabilities.dig(:workspace, :didChangeConfiguration, :dynamicRegistration) || false
|
20
|
+
end
|
21
|
+
|
22
|
+
def initialization_option(key)
|
23
|
+
@capabilities.dig(:initializationOptions, key)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PlatformosCheck
|
4
|
+
module LanguageServer
|
5
|
+
class CodeActionEngine
|
6
|
+
include PositionHelper
|
7
|
+
|
8
|
+
def initialize(storage, diagnostics_manager)
|
9
|
+
@storage = storage
|
10
|
+
@providers = CodeActionProvider.all.map { |c| c.new(storage, diagnostics_manager) }
|
11
|
+
end
|
12
|
+
|
13
|
+
def code_actions(absolute_path, start_position, end_position, only_kinds = [])
|
14
|
+
relative_path = @storage.relative_path(absolute_path)
|
15
|
+
buffer = @storage.read(relative_path)
|
16
|
+
start_index = from_row_column_to_index(buffer, start_position[0], start_position[1])
|
17
|
+
end_index = from_row_column_to_index(buffer, end_position[0], end_position[1])
|
18
|
+
range = (start_index...end_index)
|
19
|
+
|
20
|
+
@providers
|
21
|
+
.filter do |provider|
|
22
|
+
only_kinds.empty? ||
|
23
|
+
only_kinds.include?(provider.kind) ||
|
24
|
+
only_kinds.include?(provider.base_kind)
|
25
|
+
end
|
26
|
+
.flat_map do |provider|
|
27
|
+
provider.code_actions(relative_path, range)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PlatformosCheck
|
4
|
+
module LanguageServer
|
5
|
+
class CodeActionProvider
|
6
|
+
class << self
|
7
|
+
def all
|
8
|
+
@all ||= []
|
9
|
+
end
|
10
|
+
|
11
|
+
def inherited(subclass)
|
12
|
+
all << subclass
|
13
|
+
end
|
14
|
+
|
15
|
+
def kind(k = nil)
|
16
|
+
@kind = k unless k.nil?
|
17
|
+
@kind
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
attr_reader :storage, :diagnostics_manager
|
22
|
+
|
23
|
+
def initialize(storage, diagnostics_manager)
|
24
|
+
@storage = storage
|
25
|
+
@diagnostics_manager = diagnostics_manager
|
26
|
+
end
|
27
|
+
|
28
|
+
def kind
|
29
|
+
self.class.kind
|
30
|
+
end
|
31
|
+
|
32
|
+
def base_kind
|
33
|
+
kind.split('.')[0]
|
34
|
+
end
|
35
|
+
|
36
|
+
def code_actions(relative_path, range)
|
37
|
+
raise NotImplementedError
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/lib/platformos_check/language_server/code_action_providers/quickfix_code_action_provider.rb
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PlatformosCheck
|
4
|
+
module LanguageServer
|
5
|
+
class QuickfixCodeActionProvider < CodeActionProvider
|
6
|
+
kind "quickfix"
|
7
|
+
|
8
|
+
def code_actions(relative_path, range)
|
9
|
+
correctable_diagnostics = diagnostics_manager
|
10
|
+
.diagnostics(relative_path)
|
11
|
+
.filter(&:correctable?)
|
12
|
+
.reject do |diagnostic|
|
13
|
+
# We cannot quickfix if the buffer was modified. This means
|
14
|
+
# our diagnostics and InMemoryStorage are out of sync.
|
15
|
+
diagnostic.file_version != storage.version(diagnostic.relative_path)
|
16
|
+
end
|
17
|
+
|
18
|
+
diagnostics_under_cursor = correctable_diagnostics
|
19
|
+
.filter { |diagnostic| diagnostic.offense.in_range?(range) }
|
20
|
+
|
21
|
+
return [] if diagnostics_under_cursor.empty?
|
22
|
+
|
23
|
+
(
|
24
|
+
quickfix_cursor_code_actions(diagnostics_under_cursor) +
|
25
|
+
quickfix_all_of_type_code_actions(diagnostics_under_cursor, correctable_diagnostics) +
|
26
|
+
quickfix_all_code_action(correctable_diagnostics)
|
27
|
+
)
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def quickfix_cursor_code_actions(diagnostics)
|
33
|
+
diagnostics.map do |diagnostic|
|
34
|
+
{
|
35
|
+
title: "Fix this #{diagnostic.code} problem: #{diagnostic.message}",
|
36
|
+
kind:,
|
37
|
+
diagnostics: [diagnostic.to_h],
|
38
|
+
isPreferred: true,
|
39
|
+
command: {
|
40
|
+
title: 'quickfix',
|
41
|
+
command: CorrectionExecuteCommandProvider.command,
|
42
|
+
arguments: [diagnostic.to_h]
|
43
|
+
}
|
44
|
+
}
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def quickfix_all_of_type_code_actions(cursor_diagnostics, correctable_diagnostics)
|
49
|
+
codes = Set.new(cursor_diagnostics.map(&:code))
|
50
|
+
correctable_diagnostics_by_code = correctable_diagnostics.group_by(&:code)
|
51
|
+
codes.flat_map do |code|
|
52
|
+
diagnostics = correctable_diagnostics_by_code[code].map(&:to_h)
|
53
|
+
return [] unless diagnostics.size > 1
|
54
|
+
|
55
|
+
{
|
56
|
+
title: "Fix all #{code} problems",
|
57
|
+
kind:,
|
58
|
+
diagnostics:,
|
59
|
+
command: {
|
60
|
+
title: 'quickfix',
|
61
|
+
command: CorrectionExecuteCommandProvider.command,
|
62
|
+
arguments: diagnostics
|
63
|
+
}
|
64
|
+
}
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def quickfix_all_code_action(diagnostics)
|
69
|
+
return [] unless diagnostics.size > 1
|
70
|
+
|
71
|
+
diagnostics = diagnostics.map(&:to_h)
|
72
|
+
[{
|
73
|
+
title: "Fix all auto-fixable problems",
|
74
|
+
kind:,
|
75
|
+
diagnostics:,
|
76
|
+
command: {
|
77
|
+
title: 'quickfix',
|
78
|
+
command: CorrectionExecuteCommandProvider.command,
|
79
|
+
arguments: diagnostics
|
80
|
+
}
|
81
|
+
}]
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PlatformosCheck
|
4
|
+
module LanguageServer
|
5
|
+
class SourceFixAllCodeActionProvider < CodeActionProvider
|
6
|
+
kind "source.fixAll"
|
7
|
+
|
8
|
+
def code_actions(relative_path, _)
|
9
|
+
diagnostics = diagnostics_manager
|
10
|
+
.diagnostics(relative_path)
|
11
|
+
.filter(&:correctable?)
|
12
|
+
.reject do |diagnostic|
|
13
|
+
# We cannot quickfix if the buffer was modified. This means
|
14
|
+
# our diagnostics and InMemoryStorage are out of sync.
|
15
|
+
diagnostic.file_version != storage.version(diagnostic.relative_path)
|
16
|
+
end
|
17
|
+
.map(&:to_h)
|
18
|
+
diagnostics_to_code_action(diagnostics)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def diagnostics_to_code_action(diagnostics)
|
24
|
+
return [] if diagnostics.empty?
|
25
|
+
|
26
|
+
[
|
27
|
+
{
|
28
|
+
title: "Fix all PlatformOS Check auto-fixable problems",
|
29
|
+
kind:,
|
30
|
+
diagnostics:,
|
31
|
+
command: {
|
32
|
+
title: 'fixAll.file',
|
33
|
+
command: LanguageServer::CorrectionExecuteCommandProvider.command,
|
34
|
+
arguments: diagnostics
|
35
|
+
}
|
36
|
+
}
|
37
|
+
]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PlatformosCheck
|
4
|
+
module LanguageServer
|
5
|
+
class CompletionContext
|
6
|
+
include PositionHelper
|
7
|
+
|
8
|
+
attr_reader :storage, :relative_path, :line, :col
|
9
|
+
|
10
|
+
def initialize(storage, relative_path, line, col)
|
11
|
+
@storage = storage
|
12
|
+
@relative_path = relative_path
|
13
|
+
@line = line
|
14
|
+
@col = col
|
15
|
+
end
|
16
|
+
|
17
|
+
def buffer
|
18
|
+
@buffer ||= storage.read(relative_path)
|
19
|
+
end
|
20
|
+
|
21
|
+
def buffer_until_previous_row
|
22
|
+
@buffer_without_current_row ||= buffer[0..absolute_cursor].lines[0...-1].join
|
23
|
+
end
|
24
|
+
|
25
|
+
def absolute_cursor
|
26
|
+
@absolute_cursor ||= from_row_column_to_index(buffer, line, col)
|
27
|
+
end
|
28
|
+
|
29
|
+
def cursor
|
30
|
+
@cursor ||= (absolute_cursor - token&.start) || 0
|
31
|
+
end
|
32
|
+
|
33
|
+
def content
|
34
|
+
@content ||= token&.content
|
35
|
+
end
|
36
|
+
|
37
|
+
def token
|
38
|
+
@token ||= Tokens.new(buffer).find do |t|
|
39
|
+
# Here we include the next character and exclude the first
|
40
|
+
# one becase when we want to autocomplete inside a token
|
41
|
+
# and at most 1 outside it since the cursor could be placed
|
42
|
+
# at the end of the token.
|
43
|
+
t.start < absolute_cursor && absolute_cursor <= t.end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def clone_and_overwrite(col:)
|
48
|
+
CompletionContext.new(storage, relative_path, line, col)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PlatformosCheck
|
4
|
+
module LanguageServer
|
5
|
+
class CompletionEngine
|
6
|
+
def initialize(storage, bridge = nil)
|
7
|
+
@storage = storage
|
8
|
+
@bridge = bridge
|
9
|
+
@providers = CompletionProvider.all.map { |x| x.new(storage) }
|
10
|
+
end
|
11
|
+
|
12
|
+
def completions(relative_path, line, col)
|
13
|
+
context = context(relative_path, line, col)
|
14
|
+
|
15
|
+
@providers
|
16
|
+
.flat_map { |provider| provider.completions(context) }
|
17
|
+
.uniq { |completion_item| completion_item[:label] }
|
18
|
+
rescue StandardError => e
|
19
|
+
@bridge || raise(e)
|
20
|
+
|
21
|
+
message = e.message
|
22
|
+
backtrace = e.backtrace.join("\n")
|
23
|
+
|
24
|
+
@bridge.log("[completion error] error: #{message}\n#{backtrace}")
|
25
|
+
end
|
26
|
+
|
27
|
+
def context(relative_path, line, col)
|
28
|
+
CompletionContext.new(@storage, relative_path, line, col)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PlatformosCheck
|
4
|
+
module LanguageServer
|
5
|
+
module CompletionHelper
|
6
|
+
WORD = /\w+/
|
7
|
+
|
8
|
+
def cursor_on_start_content?(content, cursor, regex)
|
9
|
+
content.slice(0, cursor).match?(/#{regex}(?:\s|\n)*$/m)
|
10
|
+
end
|
11
|
+
|
12
|
+
def cursor_on_first_word?(content, cursor)
|
13
|
+
word = content.match(WORD)
|
14
|
+
return false if word.nil?
|
15
|
+
|
16
|
+
word_start = word.begin(0)
|
17
|
+
word_end = word.end(0)
|
18
|
+
word_start <= cursor && cursor <= word_end
|
19
|
+
end
|
20
|
+
|
21
|
+
def first_word(content)
|
22
|
+
content.match(WORD)[0] if content.match?(WORD)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PlatformosCheck
|
4
|
+
module LanguageServer
|
5
|
+
class CompletionProvider
|
6
|
+
include CompletionHelper
|
7
|
+
include RegexHelpers
|
8
|
+
|
9
|
+
attr_reader :storage
|
10
|
+
|
11
|
+
CurrentToken = Struct.new(:content, :cursor, :absolute_cursor, :buffer)
|
12
|
+
|
13
|
+
class << self
|
14
|
+
def all
|
15
|
+
@all ||= []
|
16
|
+
end
|
17
|
+
|
18
|
+
def inherited(subclass)
|
19
|
+
all << subclass
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize(storage = InMemoryStorage.new)
|
24
|
+
@storage = storage
|
25
|
+
end
|
26
|
+
|
27
|
+
def completions(relative_path, line, col)
|
28
|
+
raise NotImplementedError
|
29
|
+
end
|
30
|
+
|
31
|
+
def doc_hash(content)
|
32
|
+
return {} if content.nil? || content.empty?
|
33
|
+
|
34
|
+
{
|
35
|
+
documentation: {
|
36
|
+
kind: MarkupKinds::MARKDOWN,
|
37
|
+
value: content
|
38
|
+
}
|
39
|
+
}
|
40
|
+
end
|
41
|
+
|
42
|
+
def format_hash(entry)
|
43
|
+
return {} unless entry
|
44
|
+
return { sortText: entry.name } unless entry.deprecated?
|
45
|
+
|
46
|
+
{
|
47
|
+
tags: [CompletionItemTag::DEPRECATED],
|
48
|
+
sortText: "~#{entry.name}"
|
49
|
+
}
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
data/lib/platformos_check/language_server/completion_providers/assignments_completion_provider.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PlatformosCheck
|
4
|
+
module LanguageServer
|
5
|
+
class AssignmentsCompletionProvider < CompletionProvider
|
6
|
+
def completions(context)
|
7
|
+
content = context.buffer_until_previous_row
|
8
|
+
|
9
|
+
return [] if content.nil?
|
10
|
+
return [] unless (variable_lookup = VariableLookupFinder.lookup(context))
|
11
|
+
return [] unless variable_lookup.lookups.empty?
|
12
|
+
return [] if context.content[context.cursor - 1] == "."
|
13
|
+
|
14
|
+
finder = VariableLookupFinder::AssignmentsFinder.new(content)
|
15
|
+
finder.find!
|
16
|
+
finder
|
17
|
+
.assignments
|
18
|
+
.map { |label, potential_lookup| object_to_completion(label, object_name(potential_lookup)) }
|
19
|
+
.compact
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def object_to_completion(label, object)
|
25
|
+
content = PlatformosLiquid::Documentation.object_doc(object)
|
26
|
+
|
27
|
+
{
|
28
|
+
label:,
|
29
|
+
kind: CompletionItemKinds::VARIABLE,
|
30
|
+
**doc_hash(content)
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
34
|
+
def object_name(potential_lookup)
|
35
|
+
object, _property = VariableLookupTraverser.lookup_object_and_property(potential_lookup)
|
36
|
+
object&.name
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PlatformosCheck
|
4
|
+
module LanguageServer
|
5
|
+
class FilterCompletionProvider < CompletionProvider
|
6
|
+
NAMED_FILTER = /#{Liquid::FilterSeparator}\s*(\w+)/o
|
7
|
+
FILTER_SEPARATOR_INCLUDING_SPACES = /\s*#{Liquid::FilterSeparator}/
|
8
|
+
INPUT_TYPE_VARIABLE = 'variable'
|
9
|
+
|
10
|
+
def completions(context)
|
11
|
+
content = context.content
|
12
|
+
cursor = context.cursor
|
13
|
+
|
14
|
+
return [] if content.nil?
|
15
|
+
return [] unless can_complete?(content, cursor)
|
16
|
+
|
17
|
+
context = context_with_cursor_before_potential_filter_separator(context)
|
18
|
+
variable_lookup = VariableLookupFinder.lookup(context)
|
19
|
+
denied_filters = denied_filters_for(variable_lookup)
|
20
|
+
available_filters_for(determine_input_type(variable_lookup))
|
21
|
+
.select { |filter| filter.name.start_with?(partial(content, cursor)) && denied_filters.none?(filter.name) }
|
22
|
+
.map { |filter| filter_to_completion(filter) }
|
23
|
+
end
|
24
|
+
|
25
|
+
def can_complete?(content, cursor)
|
26
|
+
content.match?(Liquid::FilterSeparator) && (
|
27
|
+
cursor_on_start_content?(content, cursor, Liquid::FilterSeparator) ||
|
28
|
+
cursor_on_filter?(content, cursor)
|
29
|
+
)
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def context_with_cursor_before_potential_filter_separator(context)
|
35
|
+
content = context.content
|
36
|
+
diff = content.index(FILTER_SEPARATOR_INCLUDING_SPACES) - context.cursor
|
37
|
+
|
38
|
+
return context unless content.scan(FILTER_SEPARATOR_INCLUDING_SPACES).size == 1
|
39
|
+
|
40
|
+
context.clone_and_overwrite(col: context.col + diff)
|
41
|
+
end
|
42
|
+
|
43
|
+
def determine_input_type(variable_lookup)
|
44
|
+
return if variable_lookup.nil?
|
45
|
+
|
46
|
+
object, property = VariableLookupTraverser.lookup_object_and_property(variable_lookup)
|
47
|
+
return property.return_type if property
|
48
|
+
|
49
|
+
object&.name
|
50
|
+
end
|
51
|
+
|
52
|
+
def denied_filters_for(variable_lookup)
|
53
|
+
return [] if variable_lookup.nil?
|
54
|
+
|
55
|
+
VariableLookupTraverser.find_object(variable_lookup.name)&.denied_filters || []
|
56
|
+
end
|
57
|
+
|
58
|
+
def available_filters_for(input_type)
|
59
|
+
filters = PlatformosLiquid::SourceIndex.filters
|
60
|
+
.select { |filter| input_type.nil? || filter.input_type == input_type }
|
61
|
+
return all_labels if filters.empty?
|
62
|
+
return filters if input_type == INPUT_TYPE_VARIABLE
|
63
|
+
|
64
|
+
filters + available_filters_for(INPUT_TYPE_VARIABLE)
|
65
|
+
end
|
66
|
+
|
67
|
+
def all_labels
|
68
|
+
available_filters_for(nil)
|
69
|
+
end
|
70
|
+
|
71
|
+
def cursor_on_filter?(content, cursor)
|
72
|
+
return false unless content.match?(NAMED_FILTER)
|
73
|
+
|
74
|
+
matches(content, NAMED_FILTER).any? do |match|
|
75
|
+
match.begin(1) <= cursor && cursor < match.end(1) + 1 # including next character
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def partial(content, cursor)
|
80
|
+
return '' unless content.match?(NAMED_FILTER)
|
81
|
+
|
82
|
+
partial_match = matches(content, NAMED_FILTER).find do |match|
|
83
|
+
match.begin(1) <= cursor && cursor < match.end(1) + 1 # including next character
|
84
|
+
end
|
85
|
+
return '' if partial_match.nil?
|
86
|
+
|
87
|
+
partial_match[1]
|
88
|
+
end
|
89
|
+
|
90
|
+
def filter_to_completion(filter)
|
91
|
+
content = PlatformosLiquid::Documentation.render_doc(filter)
|
92
|
+
|
93
|
+
{
|
94
|
+
label: filter.name,
|
95
|
+
kind: CompletionItemKinds::FUNCTION,
|
96
|
+
**format_hash(filter),
|
97
|
+
**doc_hash(content)
|
98
|
+
}
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PlatformosCheck
|
4
|
+
module LanguageServer
|
5
|
+
class ObjectAttributeCompletionProvider < CompletionProvider
|
6
|
+
def completions(context)
|
7
|
+
content = context.content
|
8
|
+
cursor = context.cursor
|
9
|
+
|
10
|
+
return [] if content.nil?
|
11
|
+
return [] unless (variable_lookup = VariableLookupFinder.lookup(context))
|
12
|
+
return [] if content[cursor - 1] == "." && content[cursor - 2] == "."
|
13
|
+
|
14
|
+
# Navigate through lookups until the last valid [object, property] level
|
15
|
+
object, property = VariableLookupTraverser.lookup_object_and_property(variable_lookup)
|
16
|
+
|
17
|
+
# If the last lookup level is incomplete/invalid, use the partial term
|
18
|
+
# to filter object properties.
|
19
|
+
partial = partial_property_name(property, variable_lookup)
|
20
|
+
|
21
|
+
return [] unless object
|
22
|
+
|
23
|
+
object
|
24
|
+
.properties
|
25
|
+
.select { |prop| partial.nil? || prop.name.start_with?(partial) }
|
26
|
+
.map { |prop| property_to_completion(prop) }
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def partial_property_name(property, variable_lookup)
|
32
|
+
last_property = variable_lookup.lookups.last
|
33
|
+
last_property if last_property != property&.name
|
34
|
+
end
|
35
|
+
|
36
|
+
def property_to_completion(prop)
|
37
|
+
content = PlatformosLiquid::Documentation.render_doc(prop)
|
38
|
+
|
39
|
+
{
|
40
|
+
label: prop.name,
|
41
|
+
kind: CompletionItemKinds::PROPERTY,
|
42
|
+
**format_hash(prop),
|
43
|
+
**doc_hash(content)
|
44
|
+
}
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PlatformosCheck
|
4
|
+
module LanguageServer
|
5
|
+
class ObjectCompletionProvider < CompletionProvider
|
6
|
+
def completions(context)
|
7
|
+
content = context.content
|
8
|
+
|
9
|
+
return [] if content.nil?
|
10
|
+
return [] unless (variable_lookup = VariableLookupFinder.lookup(context))
|
11
|
+
return [] unless variable_lookup.lookups.empty?
|
12
|
+
return [] if content[context.cursor - 1] == "."
|
13
|
+
|
14
|
+
PlatformosLiquid::SourceIndex
|
15
|
+
.objects
|
16
|
+
.select { |object| object.name.start_with?(partial(variable_lookup)) }
|
17
|
+
.map { |object| object_to_completion(object) }
|
18
|
+
end
|
19
|
+
|
20
|
+
def partial(variable_lookup)
|
21
|
+
variable_lookup.name || ''
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def object_to_completion(object)
|
27
|
+
content = PlatformosLiquid::Documentation.render_doc(object)
|
28
|
+
|
29
|
+
{
|
30
|
+
label: object.name,
|
31
|
+
kind: CompletionItemKinds::VARIABLE,
|
32
|
+
**format_hash(object),
|
33
|
+
**doc_hash(content)
|
34
|
+
}
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|