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
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,197 @@
|
|
1
|
+
# How to correct code with Code Actions, Commands and Workspace Edits
|
2
|
+
|
3
|
+
The [Language Server Protocol (LSP)][lsp] empowers Language Server developers to offer refactorings, quick fixes and the execution of commands to their users. In PlatformOS Check, we take advantage of this to offer quick fixes and autocorrection to users.
|
4
|
+
|
5
|
+
This document exists to give you an overview of how that works.
|
6
|
+
|
7
|
+
## Overview
|
8
|
+
|
9
|
+
It goes like this:
|
10
|
+
|
11
|
+
1. The Client (VS Code, vim, etc.) asks the Server for [Code Actions](#code-actions) at a character position, and the server responds with available `quickfix` and `source.fixAll` code actions.
|
12
|
+
2. The User _may_ select one of the code actions by interacting with the UI (see [visual examples](#code-actions)). The selection triggers the Client to asks the Server to execute a `correction` [Command](#Commands) for certain [Diagnostics](#diagnostic) on behalf of the user.
|
13
|
+
3. The Server asks the Client to apply a [Workspace Edit](#workspace-edits) to fix the diagnostics.
|
14
|
+
|
15
|
+
## Sequence Diagram
|
16
|
+
|
17
|
+
<img src="code-action-flow.png" width="700">
|
18
|
+
|
19
|
+
<details>
|
20
|
+
<summary>Sequence Diagram Explanation</summary>
|
21
|
+
|
22
|
+
1. The Client asks the Server for [code actions](#code-actions) for the current file and character range.
|
23
|
+
2. The Server responds with a list of code actions that can be applied for this location and character range. (`quickfix` and `source.fixAll`)
|
24
|
+
3. ...Wait for user input...
|
25
|
+
4. The User selects one of the code actions
|
26
|
+
5. The Client sends a `workspace/executeCommand` request to the Server for this code action.
|
27
|
+
6. The Server figures out the [workspace edit](#workspace-edits) for the command and arguments.
|
28
|
+
7. The Server sends a `workspace/applyEdit` request for the file modifications that would fix the diagnostics.
|
29
|
+
8. The Client responds with the status of the `applyEdit`.
|
30
|
+
9. The Server cleans up its internal representation of the diagnostics and updates the client with the latest diagnostics.
|
31
|
+
11. The Server responds to the `workspace/executeCommand` request.
|
32
|
+
|
33
|
+
</details>
|
34
|
+
|
35
|
+
## What it's like in the code
|
36
|
+
|
37
|
+
We handle two Client->Server LSP requests:
|
38
|
+
|
39
|
+
1. On `textDocument/codeAction`, our `CodeActionEngine` returns the results obtained from all `CodeActionProvider`.
|
40
|
+
2. On `workspace/executeCommand`, our `ExecuteCommandEngine` dispatches the command and arguments to appropriate `ExecuteCommandProvider`.
|
41
|
+
|
42
|
+
We define providers:
|
43
|
+
|
44
|
+
- Two `CodeActionProvider`:
|
45
|
+
1. [`QuickFixCodeActionProvider`](/lib/platformos_check/language_server/code_action_providers/quickfix_code_action_provider.rb) - This one provides code actions that fix _one_ diagnostic.
|
46
|
+
2. [`SourceFixAllCodeActionProvider`](/lib/platformos_check/language_server/code_action_providers/source_fix_all_code_action_provider.rb) - This one provides code actions that fix _all diagnostics in the current file_.
|
47
|
+
- One `ExecuteCommandProvider`:
|
48
|
+
1. [`CorrectionExecuteCommandProvider`](/lib/platformos_check/language_server/execute_command_providers/correction_execute_command_provider.rb) - This one takes a list of diagnostics as arguments, turns them into a [WorkspaceEdit](#workspace-edit) and tries to apply them with the server->client `workspace/applyEdit` request.
|
49
|
+
|
50
|
+
We define a [`DocumentChangeCorrector`](/lib/platformos_check/language_server/document_change_corrector.rb) (an LSP analog to our [`Corrector`](/lib/platformos_check/corrector.rb) class). This class turns corrector calls into document changes supported by the LSP. For more details, see the [LSP reference on resource changes][lspresourcechange].
|
51
|
+
|
52
|
+
## Definitions
|
53
|
+
|
54
|
+
### Code Actions
|
55
|
+
|
56
|
+
A [CodeAction][lspcodeaction] is the Language Server Protocol construct for "stuff you might want to do on the code."
|
57
|
+
|
58
|
+
Think refactoring, running tests, fixing lint errors, etc.
|
59
|
+
|
60
|
+
The client figures out which one it can run by executing the client->server `textDocument/codeAction` request.
|
61
|
+
|
62
|
+
<details>
|
63
|
+
<summary>Visual Examples</summary>
|
64
|
+
|
65
|
+
In VS Code, code actions of different kinds have special meanings and are mapped to multiple places in the UI.
|
66
|
+
|
67
|
+
* `Quick Fix...` button on diagnostic hover
|
68
|
+
|
69
|
+
<img src="code-action-quickfix.png" width=500>
|
70
|
+
|
71
|
+
* Diagnostic right click in the problems tab
|
72
|
+
|
73
|
+
<img src="code-action-problem.png" width=500>
|
74
|
+
|
75
|
+
* Command palette `Quick Fix...` and `Fix All`
|
76
|
+
|
77
|
+
<img src="code-action-command-palette.png" width=500>
|
78
|
+
|
79
|
+
* Keyboard shortcuts
|
80
|
+
|
81
|
+
<img src="code-action-keyboard.png" width=700>
|
82
|
+
</details>
|
83
|
+
|
84
|
+
<details>
|
85
|
+
<summary>TypeScript Interface</summary>
|
86
|
+
|
87
|
+
```ts
|
88
|
+
interface CodeAction {
|
89
|
+
title: string; // UI string, human readable for the action
|
90
|
+
kind?: CodeActionKind; // OPTIONAL, for filtering
|
91
|
+
diagnostics?: Diagnostic[]; // The diagnostics that the action SOLVES.
|
92
|
+
isPreferred?: boolean; // Are used by auto fix and can be targetted by keybindings.
|
93
|
+
// Shown as faded out in the code action menu when the user request a more specific type of code action
|
94
|
+
disabled?: {
|
95
|
+
reason: string;
|
96
|
+
},
|
97
|
+
|
98
|
+
// if both edit and command are present, edit is run first then command.
|
99
|
+
// I think edit is used so the client performs the change, wheras the command
|
100
|
+
// would be done by the server
|
101
|
+
edit?: WorkspaceEdit; // what this action does ??!!
|
102
|
+
command?: Command; // the command that it executes
|
103
|
+
data?: any; // sent from the CodeAction to the codeAction/resolve.
|
104
|
+
}
|
105
|
+
|
106
|
+
interface Command {
|
107
|
+
title: string; // Title of the command, like `save`
|
108
|
+
command: string; // id
|
109
|
+
arguments?: any[]
|
110
|
+
}
|
111
|
+
```
|
112
|
+
</details>
|
113
|
+
|
114
|
+
---
|
115
|
+
|
116
|
+
### Commands
|
117
|
+
|
118
|
+
A [Command][lspcommand] is the Language Server Protocol representation of a command that can be executed by the Server.
|
119
|
+
|
120
|
+
Think of them as data representation of function calls that can be made by the Client.
|
121
|
+
|
122
|
+
Typically, a command is associated with a Code Action. If the Client wants to perform this Code Action, it will make a Client->Server `workspace/executeCommand` request.
|
123
|
+
|
124
|
+
<details>
|
125
|
+
<summary>TypeScript Interface</summary>
|
126
|
+
|
127
|
+
```ts
|
128
|
+
interface Command {
|
129
|
+
title: string; // Title of the command, like `save`
|
130
|
+
command: string; // id
|
131
|
+
arguments?: any[]
|
132
|
+
}
|
133
|
+
```
|
134
|
+
</details>
|
135
|
+
|
136
|
+
---
|
137
|
+
|
138
|
+
### Workspace Edits
|
139
|
+
|
140
|
+
A [WorkspaceEdit][lspworkspaceedit] is the Language Server Protocol construct that abstracts code changes as data.
|
141
|
+
|
142
|
+
Think edit file, create file, delete file but as data.
|
143
|
+
|
144
|
+
<details>
|
145
|
+
<summary>TypeScript Interface</summary>
|
146
|
+
|
147
|
+
```ts
|
148
|
+
interface WorkspaceEdit {
|
149
|
+
changes?: { uri: TextEdit[] ];
|
150
|
+
documentChanges: (TextDocumentEdit | CreateFile | RenameFile | DeleteFile|)[];
|
151
|
+
changeAnnotations?: { [id: string]: ChangeAnnotation }
|
152
|
+
}
|
153
|
+
|
154
|
+
// see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#resourceChanges
|
155
|
+
interface CreateFile {
|
156
|
+
kind: 'create';
|
157
|
+
uri: DocumentUri;
|
158
|
+
options?: CreateFileOptions;
|
159
|
+
annotationId?: ChangeAnnotationIdentifier;
|
160
|
+
}
|
161
|
+
|
162
|
+
interface TextEdit {
|
163
|
+
range: Range;
|
164
|
+
newText: string; // can be empty to delete
|
165
|
+
}
|
166
|
+
|
167
|
+
interface TextDocumentEdit {
|
168
|
+
textDocument: OptionalVersionedTextDocumentIdentifier;
|
169
|
+
edits: (TextEdit | AnnotatedTextEdit)[]
|
170
|
+
}
|
171
|
+
|
172
|
+
interface OptionalVersionedTextDocumentIdentifier {
|
173
|
+
uri: TextDocumentURI;
|
174
|
+
// null is for when the file wasn't open from the client
|
175
|
+
// integer is for when you know what it was.
|
176
|
+
version: integer | null;
|
177
|
+
}
|
178
|
+
```
|
179
|
+
</details>
|
180
|
+
|
181
|
+
---
|
182
|
+
|
183
|
+
### Diagnostic
|
184
|
+
|
185
|
+
A [Diagnostic][lspdiagnostic] is the Language Server Protocol construct for errors, warnings and information bubbles.
|
186
|
+
|
187
|
+
In our case, diagnostics are PlatformOS Check offenses.
|
188
|
+
|
189
|
+
They appear in the Problems tab and in the gutter of the editor.
|
190
|
+
|
191
|
+
[lsp]: https://microsoft.github.io/language-server-protocol/specification
|
192
|
+
[lspcodeaction]: https://microsoft.github.io/language-server-protocol/specification#textDocument_codeAction
|
193
|
+
[lspcommand]: https://microsoft.github.io/language-server-protocol/specification#command
|
194
|
+
[lspexecutecommand]: https://microsoft.github.io/language-server-protocol/specification#workspace_executeCommand
|
195
|
+
[lspworkspaceedit]: https://microsoft.github.io/language-server-protocol/specification#workspaceEdit
|
196
|
+
[lspdiagnostic]: https://microsoft.github.io/language-server-protocol/specifications/specification-3-17/#diagnostic
|
197
|
+
[lspresourcechange]: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#resourceChanges
|
data/docs/preview.png
ADDED
Binary file
|
@@ -0,0 +1,178 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PlatformosCheck
|
4
|
+
class Analyzer
|
5
|
+
def initialize(platformos_app, checks = Check.all.map(&:new), auto_correct = false)
|
6
|
+
@platformos_app = platformos_app
|
7
|
+
@auto_correct = auto_correct
|
8
|
+
|
9
|
+
@liquid_checks = Checks.new
|
10
|
+
@yaml_checks = Checks.new
|
11
|
+
@html_checks = Checks.new
|
12
|
+
|
13
|
+
checks.each do |check|
|
14
|
+
check.platformos_app = @platformos_app
|
15
|
+
|
16
|
+
case check
|
17
|
+
when LiquidCheck
|
18
|
+
@liquid_checks << check
|
19
|
+
when YamlCheck
|
20
|
+
@yaml_checks << check
|
21
|
+
when HtmlCheck
|
22
|
+
@html_checks << check
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def offenses
|
28
|
+
@liquid_checks.flat_map(&:offenses) +
|
29
|
+
@yaml_checks.flat_map(&:offenses) +
|
30
|
+
@html_checks.flat_map(&:offenses)
|
31
|
+
end
|
32
|
+
|
33
|
+
def yaml_file_count
|
34
|
+
@yaml_file_count ||= @platformos_app.yaml.size
|
35
|
+
end
|
36
|
+
|
37
|
+
def liquid_file_count
|
38
|
+
@liquid_file_count ||= @platformos_app.liquid.size
|
39
|
+
end
|
40
|
+
|
41
|
+
def total_file_count
|
42
|
+
yaml_file_count + liquid_file_count
|
43
|
+
end
|
44
|
+
|
45
|
+
# Returns all offenses for all files in platformos_app
|
46
|
+
def analyze_platformos_app
|
47
|
+
reset
|
48
|
+
|
49
|
+
liquid_visitor = LiquidVisitor.new(@liquid_checks, @disabled_checks)
|
50
|
+
html_visitor = HtmlVisitor.new(@html_checks)
|
51
|
+
|
52
|
+
PlatformosCheck.with_liquid_c_disabled do
|
53
|
+
@platformos_app.liquid.each_with_index do |liquid_file, i|
|
54
|
+
yield(liquid_file.relative_path.to_s, i, total_file_count) if block_given?
|
55
|
+
liquid_visitor.visit_liquid_file(liquid_file)
|
56
|
+
html_visitor.visit_liquid_file(liquid_file)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
@platformos_app.yaml.each_with_index do |yaml_file, i|
|
61
|
+
yield(yaml_file.relative_path.to_s, liquid_file_count + i, total_file_count) if block_given?
|
62
|
+
@yaml_checks.call(:on_file, yaml_file)
|
63
|
+
end
|
64
|
+
|
65
|
+
finish(false)
|
66
|
+
|
67
|
+
offenses
|
68
|
+
end
|
69
|
+
|
70
|
+
# When only_single_file is false:
|
71
|
+
# Runs single file checks for each file in `files`
|
72
|
+
# Runs whole platformos_app checks
|
73
|
+
# Returns single file checks offenses for file in `files` + whole platformos_app checks
|
74
|
+
# When only_single_file is true:
|
75
|
+
# Runs single file checks for each file in `files`
|
76
|
+
# Does not run whole platformos_app checks
|
77
|
+
# Returns single file checks offenses for file in `files`
|
78
|
+
# When files is empty and only_single_file is false:
|
79
|
+
# Only returns whole platformos_app checks
|
80
|
+
# When files is empty and only_single_file is true:
|
81
|
+
# Returns empty array
|
82
|
+
def analyze_files(files, only_single_file: false)
|
83
|
+
reset
|
84
|
+
|
85
|
+
PlatformosCheck.with_liquid_c_disabled do
|
86
|
+
total = files.size
|
87
|
+
offset = 0
|
88
|
+
|
89
|
+
unless only_single_file
|
90
|
+
# Call all checks that run on the whole platformos_app
|
91
|
+
liquid_visitor = LiquidVisitor.new(@liquid_checks.whole_platformos_app, @disabled_checks)
|
92
|
+
html_visitor = HtmlVisitor.new(@html_checks.whole_platformos_app)
|
93
|
+
total += total_file_count
|
94
|
+
offset = total_file_count
|
95
|
+
@platformos_app.liquid.each_with_index do |liquid_file, i|
|
96
|
+
yield(liquid_file.relative_path.to_s, i, total) if block_given?
|
97
|
+
liquid_visitor.visit_liquid_file(liquid_file)
|
98
|
+
html_visitor.visit_liquid_file(liquid_file)
|
99
|
+
end
|
100
|
+
|
101
|
+
@platformos_app.yaml.each_with_index do |yaml_file, i|
|
102
|
+
yield(yaml_file.relative_path.to_s, liquid_file_count + i, total) if block_given?
|
103
|
+
@yaml_checks.whole_platformos_app.call(:on_file, yaml_file)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
# Call checks that run on a single files, only on specified file
|
108
|
+
liquid_visitor = LiquidVisitor.new(@liquid_checks.single_file, @disabled_checks)
|
109
|
+
html_visitor = HtmlVisitor.new(@html_checks.single_file)
|
110
|
+
files.each_with_index do |app_file, i|
|
111
|
+
yield(app_file.relative_path.to_s, offset + i, total) if block_given?
|
112
|
+
if app_file.liquid?
|
113
|
+
liquid_visitor.visit_liquid_file(app_file)
|
114
|
+
html_visitor.visit_liquid_file(app_file)
|
115
|
+
elsif app_file.yaml?
|
116
|
+
@yaml_checks.single_file.call(:on_file, app_file)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
finish(only_single_file)
|
122
|
+
|
123
|
+
offenses
|
124
|
+
end
|
125
|
+
|
126
|
+
def uncorrectable_offenses
|
127
|
+
return offenses unless @auto_correct
|
128
|
+
|
129
|
+
offenses.select { |offense| !offense.correctable? }
|
130
|
+
end
|
131
|
+
|
132
|
+
def correct_offenses
|
133
|
+
return unless @auto_correct
|
134
|
+
|
135
|
+
offenses.each(&:correct)
|
136
|
+
end
|
137
|
+
|
138
|
+
def write_corrections
|
139
|
+
return unless @auto_correct
|
140
|
+
|
141
|
+
@platformos_app.liquid.each(&:write)
|
142
|
+
end
|
143
|
+
|
144
|
+
private
|
145
|
+
|
146
|
+
def reset
|
147
|
+
@disabled_checks = DisabledChecks.new
|
148
|
+
|
149
|
+
@liquid_checks.each do |check|
|
150
|
+
check.offenses.clear
|
151
|
+
end
|
152
|
+
|
153
|
+
@html_checks.each do |check|
|
154
|
+
check.offenses.clear
|
155
|
+
end
|
156
|
+
|
157
|
+
@yaml_checks.each do |check|
|
158
|
+
check.offenses.clear
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def finish(only_single_file = false)
|
163
|
+
if only_single_file
|
164
|
+
@liquid_checks.single_file.call(:on_end)
|
165
|
+
@html_checks.single_file.call(:on_end)
|
166
|
+
@yaml_checks.single_file.call(:on_end)
|
167
|
+
else
|
168
|
+
@liquid_checks.call(:on_end)
|
169
|
+
@html_checks.call(:on_end)
|
170
|
+
@yaml_checks.call(:on_end)
|
171
|
+
end
|
172
|
+
|
173
|
+
@disabled_checks.remove_disabled_offenses(@liquid_checks)
|
174
|
+
@disabled_checks.remove_disabled_offenses(@yaml_checks)
|
175
|
+
@disabled_checks.remove_disabled_offenses(@html_checks)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# frozen_string_lite/al: true
|
4
|
+
|
5
|
+
require "pathname"
|
6
|
+
|
7
|
+
module PlatformosCheck
|
8
|
+
class App
|
9
|
+
API_CALLS_REGEX = %r{\A(?-mix:^/?((marketplace_builder|app)/|modules/(.+)(private|public|marketplace_builder|app)/)?)(notifications/api_call_notifications|api_calls)/(.+)\.liquid\z}
|
10
|
+
ASSETS_REGEX = %r{\A(?-mix:^/?((marketplace_builder|app)/|modules/(.+)(private|public|marketplace_builder|app)/)?)assets/}
|
11
|
+
EMAILS_REGEX = %r{\A(?-mix:^/?((marketplace_builder|app)/|modules/(.+)(private|public|marketplace_builder|app)/)?)(notifications/email_notifications|emails)/(.+)\.liquid\z}
|
12
|
+
GRAPHQL_REGEX = %r{\A(?-mix:^/?((marketplace_builder|app)/|modules/(.+)(private|public|marketplace_builder|app)/)?)(graph_queries|graphql)s?/(.+)\.graphql\z}
|
13
|
+
|
14
|
+
MIGRATIONS_REGEX = %r{\A(?-mix:^/?((marketplace_builder|app)/|modules/(.+)(private|public|marketplace_builder|app)/)?)migrations/(.+)\.liquid\z}
|
15
|
+
PAGES_REGEX = %r{\A(?-mix:^/?((marketplace_builder|app)/|modules/(.+)(private|public|marketplace_builder|app)/)?)(pages|views/pages)/(.+)}
|
16
|
+
PARTIALS_REGEX = %r{\A(?-mix:^/?((marketplace_builder|app)/|modules/(.+)(private|public|marketplace_builder|app)/)?)(views/partials|lib)/(.+)}
|
17
|
+
LAYOUTS_REGEX = %r{\A(?-mix:^/?((marketplace_builder|app)/|modules/(.+)(private|public|marketplace_builder|app)/)?)(views/layouts)/(.+)}
|
18
|
+
SCHEMA_REGEX = %r{\A(?-mix:^/?((marketplace_builder|app)/|modules/(.+)(private|public|marketplace_builder|app)/)?)(custom_model_types|model_schemas|schema)/(.+)\.yml\z}
|
19
|
+
SMSES_REGEX = %r{\A(?-mix:^/?((marketplace_builder|app)/|modules/(.+)(private|public|marketplace_builder|app)/)?)(notifications/sms_notifications|smses)/(.+)\.liquid\z}
|
20
|
+
USER_SCHEMA_REGEX = %r{\A(?-mix:^/?((marketplace_builder|app)/)?)user.yml}
|
21
|
+
TRANSLATIONS_REGEX = %r{\A(?-mix:^/?((marketplace_builder|app)/|modules/(.+)(private|public|marketplace_builder|app)/)?)translations.+.yml}
|
22
|
+
CONFIG_REGEX = %r{(?-mix:^\\/?((marketplace_builder|app)\\/)?)config.yml}
|
23
|
+
|
24
|
+
REGEXP_MAP = {
|
25
|
+
API_CALLS_REGEX => ApiCallFile,
|
26
|
+
ASSETS_REGEX => AssetFile,
|
27
|
+
EMAILS_REGEX => EmailFile,
|
28
|
+
GRAPHQL_REGEX => GraphqlFile,
|
29
|
+
MIGRATIONS_REGEX => MigrationFile,
|
30
|
+
PAGES_REGEX => PageFile,
|
31
|
+
PARTIALS_REGEX => PartialFile,
|
32
|
+
LAYOUTS_REGEX => LayoutFile,
|
33
|
+
SCHEMA_REGEX => SchemaFile,
|
34
|
+
SMSES_REGEX => SmsFile,
|
35
|
+
USER_SCHEMA_REGEX => UserSchemaFile,
|
36
|
+
TRANSLATIONS_REGEX => TranslationFile,
|
37
|
+
CONFIG_REGEX => ConfigFile
|
38
|
+
}
|
39
|
+
|
40
|
+
attr_reader :storage
|
41
|
+
|
42
|
+
def initialize(storage)
|
43
|
+
@storage = storage
|
44
|
+
end
|
45
|
+
|
46
|
+
def grouped_files
|
47
|
+
@grouped_files ||= begin
|
48
|
+
hash = {}
|
49
|
+
REGEXP_MAP.each_value { |v| hash[v] = {} }
|
50
|
+
storage.files.each do |path|
|
51
|
+
regexp, klass = REGEXP_MAP.detect { |k, _v| k.match?(path) }
|
52
|
+
if regexp
|
53
|
+
f = klass.new(path, storage)
|
54
|
+
hash[klass][f.name] = f
|
55
|
+
elsif /\.liquid$/i.match?(path)
|
56
|
+
hash[LiquidFile] ||= {}
|
57
|
+
f = LiquidFile.new(path, storage)
|
58
|
+
hash[LiquidFile][f.name] = f
|
59
|
+
end
|
60
|
+
end
|
61
|
+
hash
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def assets
|
66
|
+
grouped_files[AssetFile]&.values
|
67
|
+
end
|
68
|
+
|
69
|
+
def liquid
|
70
|
+
layouts + partials + pages + notifications
|
71
|
+
end
|
72
|
+
|
73
|
+
def yaml
|
74
|
+
schema + translations
|
75
|
+
end
|
76
|
+
|
77
|
+
def schema
|
78
|
+
grouped_files[SchemaFile]&.values || []
|
79
|
+
end
|
80
|
+
|
81
|
+
def translations
|
82
|
+
grouped_files[TranslationFile]&.values || []
|
83
|
+
end
|
84
|
+
|
85
|
+
def partials
|
86
|
+
grouped_files[PartialFile]&.values || []
|
87
|
+
end
|
88
|
+
|
89
|
+
def layouts
|
90
|
+
grouped_files[LayoutFile]&.values || []
|
91
|
+
end
|
92
|
+
|
93
|
+
def notifications
|
94
|
+
emails + smses + api_calls
|
95
|
+
end
|
96
|
+
|
97
|
+
def emails
|
98
|
+
grouped_files[EmailFile]&.values || []
|
99
|
+
end
|
100
|
+
|
101
|
+
def smses
|
102
|
+
grouped_files[SmsFile]&.values || []
|
103
|
+
end
|
104
|
+
|
105
|
+
def api_calls
|
106
|
+
grouped_files[ApiCallFile]&.values || []
|
107
|
+
end
|
108
|
+
|
109
|
+
def pages
|
110
|
+
grouped_files[PageFile]&.values || []
|
111
|
+
end
|
112
|
+
|
113
|
+
def directories
|
114
|
+
storage.directories
|
115
|
+
end
|
116
|
+
|
117
|
+
def all
|
118
|
+
@all ||= grouped_files.values.map(&:values).flatten
|
119
|
+
end
|
120
|
+
|
121
|
+
def [](name_or_relative_path)
|
122
|
+
case name_or_relative_path
|
123
|
+
when Pathname
|
124
|
+
all.find { |t| t.relative_path == name_or_relative_path }
|
125
|
+
else
|
126
|
+
all.find { |t| t.name == name_or_relative_path }
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def sections
|
131
|
+
liquid.select(&:section?)
|
132
|
+
end
|
133
|
+
|
134
|
+
def snippets
|
135
|
+
liquid.select(&:snippet?)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "pathname"
|
4
|
+
|
5
|
+
module PlatformosCheck
|
6
|
+
class AppFile
|
7
|
+
attr_reader :version, :storage
|
8
|
+
|
9
|
+
def initialize(relative_path, storage)
|
10
|
+
@relative_path = relative_path
|
11
|
+
@storage = storage
|
12
|
+
@source = nil
|
13
|
+
@version = nil
|
14
|
+
@eol = "\n"
|
15
|
+
end
|
16
|
+
|
17
|
+
def path
|
18
|
+
@storage.path(@relative_path)
|
19
|
+
end
|
20
|
+
|
21
|
+
def relative_path
|
22
|
+
@relative_pathname ||= Pathname.new(@relative_path)
|
23
|
+
end
|
24
|
+
|
25
|
+
def name
|
26
|
+
@name ||= dir_prefix.nil? ? relative_path.sub_ext('').to_s : build_name
|
27
|
+
end
|
28
|
+
|
29
|
+
def build_name
|
30
|
+
n = relative_path.sub(dir_prefix, '').sub_ext('').to_s
|
31
|
+
return n if module_name.nil?
|
32
|
+
|
33
|
+
prefix = "modules#{File::SEPARATOR}#{module_name}#{File::SEPARATOR}"
|
34
|
+
return n if n.start_with?(prefix)
|
35
|
+
|
36
|
+
"#{prefix}#{n}"
|
37
|
+
end
|
38
|
+
|
39
|
+
def dir_prefix
|
40
|
+
nil
|
41
|
+
end
|
42
|
+
|
43
|
+
def module_name
|
44
|
+
@module_name ||= begin
|
45
|
+
dir_names = @relative_path.split(File::SEPARATOR).reject(&:empty?)
|
46
|
+
dir_names.first == 'modules' ? dir_names[1] : nil
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# For the corrector to work properly, we should have a
|
51
|
+
# simple mental model of the internal representation of eol
|
52
|
+
# characters (Windows uses \r\n, Linux uses \n).
|
53
|
+
#
|
54
|
+
# Parser::Source::Buffer strips the \r from the source file, so if
|
55
|
+
# you are autocorrecting the file you might lose that info and
|
56
|
+
# cause a git diff. It also makes the node.start_index/end_index
|
57
|
+
# calculation break. That's not cool.
|
58
|
+
#
|
59
|
+
# So in here we track whether the source file has \r\n in it and
|
60
|
+
# we'll make sure that the file we write has the same eol as the
|
61
|
+
# source file.
|
62
|
+
def source
|
63
|
+
return @source if @source
|
64
|
+
|
65
|
+
if @storage.versioned?
|
66
|
+
@source, @version = @storage.read_version(@relative_path)
|
67
|
+
else
|
68
|
+
@source = @storage.read(@relative_path)
|
69
|
+
end
|
70
|
+
@eol = @source.include?("\r\n") ? "\r\n" : "\n"
|
71
|
+
@source = @source
|
72
|
+
.gsub(/\r(?!\n)/, "\r\n") # fix rogue \r without followup \n with \r\n
|
73
|
+
.gsub("\r\n", "\n")
|
74
|
+
end
|
75
|
+
|
76
|
+
def yaml?
|
77
|
+
false
|
78
|
+
end
|
79
|
+
|
80
|
+
def liquid?
|
81
|
+
false
|
82
|
+
end
|
83
|
+
|
84
|
+
def ==(other)
|
85
|
+
other.is_a?(self.class) && relative_path == other.relative_path
|
86
|
+
end
|
87
|
+
alias eql? ==
|
88
|
+
end
|
89
|
+
end
|