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
data/lib/platformos_check/language_server/completion_providers/render_snippet_completion_provider.rb
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PlatformosCheck
|
|
4
|
+
module LanguageServer
|
|
5
|
+
class RenderSnippetCompletionProvider < CompletionProvider
|
|
6
|
+
def completions(context)
|
|
7
|
+
content = context.content
|
|
8
|
+
cursor = context.cursor
|
|
9
|
+
|
|
10
|
+
return [] if content.nil?
|
|
11
|
+
return [] unless cursor_on_quoted_argument?(content, cursor)
|
|
12
|
+
|
|
13
|
+
partial = snippet(content) || ''
|
|
14
|
+
snippets
|
|
15
|
+
.select { |x| x.start_with?(partial) }
|
|
16
|
+
.map { |x| snippet_to_completion(x) }
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def cursor_on_quoted_argument?(content, cursor)
|
|
22
|
+
match = content.match(PARTIAL_RENDER)
|
|
23
|
+
return false if match.nil?
|
|
24
|
+
|
|
25
|
+
match.begin(:partial) <= cursor && cursor <= match.end(:partial)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def snippet(content)
|
|
29
|
+
match = content.match(PARTIAL_RENDER)
|
|
30
|
+
return if match.nil?
|
|
31
|
+
|
|
32
|
+
match[:partial]
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def snippets
|
|
36
|
+
@storage
|
|
37
|
+
.files
|
|
38
|
+
.select { |x| x.include?('snippets/') }
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def snippet_to_completion(file)
|
|
42
|
+
{
|
|
43
|
+
label: File.basename(file, '.liquid'),
|
|
44
|
+
kind: CompletionItemKinds::SNIPPET,
|
|
45
|
+
detail: file
|
|
46
|
+
}
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PlatformosCheck
|
|
4
|
+
module LanguageServer
|
|
5
|
+
class TagCompletionProvider < CompletionProvider
|
|
6
|
+
def completions(context)
|
|
7
|
+
content = context.content
|
|
8
|
+
cursor = context.cursor
|
|
9
|
+
|
|
10
|
+
return [] if content.nil?
|
|
11
|
+
return [] unless can_complete?(content, cursor)
|
|
12
|
+
|
|
13
|
+
partial = first_word(content) || ''
|
|
14
|
+
labels = PlatformosLiquid::Tag.labels
|
|
15
|
+
labels += PlatformosLiquid::Tag.end_labels
|
|
16
|
+
labels
|
|
17
|
+
.select { |w| w.start_with?(partial) }
|
|
18
|
+
.map { |tag| tag_to_completion(tag) }
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def can_complete?(content, cursor)
|
|
22
|
+
content.start_with?(Liquid::TagStart) && (
|
|
23
|
+
cursor_on_first_word?(content, cursor) ||
|
|
24
|
+
cursor_on_start_content?(content, cursor, Liquid::TagStart)
|
|
25
|
+
)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def tag_to_completion(tag)
|
|
31
|
+
content = PlatformosLiquid::Documentation.tag_doc(tag)
|
|
32
|
+
|
|
33
|
+
{
|
|
34
|
+
label: tag,
|
|
35
|
+
kind: CompletionItemKinds::KEYWORD,
|
|
36
|
+
**doc_hash(content)
|
|
37
|
+
}
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PlatformosCheck
|
|
4
|
+
module LanguageServer
|
|
5
|
+
class Configuration
|
|
6
|
+
CHECK_ON_OPEN = :'platformosCheck.checkOnOpen'
|
|
7
|
+
CHECK_ON_SAVE = :'platformosCheck.checkOnSave'
|
|
8
|
+
CHECK_ON_CHANGE = :'platformosCheck.checkOnChange'
|
|
9
|
+
ONLY_SINGLE_FILE = :'platformosCheck.onlySingleFileChecks'
|
|
10
|
+
|
|
11
|
+
def initialize(bridge, capabilities)
|
|
12
|
+
@bridge = bridge
|
|
13
|
+
@capabilities = capabilities
|
|
14
|
+
@mutex = Mutex.new
|
|
15
|
+
@initialized = false
|
|
16
|
+
@config = {
|
|
17
|
+
CHECK_ON_OPEN => null_coalesce(@capabilities.initialization_option(CHECK_ON_OPEN), true),
|
|
18
|
+
CHECK_ON_SAVE => null_coalesce(@capabilities.initialization_option(CHECK_ON_SAVE), true),
|
|
19
|
+
CHECK_ON_CHANGE => null_coalesce(@capabilities.initialization_option(CHECK_ON_CHANGE), true),
|
|
20
|
+
ONLY_SINGLE_FILE => null_coalesce(@capabilities.initialization_option(ONLY_SINGLE_FILE), false)
|
|
21
|
+
}
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def fetch(force: nil)
|
|
25
|
+
@mutex.synchronize do
|
|
26
|
+
return unless @capabilities.supports_workspace_configuration?
|
|
27
|
+
return if initialized? && !force
|
|
28
|
+
|
|
29
|
+
keys = [
|
|
30
|
+
CHECK_ON_OPEN,
|
|
31
|
+
CHECK_ON_SAVE,
|
|
32
|
+
CHECK_ON_CHANGE,
|
|
33
|
+
ONLY_SINGLE_FILE
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
configs = @bridge.send_request(
|
|
37
|
+
"workspace/configuration",
|
|
38
|
+
items: keys.map do |key|
|
|
39
|
+
{ section: key }
|
|
40
|
+
end
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
keys.each.with_index do |key, i|
|
|
44
|
+
@config[key] = configs[i] unless configs[i].nil?
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
@initialized = true
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def register_did_change_capability
|
|
52
|
+
return unless @capabilities.supports_workspace_did_change_configuration_dynamic_registration?
|
|
53
|
+
|
|
54
|
+
@bridge.send_request('client/registerCapability', registrations: [{
|
|
55
|
+
id: "workspace/didChangeConfiguration",
|
|
56
|
+
method: "workspace/didChangeConfiguration"
|
|
57
|
+
}])
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def initialized?
|
|
61
|
+
@initialized
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def check_on_open?
|
|
65
|
+
fetch # making sure we have an initialized value
|
|
66
|
+
@config[CHECK_ON_OPEN]
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def check_on_save?
|
|
70
|
+
fetch # making sure we have for an initialized value
|
|
71
|
+
@config[CHECK_ON_SAVE]
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def check_on_change?
|
|
75
|
+
fetch # making sure we have for an initialized value
|
|
76
|
+
@config[CHECK_ON_CHANGE]
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def only_single_file?
|
|
80
|
+
fetch # making sure we have for an initialized value
|
|
81
|
+
@config[ONLY_SINGLE_FILE]
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def null_coalesce(value, default)
|
|
85
|
+
value.nil? ? default : value
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PlatformosCheck
|
|
4
|
+
module LanguageServer
|
|
5
|
+
def self.partial_tag(tag)
|
|
6
|
+
/
|
|
7
|
+
\{%-?\s*#{tag}\s+'(?<partial>[^']*)'|
|
|
8
|
+
\{%-?\s*#{tag}\s+"(?<partial>[^"]*)"|
|
|
9
|
+
|
|
10
|
+
# in liquid tags the whole line is white space until the tag
|
|
11
|
+
^\s*#{tag}\s+'(?<partial>[^']*)'|
|
|
12
|
+
^\s*#{tag}\s+"(?<partial>[^"]*)"
|
|
13
|
+
/mix
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
PARTIAL_RENDER = partial_tag('render')
|
|
17
|
+
PARTIAL_INCLUDE = partial_tag('include')
|
|
18
|
+
PARTIAL_SECTION = partial_tag('section')
|
|
19
|
+
|
|
20
|
+
ASSET_INCLUDE = /
|
|
21
|
+
\{\{-?\s*'(?<partial>[^']*)'\s*\|\s*asset_url|
|
|
22
|
+
\{\{-?\s*"(?<partial>[^"]*)"\s*\|\s*asset_url|
|
|
23
|
+
|
|
24
|
+
# in liquid tags the whole line is white space until the asset partial
|
|
25
|
+
^\s*(?:echo|assign[^=]*=)\s*'(?<partial>[^']*)'\s*\|\s*asset_url|
|
|
26
|
+
^\s*(?:echo|assign[^=]*=)\s*"(?<partial>[^"]*)"\s*\|\s*asset_url
|
|
27
|
+
/mix
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PlatformosCheck
|
|
4
|
+
module LanguageServer
|
|
5
|
+
class Diagnostic
|
|
6
|
+
include URIHelper
|
|
7
|
+
|
|
8
|
+
attr_reader :offense
|
|
9
|
+
|
|
10
|
+
def initialize(offense)
|
|
11
|
+
@offense = offense
|
|
12
|
+
@diagnostic = nil
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def ==(other)
|
|
16
|
+
case other
|
|
17
|
+
when Hash, Diagnostic
|
|
18
|
+
to_h == other.to_h
|
|
19
|
+
else
|
|
20
|
+
raise ArgumentError
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def to_h
|
|
25
|
+
return @diagnostic unless @diagnostic.nil?
|
|
26
|
+
|
|
27
|
+
@diagnostic = {
|
|
28
|
+
source: "platformos-check",
|
|
29
|
+
code:,
|
|
30
|
+
message:,
|
|
31
|
+
range:,
|
|
32
|
+
severity:,
|
|
33
|
+
data:
|
|
34
|
+
}
|
|
35
|
+
@diagnostic[:codeDescription] = code_description unless offense.doc.nil?
|
|
36
|
+
@diagnostic
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def to_s
|
|
40
|
+
to_h.to_s
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def single_file?
|
|
44
|
+
offense.single_file?
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def whole_platformos_app?
|
|
48
|
+
offense.whole_platformos_app?
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def correctable?
|
|
52
|
+
offense.correctable?
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def code
|
|
56
|
+
offense.code_name
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def message
|
|
60
|
+
offense.message
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def code_description
|
|
64
|
+
{
|
|
65
|
+
href: offense.doc
|
|
66
|
+
}
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def severity
|
|
70
|
+
case offense.severity
|
|
71
|
+
when :error
|
|
72
|
+
1
|
|
73
|
+
when :suggestion
|
|
74
|
+
2
|
|
75
|
+
when :style
|
|
76
|
+
3
|
|
77
|
+
else
|
|
78
|
+
4
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def range
|
|
83
|
+
{
|
|
84
|
+
start: {
|
|
85
|
+
line: offense.start_row,
|
|
86
|
+
character: offense.start_column
|
|
87
|
+
},
|
|
88
|
+
end: {
|
|
89
|
+
line: offense.end_row,
|
|
90
|
+
character: offense.end_column
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def start_index
|
|
96
|
+
offense.start_index
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def end_index
|
|
100
|
+
offense.end_index
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def absolute_path
|
|
104
|
+
@absolute_path ||= offense&.app_file&.path
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def relative_path
|
|
108
|
+
@relative_path ||= offense&.app_file&.relative_path
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def uri
|
|
112
|
+
@uri ||= absolute_path && file_uri(absolute_path)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def file_version
|
|
116
|
+
@version ||= offense&.version
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def data
|
|
120
|
+
{
|
|
121
|
+
absolute_path: absolute_path.to_s,
|
|
122
|
+
relative_path: relative_path.to_s,
|
|
123
|
+
uri:,
|
|
124
|
+
version: file_version
|
|
125
|
+
}
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PlatformosCheck
|
|
4
|
+
module LanguageServer
|
|
5
|
+
class DiagnosticsEngine
|
|
6
|
+
include URIHelper
|
|
7
|
+
|
|
8
|
+
attr_reader :storage
|
|
9
|
+
|
|
10
|
+
def initialize(storage, bridge, diagnostics_manager = DiagnosticsManager.new)
|
|
11
|
+
@diagnostics_lock = Mutex.new
|
|
12
|
+
@diagnostics_manager = diagnostics_manager
|
|
13
|
+
@storage = storage
|
|
14
|
+
@bridge = bridge
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def first_run?
|
|
18
|
+
@diagnostics_manager.first_run?
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def analyze_and_send_offenses(absolute_path_or_paths, config, force: false, only_single_file: false)
|
|
22
|
+
return unless @diagnostics_lock.try_lock
|
|
23
|
+
|
|
24
|
+
platformos_app = PlatformosCheck::App.new(storage)
|
|
25
|
+
analyzer = PlatformosCheck::Analyzer.new(platformos_app, config.enabled_checks)
|
|
26
|
+
|
|
27
|
+
if !only_single_file && (@diagnostics_manager.first_run? || force)
|
|
28
|
+
run_full_platformos_check(analyzer)
|
|
29
|
+
else
|
|
30
|
+
run_partial_platformos_check(absolute_path_or_paths, platformos_app, analyzer, only_single_file)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
@diagnostics_lock.unlock
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def clear_diagnostics(relative_paths)
|
|
37
|
+
return unless @diagnostics_lock.try_lock
|
|
38
|
+
|
|
39
|
+
as_array(relative_paths).each do |relative_path|
|
|
40
|
+
send_clearing_diagnostics(relative_path)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
@diagnostics_lock.unlock
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
def run_full_platformos_check(analyzer)
|
|
49
|
+
raise 'Unsafe operation' unless @diagnostics_lock.owned?
|
|
50
|
+
|
|
51
|
+
token = @bridge.send_create_work_done_progress_request
|
|
52
|
+
@bridge.send_work_done_progress_begin(token, "Full platformos_app check")
|
|
53
|
+
@bridge.log("Checking #{storage.root}")
|
|
54
|
+
offenses = nil
|
|
55
|
+
time = Benchmark.measure do
|
|
56
|
+
offenses = analyzer.analyze_platformos_app do |path, i, total|
|
|
57
|
+
@bridge.send_work_done_progress_report(token, "#{i}/#{total} #{path}", (i.to_f / total * 100.0).to_i)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end_message = "Found #{offenses.size} offenses in #{format("%0.2f", time.real)}s"
|
|
61
|
+
@bridge.send_work_done_progress_end(token, end_message)
|
|
62
|
+
@bridge.log(end_message)
|
|
63
|
+
send_diagnostics(offenses)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def run_partial_platformos_check(absolute_path_or_paths, platformos_app, analyzer, only_single_file)
|
|
67
|
+
raise 'Unsafe operation' unless @diagnostics_lock.owned?
|
|
68
|
+
|
|
69
|
+
relative_paths = as_array(absolute_path_or_paths).map do |absolute_path|
|
|
70
|
+
Pathname.new(storage.relative_path(absolute_path))
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
app_files = relative_paths
|
|
74
|
+
.map { |relative_path| platformos_app[relative_path] }
|
|
75
|
+
.reject(&:nil?)
|
|
76
|
+
|
|
77
|
+
deleted_relative_paths = relative_paths - app_files.map(&:relative_path)
|
|
78
|
+
deleted_relative_paths
|
|
79
|
+
.each { |p| send_clearing_diagnostics(p) }
|
|
80
|
+
|
|
81
|
+
token = @bridge.send_create_work_done_progress_request
|
|
82
|
+
@bridge.send_work_done_progress_begin(token, "Partial platformos_app check")
|
|
83
|
+
offenses = nil
|
|
84
|
+
time = Benchmark.measure do
|
|
85
|
+
offenses = analyzer.analyze_files(app_files, only_single_file:) do |path, i, total|
|
|
86
|
+
@bridge.send_work_done_progress_report(token, "#{i}/#{total} #{path}", (i.to_f / total * 100.0).to_i)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end_message = "Found #{offenses.size} new offenses in #{format("%0.2f", time.real)}s"
|
|
90
|
+
@bridge.send_work_done_progress_end(token, end_message)
|
|
91
|
+
@bridge.log(end_message)
|
|
92
|
+
send_diagnostics(offenses, app_files.map(&:relative_path), only_single_file:)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def send_clearing_diagnostics(relative_path)
|
|
96
|
+
raise 'Unsafe operation' unless @diagnostics_lock.owned?
|
|
97
|
+
|
|
98
|
+
relative_path = Pathname.new(relative_path) unless relative_path.is_a?(Pathname)
|
|
99
|
+
@diagnostics_manager.clear_diagnostics(relative_path)
|
|
100
|
+
send_diagnostic(relative_path, DiagnosticsManager::NO_DIAGNOSTICS)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def as_array(maybe_array)
|
|
104
|
+
case maybe_array
|
|
105
|
+
when Array
|
|
106
|
+
maybe_array
|
|
107
|
+
else
|
|
108
|
+
[maybe_array]
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def send_diagnostics(offenses, analyzed_files = nil, only_single_file: false)
|
|
113
|
+
@diagnostics_manager.build_diagnostics(
|
|
114
|
+
offenses,
|
|
115
|
+
analyzed_files:,
|
|
116
|
+
only_single_file:
|
|
117
|
+
).each do |relative_path, diagnostics|
|
|
118
|
+
send_diagnostic(relative_path, diagnostics)
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def send_diagnostic(relative_path, diagnostics)
|
|
123
|
+
# https://microsoft.github.io/language-server-protocol/specifications/specification-current/#notificationMessage
|
|
124
|
+
@bridge.send_notification('textDocument/publishDiagnostics', {
|
|
125
|
+
uri: file_uri(storage.path(relative_path)),
|
|
126
|
+
diagnostics: diagnostics.map(&:to_h)
|
|
127
|
+
})
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "logger"
|
|
4
|
+
|
|
5
|
+
module PlatformosCheck
|
|
6
|
+
module LanguageServer
|
|
7
|
+
class DiagnosticsManager
|
|
8
|
+
# The empty array is used in the protocol to mean that no
|
|
9
|
+
# diagnostics exist for this file. It's not always evident when
|
|
10
|
+
# reading code.
|
|
11
|
+
NO_DIAGNOSTICS = [].freeze
|
|
12
|
+
|
|
13
|
+
# This class exists to facilitate LanguageServer diagnostics tracking.
|
|
14
|
+
#
|
|
15
|
+
# Motivations:
|
|
16
|
+
# 1. The first time we lint, we want all the errors from all the files.
|
|
17
|
+
# 2. If we fix all the errors in a file, we have to send an empty array for that file.
|
|
18
|
+
# 3. If we do a partial check, we should consider the whole platformos_app diagnostics as valid, and return cached results
|
|
19
|
+
# 4. We should be able to create WorkspaceEdits from diagnostics, so that the ExecuteCommandEngine can do its job
|
|
20
|
+
# 5. We should clean up diagnostics that were applied by the client
|
|
21
|
+
def initialize
|
|
22
|
+
@latest_diagnostics = {} # { [Pathname(relative_path)] => Diagnostic[] }
|
|
23
|
+
@mutex = Mutex.new
|
|
24
|
+
@first_run = true
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def first_run?
|
|
28
|
+
@first_run
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def diagnostics(relative_path)
|
|
32
|
+
relative_path = Pathname.new(relative_path) if relative_path.is_a?(String)
|
|
33
|
+
@mutex.synchronize { @latest_diagnostics[relative_path] || [] }
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def build_diagnostics(offenses, analyzed_files: nil, only_single_file: false)
|
|
37
|
+
@mutex.synchronize do
|
|
38
|
+
full_check = analyzed_files.nil?
|
|
39
|
+
analyzed_paths = analyzed_files.map { |path| Pathname.new(path) } unless full_check
|
|
40
|
+
|
|
41
|
+
# When analyzed_files is nil, contains all offenses.
|
|
42
|
+
# When analyzed_files is !nil, contains all whole platformos_app offenses and single file offenses of the analyzed_files.
|
|
43
|
+
current_diagnostics = offenses
|
|
44
|
+
.select(&:app_file)
|
|
45
|
+
.group_by(&:app_file)
|
|
46
|
+
.transform_keys { |app_file| Pathname.new(app_file.relative_path) }
|
|
47
|
+
.transform_values do |app_file_offenses|
|
|
48
|
+
app_file_offenses.map { |o| Diagnostic.new(o) }
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
previous_paths = paths(@latest_diagnostics)
|
|
52
|
+
current_paths = paths(current_diagnostics)
|
|
53
|
+
|
|
54
|
+
diagnostics_update = (current_paths + previous_paths).map do |path|
|
|
55
|
+
# When doing single file checks, we keep the whole platformos_app old
|
|
56
|
+
# ones and accept the new single ones
|
|
57
|
+
if only_single_file && analyzed_paths.include?(path)
|
|
58
|
+
single_file_diagnostics = current_diagnostics[path] || NO_DIAGNOSTICS
|
|
59
|
+
whole_platformos_app_diagnostics = whole_platformos_app_diagnostics(path) || NO_DIAGNOSTICS
|
|
60
|
+
[path, single_file_diagnostics + whole_platformos_app_diagnostics]
|
|
61
|
+
|
|
62
|
+
# If doing single file checks that are not in the
|
|
63
|
+
# analyzed_paths array then we just keep the old
|
|
64
|
+
# diagnostics
|
|
65
|
+
elsif only_single_file
|
|
66
|
+
[path, previous_diagnostics(path) || NO_DIAGNOSTICS]
|
|
67
|
+
|
|
68
|
+
# When doing a full_check, we either send the current
|
|
69
|
+
# diagnostics or an empty array to clear the diagnostics
|
|
70
|
+
# for that file.
|
|
71
|
+
elsif full_check
|
|
72
|
+
[path, current_diagnostics[path] || NO_DIAGNOSTICS]
|
|
73
|
+
|
|
74
|
+
# When doing a partial check, the single file diagnostics
|
|
75
|
+
# from the previous runs should be sent. Otherwise the
|
|
76
|
+
# latest results are the good ones.
|
|
77
|
+
else
|
|
78
|
+
new_diagnostics = current_diagnostics[path] || NO_DIAGNOSTICS
|
|
79
|
+
should_use_cached_results = !analyzed_paths.include?(path)
|
|
80
|
+
old_diagnostics = should_use_cached_results ? single_file_diagnostics(path) : []
|
|
81
|
+
[path, new_diagnostics + old_diagnostics]
|
|
82
|
+
end
|
|
83
|
+
end.to_h
|
|
84
|
+
|
|
85
|
+
@latest_diagnostics = diagnostics_update
|
|
86
|
+
.reject { |_, v| v.empty? }
|
|
87
|
+
|
|
88
|
+
@first_run = false
|
|
89
|
+
|
|
90
|
+
# Only send updates for the current file when running with only_single_file
|
|
91
|
+
diagnostics_update
|
|
92
|
+
.reject { |p, _| only_single_file && !analyzed_paths.include?(p) }
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def workspace_edit(diagnostics)
|
|
97
|
+
diagnostics = sanitize(diagnostics)
|
|
98
|
+
.select(&:correctable?)
|
|
99
|
+
|
|
100
|
+
{
|
|
101
|
+
documentChanges: document_changes(diagnostics)
|
|
102
|
+
}
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def delete_applied(diagnostics)
|
|
106
|
+
diagnostics = sanitize(diagnostics)
|
|
107
|
+
.select(&:correctable?)
|
|
108
|
+
|
|
109
|
+
previous_paths = paths(@latest_diagnostics)
|
|
110
|
+
|
|
111
|
+
diagnostics.each do |diagnostic|
|
|
112
|
+
delete(diagnostic.relative_path, diagnostic)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
current_paths = paths(@latest_diagnostics)
|
|
116
|
+
|
|
117
|
+
(current_paths + previous_paths).map do |path|
|
|
118
|
+
[path, @latest_diagnostics[path] || []]
|
|
119
|
+
end.to_h
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# For when you know there shouldn't be anything on that file
|
|
123
|
+
# anymore. (e.g. file delete or file rename)
|
|
124
|
+
def clear_diagnostics(relative_path)
|
|
125
|
+
relative_path = sanitize_path(relative_path)
|
|
126
|
+
@latest_diagnostics.delete(relative_path)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
private
|
|
130
|
+
|
|
131
|
+
def sanitize(diagnostics)
|
|
132
|
+
diagnostics = diagnostics.map { |hash| find(hash) }.reject(&:nil?) if diagnostics[0].is_a?(Hash)
|
|
133
|
+
diagnostics
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def sanitize_path(relative_path)
|
|
137
|
+
case relative_path
|
|
138
|
+
when String
|
|
139
|
+
Pathname.new(relative_path)
|
|
140
|
+
else
|
|
141
|
+
relative_path
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def delete(relative_path, diagnostic)
|
|
146
|
+
relative_path = sanitize_path(relative_path)
|
|
147
|
+
@mutex.synchronize do
|
|
148
|
+
@latest_diagnostics[relative_path]&.delete(diagnostic)
|
|
149
|
+
@latest_diagnostics.delete(relative_path) if @latest_diagnostics[relative_path] && @latest_diagnostics[relative_path].empty?
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def find(diagnostic_hash)
|
|
154
|
+
diagnostics(diagnostic_hash.dig(:data, :relative_path))
|
|
155
|
+
.find { |d| d == diagnostic_hash }
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def document_changes(diagnostics)
|
|
159
|
+
corrector = DocumentChangeCorrector.new
|
|
160
|
+
diagnostics.each do |diagnostic|
|
|
161
|
+
offense = diagnostic.offense
|
|
162
|
+
offense.correct(corrector)
|
|
163
|
+
end
|
|
164
|
+
corrector.document_changes
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def paths(diagnostics)
|
|
168
|
+
(diagnostics || {}).keys.map { |path| Pathname.new(path) }.to_set
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def single_file_diagnostics(relative_path)
|
|
172
|
+
@latest_diagnostics[relative_path]&.select(&:single_file?) || []
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def whole_platformos_app_diagnostics(relative_path)
|
|
176
|
+
@latest_diagnostics[relative_path]&.select(&:whole_platformos_app?) || []
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def previous_diagnostics(relative_path)
|
|
180
|
+
@latest_diagnostics[relative_path]
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
end
|