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.
Files changed (240) hide show
  1. checksums.yaml +7 -0
  2. data/.dockerignore +2 -0
  3. data/.gitignore +22 -0
  4. data/.rubocop.yml +555 -0
  5. data/CHANGELOG.md +5 -0
  6. data/CODE_OF_CONDUCT.md +74 -0
  7. data/CONTRIBUTING.md +209 -0
  8. data/Gemfile +33 -0
  9. data/Guardfile +7 -0
  10. data/LICENSE.md +10 -0
  11. data/Makefile +18 -0
  12. data/README.md +189 -0
  13. data/RELEASING.md +35 -0
  14. data/Rakefile +83 -0
  15. data/TROUBLESHOOTING.md +35 -0
  16. data/bin/platformos-check +29 -0
  17. data/bin/platformos-check-language-server +29 -0
  18. data/config/default.yml +98 -0
  19. data/config/nothing.yml +11 -0
  20. data/data/platformos_liquid/built_in_liquid_objects.json +66 -0
  21. data/data/platformos_liquid/deprecated_filters.json +22 -0
  22. data/data/platformos_liquid/documentation/filters.json +6 -0
  23. data/data/platformos_liquid/documentation/latest.json +2 -0
  24. data/data/platformos_liquid/documentation/objects.json +6 -0
  25. data/data/platformos_liquid/documentation/tags.json +6 -0
  26. data/docker/check.Dockerfile +3 -0
  27. data/docker/lsp.Dockerfile +21 -0
  28. data/docs/api/check.md +15 -0
  29. data/docs/api/html_check.md +46 -0
  30. data/docs/api/liquid_check.md +115 -0
  31. data/docs/api/yaml_check.md +19 -0
  32. data/docs/checks/TEMPLATE.md.erb +52 -0
  33. data/docs/checks/convert_include_to_render.md +48 -0
  34. data/docs/checks/deprecated_filter.md +30 -0
  35. data/docs/checks/html_parsing_error.md +50 -0
  36. data/docs/checks/img_lazy_loading.md +63 -0
  37. data/docs/checks/img_width_and_height.md +79 -0
  38. data/docs/checks/invalid_args.md +56 -0
  39. data/docs/checks/liquid_tag.md +65 -0
  40. data/docs/checks/missing_enable_comment.md +50 -0
  41. data/docs/checks/missing_template.md +65 -0
  42. data/docs/checks/parse_json_format.md +76 -0
  43. data/docs/checks/parser_blocking_javascript.md +97 -0
  44. data/docs/checks/required_layout_object.md +28 -0
  45. data/docs/checks/space_inside_braces.md +89 -0
  46. data/docs/checks/syntax_error.md +49 -0
  47. data/docs/checks/template_length.md +45 -0
  48. data/docs/checks/undefined_object.md +71 -0
  49. data/docs/checks/unknown_filter.md +46 -0
  50. data/docs/checks/unused_assign.md +63 -0
  51. data/docs/checks/unused_partial.md +32 -0
  52. data/docs/checks/valid_yaml.md +48 -0
  53. data/docs/flamegraph.svg +18488 -0
  54. data/docs/language_server/code-action-command-palette.png +0 -0
  55. data/docs/language_server/code-action-flow.png +0 -0
  56. data/docs/language_server/code-action-keyboard.png +0 -0
  57. data/docs/language_server/code-action-light-bulb.png +0 -0
  58. data/docs/language_server/code-action-problem.png +0 -0
  59. data/docs/language_server/code-action-quickfix.png +0 -0
  60. data/docs/language_server/how_to_correct_code_with_code_actions_and_execute_command.md +197 -0
  61. data/docs/preview.png +0 -0
  62. data/exe/platformos-check +6 -0
  63. data/exe/platformos-check-language-server +7 -0
  64. data/lib/platformos_check/analyzer.rb +178 -0
  65. data/lib/platformos_check/api_call_file.rb +9 -0
  66. data/lib/platformos_check/app.rb +138 -0
  67. data/lib/platformos_check/app_file.rb +89 -0
  68. data/lib/platformos_check/app_file_rewriter.rb +79 -0
  69. data/lib/platformos_check/asset_file.rb +34 -0
  70. data/lib/platformos_check/bug.rb +23 -0
  71. data/lib/platformos_check/check.rb +163 -0
  72. data/lib/platformos_check/checks/TEMPLATE.rb.erb +11 -0
  73. data/lib/platformos_check/checks/convert_include_to_render.rb +17 -0
  74. data/lib/platformos_check/checks/deprecated_filter.rb +123 -0
  75. data/lib/platformos_check/checks/html_parsing_error.rb +13 -0
  76. data/lib/platformos_check/checks/img_lazy_loading.rb +18 -0
  77. data/lib/platformos_check/checks/img_width_and_height.rb +46 -0
  78. data/lib/platformos_check/checks/invalid_args.rb +81 -0
  79. data/lib/platformos_check/checks/liquid_tag.rb +47 -0
  80. data/lib/platformos_check/checks/missing_enable_comment.rb +37 -0
  81. data/lib/platformos_check/checks/missing_template.rb +107 -0
  82. data/lib/platformos_check/checks/parse_json_format.rb +31 -0
  83. data/lib/platformos_check/checks/parser_blocking_javascript.rb +17 -0
  84. data/lib/platformos_check/checks/required_layout_object.rb +41 -0
  85. data/lib/platformos_check/checks/space_inside_braces.rb +150 -0
  86. data/lib/platformos_check/checks/syntax_error.rb +31 -0
  87. data/lib/platformos_check/checks/template_length.rb +20 -0
  88. data/lib/platformos_check/checks/undefined_object.rb +206 -0
  89. data/lib/platformos_check/checks/unknown_filter.rb +27 -0
  90. data/lib/platformos_check/checks/unused_assign.rb +101 -0
  91. data/lib/platformos_check/checks/unused_partial.rb +93 -0
  92. data/lib/platformos_check/checks/valid_yaml.rb +16 -0
  93. data/lib/platformos_check/checks.rb +73 -0
  94. data/lib/platformos_check/checks_tracking.rb +9 -0
  95. data/lib/platformos_check/cli.rb +239 -0
  96. data/lib/platformos_check/config.rb +219 -0
  97. data/lib/platformos_check/config_file.rb +6 -0
  98. data/lib/platformos_check/corrector.rb +68 -0
  99. data/lib/platformos_check/disabled_check.rb +44 -0
  100. data/lib/platformos_check/disabled_checks.rb +96 -0
  101. data/lib/platformos_check/email_file.rb +9 -0
  102. data/lib/platformos_check/exceptions.rb +36 -0
  103. data/lib/platformos_check/file_system_storage.rb +93 -0
  104. data/lib/platformos_check/graphql_file.rb +68 -0
  105. data/lib/platformos_check/html_check.rb +8 -0
  106. data/lib/platformos_check/html_node.rb +210 -0
  107. data/lib/platformos_check/html_visitor.rb +36 -0
  108. data/lib/platformos_check/in_memory_storage.rb +68 -0
  109. data/lib/platformos_check/json_file.rb +57 -0
  110. data/lib/platformos_check/json_helper.rb +73 -0
  111. data/lib/platformos_check/json_helpers.rb +24 -0
  112. data/lib/platformos_check/json_printer.rb +32 -0
  113. data/lib/platformos_check/language_server/bridge.rb +167 -0
  114. data/lib/platformos_check/language_server/channel.rb +69 -0
  115. data/lib/platformos_check/language_server/client_capabilities.rb +27 -0
  116. data/lib/platformos_check/language_server/code_action_engine.rb +32 -0
  117. data/lib/platformos_check/language_server/code_action_provider.rb +41 -0
  118. data/lib/platformos_check/language_server/code_action_providers/quickfix_code_action_provider.rb +85 -0
  119. data/lib/platformos_check/language_server/code_action_providers/source_fix_all_code_action_provider.rb +41 -0
  120. data/lib/platformos_check/language_server/completion_context.rb +52 -0
  121. data/lib/platformos_check/language_server/completion_engine.rb +32 -0
  122. data/lib/platformos_check/language_server/completion_helper.rb +26 -0
  123. data/lib/platformos_check/language_server/completion_provider.rb +53 -0
  124. data/lib/platformos_check/language_server/completion_providers/assignments_completion_provider.rb +40 -0
  125. data/lib/platformos_check/language_server/completion_providers/filter_completion_provider.rb +102 -0
  126. data/lib/platformos_check/language_server/completion_providers/object_attribute_completion_provider.rb +48 -0
  127. data/lib/platformos_check/language_server/completion_providers/object_completion_provider.rb +38 -0
  128. data/lib/platformos_check/language_server/completion_providers/render_snippet_completion_provider.rb +50 -0
  129. data/lib/platformos_check/language_server/completion_providers/tag_completion_provider.rb +41 -0
  130. data/lib/platformos_check/language_server/configuration.rb +89 -0
  131. data/lib/platformos_check/language_server/constants.rb +29 -0
  132. data/lib/platformos_check/language_server/diagnostic.rb +129 -0
  133. data/lib/platformos_check/language_server/diagnostics_engine.rb +131 -0
  134. data/lib/platformos_check/language_server/diagnostics_manager.rb +184 -0
  135. data/lib/platformos_check/language_server/document_change_corrector.rb +271 -0
  136. data/lib/platformos_check/language_server/document_link_engine.rb +21 -0
  137. data/lib/platformos_check/language_server/document_link_provider.rb +71 -0
  138. data/lib/platformos_check/language_server/document_link_providers/asset_document_link_provider.rb +11 -0
  139. data/lib/platformos_check/language_server/document_link_providers/include_document_link_provider.rb +11 -0
  140. data/lib/platformos_check/language_server/document_link_providers/render_document_link_provider.rb +11 -0
  141. data/lib/platformos_check/language_server/document_link_providers/section_document_link_provider.rb +11 -0
  142. data/lib/platformos_check/language_server/execute_command_engine.rb +19 -0
  143. data/lib/platformos_check/language_server/execute_command_provider.rb +30 -0
  144. data/lib/platformos_check/language_server/execute_command_providers/correction_execute_command_provider.rb +48 -0
  145. data/lib/platformos_check/language_server/execute_command_providers/run_checks_execute_command_provider.rb +28 -0
  146. data/lib/platformos_check/language_server/handler.rb +310 -0
  147. data/lib/platformos_check/language_server/hover_engine.rb +32 -0
  148. data/lib/platformos_check/language_server/hover_provider.rb +53 -0
  149. data/lib/platformos_check/language_server/hover_providers/filter_hover_provider.rb +113 -0
  150. data/lib/platformos_check/language_server/io_messenger.rb +109 -0
  151. data/lib/platformos_check/language_server/messenger.rb +27 -0
  152. data/lib/platformos_check/language_server/protocol.rb +55 -0
  153. data/lib/platformos_check/language_server/server.rb +188 -0
  154. data/lib/platformos_check/language_server/tokens.rb +55 -0
  155. data/lib/platformos_check/language_server/type_helper.rb +22 -0
  156. data/lib/platformos_check/language_server/uri_helper.rb +39 -0
  157. data/lib/platformos_check/language_server/variable_lookup_finder/assignments_finder/node_handler.rb +87 -0
  158. data/lib/platformos_check/language_server/variable_lookup_finder/assignments_finder/scope.rb +60 -0
  159. data/lib/platformos_check/language_server/variable_lookup_finder/assignments_finder/scope_visitor.rb +44 -0
  160. data/lib/platformos_check/language_server/variable_lookup_finder/assignments_finder.rb +76 -0
  161. data/lib/platformos_check/language_server/variable_lookup_finder/constants.rb +44 -0
  162. data/lib/platformos_check/language_server/variable_lookup_finder/liquid_fixer.rb +103 -0
  163. data/lib/platformos_check/language_server/variable_lookup_finder/potential_lookup.rb +10 -0
  164. data/lib/platformos_check/language_server/variable_lookup_finder/tolerant_parser.rb +94 -0
  165. data/lib/platformos_check/language_server/variable_lookup_finder.rb +262 -0
  166. data/lib/platformos_check/language_server/variable_lookup_traverser.rb +70 -0
  167. data/lib/platformos_check/language_server/versioned_in_memory_storage.rb +84 -0
  168. data/lib/platformos_check/language_server.rb +71 -0
  169. data/lib/platformos_check/layout_file.rb +15 -0
  170. data/lib/platformos_check/liquid_check.rb +10 -0
  171. data/lib/platformos_check/liquid_file.rb +102 -0
  172. data/lib/platformos_check/liquid_node.rb +570 -0
  173. data/lib/platformos_check/liquid_visitor.rb +39 -0
  174. data/lib/platformos_check/migration_file.rb +9 -0
  175. data/lib/platformos_check/node.rb +53 -0
  176. data/lib/platformos_check/offense.rb +228 -0
  177. data/lib/platformos_check/page_file.rb +9 -0
  178. data/lib/platformos_check/parsing_helpers.rb +21 -0
  179. data/lib/platformos_check/partial_file.rb +15 -0
  180. data/lib/platformos_check/platformos_liquid/deprecated_filter.rb +31 -0
  181. data/lib/platformos_check/platformos_liquid/documentation/markdown_template.rb +51 -0
  182. data/lib/platformos_check/platformos_liquid/documentation.rb +45 -0
  183. data/lib/platformos_check/platformos_liquid/filter.rb +19 -0
  184. data/lib/platformos_check/platformos_liquid/object.rb +15 -0
  185. data/lib/platformos_check/platformos_liquid/source_index/base_entry.rb +66 -0
  186. data/lib/platformos_check/platformos_liquid/source_index/base_state.rb +23 -0
  187. data/lib/platformos_check/platformos_liquid/source_index/filter_entry.rb +26 -0
  188. data/lib/platformos_check/platformos_liquid/source_index/filter_state.rb +11 -0
  189. data/lib/platformos_check/platformos_liquid/source_index/object_entry.rb +20 -0
  190. data/lib/platformos_check/platformos_liquid/source_index/object_state.rb +11 -0
  191. data/lib/platformos_check/platformos_liquid/source_index/parameter_entry.rb +25 -0
  192. data/lib/platformos_check/platformos_liquid/source_index/property_entry.rb +21 -0
  193. data/lib/platformos_check/platformos_liquid/source_index/return_type_entry.rb +41 -0
  194. data/lib/platformos_check/platformos_liquid/source_index/tag_entry.rb +24 -0
  195. data/lib/platformos_check/platformos_liquid/source_index/tag_state.rb +11 -0
  196. data/lib/platformos_check/platformos_liquid/source_index.rb +79 -0
  197. data/lib/platformos_check/platformos_liquid/source_manager.rb +116 -0
  198. data/lib/platformos_check/platformos_liquid/tag.rb +59 -0
  199. data/lib/platformos_check/platformos_liquid.rb +21 -0
  200. data/lib/platformos_check/position.rb +180 -0
  201. data/lib/platformos_check/position_helper.rb +57 -0
  202. data/lib/platformos_check/printer.rb +87 -0
  203. data/lib/platformos_check/regex_helpers.rb +21 -0
  204. data/lib/platformos_check/releaser.rb +43 -0
  205. data/lib/platformos_check/schema_file.rb +6 -0
  206. data/lib/platformos_check/sms_file.rb +9 -0
  207. data/lib/platformos_check/storage.rb +29 -0
  208. data/lib/platformos_check/string_helpers.rb +48 -0
  209. data/lib/platformos_check/tags/background.rb +67 -0
  210. data/lib/platformos_check/tags/base.rb +14 -0
  211. data/lib/platformos_check/tags/base_block.rb +14 -0
  212. data/lib/platformos_check/tags/base_tag_methods.rb +59 -0
  213. data/lib/platformos_check/tags/cache.rb +13 -0
  214. data/lib/platformos_check/tags/export.rb +30 -0
  215. data/lib/platformos_check/tags/form.rb +19 -0
  216. data/lib/platformos_check/tags/function.rb +58 -0
  217. data/lib/platformos_check/tags/graphql.rb +70 -0
  218. data/lib/platformos_check/tags/hash_assign.rb +75 -0
  219. data/lib/platformos_check/tags/log.rb +15 -0
  220. data/lib/platformos_check/tags/parse_json.rb +24 -0
  221. data/lib/platformos_check/tags/print.rb +20 -0
  222. data/lib/platformos_check/tags/redirect_to.rb +15 -0
  223. data/lib/platformos_check/tags/render.rb +60 -0
  224. data/lib/platformos_check/tags/response_headers.rb +20 -0
  225. data/lib/platformos_check/tags/response_status.rb +20 -0
  226. data/lib/platformos_check/tags/return.rb +20 -0
  227. data/lib/platformos_check/tags/session.rb +27 -0
  228. data/lib/platformos_check/tags/sign_in.rb +27 -0
  229. data/lib/platformos_check/tags/spam_protection.rb +15 -0
  230. data/lib/platformos_check/tags/theme_render.rb +58 -0
  231. data/lib/platformos_check/tags/try.rb +59 -0
  232. data/lib/platformos_check/tags.rb +65 -0
  233. data/lib/platformos_check/translation_file.rb +6 -0
  234. data/lib/platformos_check/user_schema_file.rb +6 -0
  235. data/lib/platformos_check/version.rb +5 -0
  236. data/lib/platformos_check/yaml_check.rb +11 -0
  237. data/lib/platformos_check/yaml_file.rb +57 -0
  238. data/lib/platformos_check.rb +106 -0
  239. data/platformos-check.gemspec +34 -0
  240. metadata +329 -0
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PlatformosCheck
4
+ module PlatformosLiquid
5
+ class SourceIndex
6
+ class ObjectState < BaseState
7
+ @up_to_date = false
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PlatformosCheck
4
+ module PlatformosLiquid
5
+ class SourceIndex
6
+ class ParameterEntry < BaseEntry
7
+ def summary
8
+ nil
9
+ end
10
+
11
+ def platformos_documentation_url
12
+ "#{PLATFORMOS_DOCUMENTATION_URL}/api-reference/liquid/filters/#{hash['name']}"
13
+ end
14
+
15
+ private
16
+
17
+ def return_type_hash
18
+ {
19
+ 'type' => (hash['types'] || ['untyped']).first
20
+ }
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PlatformosCheck
4
+ module PlatformosLiquid
5
+ class SourceIndex
6
+ class PropertyEntry < BaseEntry
7
+ attr_reader :parent_name
8
+
9
+ def initialize(hash, parent_name)
10
+ @hash = hash || {}
11
+ @return_type = nil
12
+ @parent_name = parent_name
13
+ end
14
+
15
+ def platformos_documentation_url
16
+ "#{PLATFORMOS_DOCUMENTATION_URL}/developer-guide/variables/context-variable##{@parent_name}"
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PlatformosCheck
4
+ module PlatformosLiquid
5
+ class SourceIndex
6
+ class ReturnTypeEntry < BaseEntry
7
+ def summary
8
+ nil
9
+ end
10
+
11
+ def to_s
12
+ hash['type']
13
+ end
14
+
15
+ def generic_type?
16
+ hash['type'] == 'generic'
17
+ end
18
+
19
+ def array_type?
20
+ !array_type.nil? && !array_type.empty?
21
+ end
22
+
23
+ def array_type
24
+ hash['array_value']
25
+ end
26
+
27
+ def denied_filters
28
+ hash['denied_filters'] || []
29
+ end
30
+
31
+ private
32
+
33
+ def return_type_hash
34
+ {
35
+ 'type' => "type<#{self}>"
36
+ }
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PlatformosCheck
4
+ module PlatformosLiquid
5
+ class SourceIndex
6
+ class TagEntry < BaseEntry
7
+ def parameters
8
+ (hash['parameters'] || [])
9
+ .map { |hash| ParameterEntry.new(hash) }
10
+ end
11
+
12
+ def return_type_hash
13
+ {
14
+ 'type' => "tag<#{name}>"
15
+ }
16
+ end
17
+
18
+ def platformos_documentation_url
19
+ "#{PLATFORMOS_DOCUMENTATION_URL}/api-reference/liquid/tags/#{hash['name']}"
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PlatformosCheck
4
+ module PlatformosLiquid
5
+ class SourceIndex
6
+ class TagState < BaseState
7
+ @up_to_date = false
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require 'pathname'
5
+
6
+ module PlatformosCheck
7
+ module PlatformosLiquid
8
+ class SourceIndex
9
+ class << self
10
+ def filters
11
+ @filters = nil if FilterState.outdated?
12
+
13
+ @filters ||= FilterState.mark_up_to_date &&
14
+ load_file(:filters)
15
+ .map { |hash| FilterEntry.new(hash) }
16
+ end
17
+
18
+ def objects
19
+ @objects = nil if ObjectState.outdated?
20
+
21
+ @objects ||= ObjectState.mark_up_to_date &&
22
+ load_file(:objects)
23
+ .concat(built_in_objects)
24
+ .filter_map do |hash|
25
+ next if labels_only_exposed_in_certain_contexts.include?(hash['name'])
26
+
27
+ ObjectEntry.new(hash)
28
+ end
29
+ end
30
+
31
+ def tags
32
+ @tags = nil if TagState.outdated?
33
+
34
+ @tags ||= TagState.mark_up_to_date &&
35
+ load_file(:tags)
36
+ .map { |hash| TagEntry.new(hash) }
37
+ end
38
+
39
+ def plus_labels
40
+ @plus_objects ||= load_file("../plus_labels")
41
+ end
42
+
43
+ def platformos_app_app_extension_labels
44
+ @platformos_app_app_extension_labels ||= load_file("../platformos_app_app_extension_labels")
45
+ end
46
+
47
+ def labels_only_exposed_in_certain_contexts
48
+ %w[robots app].freeze
49
+ end
50
+
51
+ def deprecated_filters
52
+ @deprecated_filters ||= load_file("../deprecated_filters")
53
+ end
54
+
55
+ private
56
+
57
+ def load_file(file_name)
58
+ read_json(local_path!(file_name))
59
+ rescue StandardError
60
+ # If files get manually deleted, fallback with an empty list.
61
+ []
62
+ end
63
+
64
+ def local_path!(file_name)
65
+ SourceManager.download unless SourceManager.has_required_files?
66
+ SourceManager.local_path(file_name)
67
+ end
68
+
69
+ def read_json(path)
70
+ JSON.parse(path.read)
71
+ end
72
+
73
+ def built_in_objects
74
+ load_file('../built_in_liquid_objects')
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'net/http'
4
+ require 'pathname'
5
+ require 'tmpdir'
6
+
7
+ module PlatformosCheck
8
+ module PlatformosLiquid
9
+ class SourceManager
10
+ REQUIRED_FILE_NAMES = %i[filters objects tags latest].freeze
11
+
12
+ class DownloadResourceError < StandardError; end
13
+
14
+ class << self
15
+ def download_or_refresh_files(destination = default_destination)
16
+ if has_required_files?(destination)
17
+ refresh(destination)
18
+ else
19
+ download(destination)
20
+ end
21
+ end
22
+
23
+ def download(destination = default_destination)
24
+ Dir.mkdir(destination) unless destination.exist?
25
+
26
+ REQUIRED_FILE_NAMES.each do |file_name|
27
+ download_file(local_path(file_name, destination), remote_path(file_name))
28
+ end
29
+ rescue DownloadResourceError
30
+ # If a request error occurs, ignore it. This ensures that PlatformOS Check
31
+ # can rely on the sources included during the bundling phase.
32
+ end
33
+
34
+ def refresh(destination = default_destination)
35
+ refresh_threads << Thread.new { refresh_thread(destination) }
36
+ end
37
+
38
+ def local_path(file_name, destination = default_destination)
39
+ destination + "#{file_name}.json"
40
+ end
41
+
42
+ def has_required_files?(destination = default_destination)
43
+ REQUIRED_FILE_NAMES.all? { |file_name| local_path(file_name, destination).exist? }
44
+ end
45
+
46
+ def wait_downloads
47
+ refresh_threads.each(&:join)
48
+ end
49
+
50
+ private
51
+
52
+ def refresh_thread(destination)
53
+ return unless refresh_needed?(destination)
54
+
55
+ Dir.mktmpdir do |tmp_dir|
56
+ download(Pathname.new(tmp_dir))
57
+
58
+ FileUtils.cp_r("#{tmp_dir}/.", destination)
59
+
60
+ mark_all_indexes_outdated
61
+ end
62
+ end
63
+
64
+ def refresh_needed?(destination)
65
+ local_latest_content = local_path(:latest, destination).read
66
+ remote_latest_content = open_uri(remote_path(:latest))
67
+
68
+ revision(local_latest_content) != revision(remote_latest_content)
69
+ end
70
+
71
+ def revision(json_content)
72
+ # Raise an error if revision isn't found to avoid returning nil
73
+ JSON.parse(json_content).fetch('revision')
74
+ end
75
+
76
+ def remote_path(file_name)
77
+ "https://documentation.platformos.com/api/liquid/#{file_name}.json"
78
+ end
79
+
80
+ def download_file(local_path, remote_uri)
81
+ content = open_uri(remote_uri)
82
+ File.binwrite(local_path, content)
83
+ end
84
+
85
+ def mark_all_indexes_outdated
86
+ SourceIndex::FilterState.mark_outdated
87
+ SourceIndex::ObjectState.mark_outdated
88
+ SourceIndex::TagState.mark_outdated
89
+ end
90
+
91
+ # State
92
+
93
+ def default_destination
94
+ @default_destination ||= Pathname.new("#{__dir__}/../../../data/platformos_liquid/documentation")
95
+ end
96
+
97
+ def refresh_threads
98
+ @refresh_threads ||= []
99
+ end
100
+
101
+ def open_uri(uri_str)
102
+ uri = URI.parse(uri_str)
103
+
104
+ res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
105
+ req = Net::HTTP::Get.new(uri)
106
+ http.request(req)
107
+ end
108
+
109
+ res.body
110
+ rescue StandardError
111
+ raise DownloadResourceError
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
4
+
5
+ module PlatformosCheck
6
+ module PlatformosLiquid
7
+ module Tag
8
+ extend self
9
+
10
+ LABELS_NOT_IN_SOURCE_INDEX = %w[elsif ifchanged catch ensure when]
11
+
12
+ def labels
13
+ @labels ||= tags_file_contents
14
+ .map { |x| to_label(x) }
15
+ .to_set
16
+ end
17
+
18
+ def end_labels
19
+ @end_labels ||= tags_file_contents
20
+ .select { |x| x.is_a?(Hash) }
21
+ .map { |x| x.values[0] }
22
+ end
23
+
24
+ def tag_regex(tag)
25
+ return unless labels.include?(tag)
26
+
27
+ @tag_regexes ||= {}
28
+ @tag_regexes[tag] ||= /\A#{Liquid::TagStart}-?\s*#{tag}/m
29
+ end
30
+
31
+ def liquid_tag_regex(tag)
32
+ return unless labels.include?(tag)
33
+
34
+ @tag_liquid_regexes ||= {}
35
+ @tag_liquid_regexes[tag] ||= /^\s*#{tag}/m
36
+ end
37
+
38
+ private
39
+
40
+ def to_label(label)
41
+ return label if label.is_a?(String)
42
+
43
+ label.keys[0]
44
+ end
45
+
46
+ def tags_file_contents
47
+ @tags_file_contents ||= SourceIndex.tags.map do |tag|
48
+ opening_tag = tag.name
49
+ closing_tag = "end#{opening_tag}"
50
+ if /#{opening_tag}.+#{closing_tag}/m.match?(tag.hash['syntax'])
51
+ { opening_tag => closing_tag }
52
+ else
53
+ opening_tag
54
+ end
55
+ end + LABELS_NOT_IN_SOURCE_INDEX
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'platformos_liquid/deprecated_filter'
4
+ require_relative 'platformos_liquid/documentation'
5
+ require_relative 'platformos_liquid/filter'
6
+ require_relative 'platformos_liquid/object'
7
+ require_relative 'platformos_liquid/source_manager'
8
+ require_relative 'platformos_liquid/source_index'
9
+ require_relative 'platformos_liquid/tag'
10
+
11
+ require_relative 'platformos_liquid/source_index/base_entry'
12
+ require_relative 'platformos_liquid/source_index/filter_entry'
13
+ require_relative 'platformos_liquid/source_index/object_entry'
14
+ require_relative 'platformos_liquid/source_index/parameter_entry'
15
+ require_relative 'platformos_liquid/source_index/property_entry'
16
+ require_relative 'platformos_liquid/source_index/return_type_entry'
17
+ require_relative 'platformos_liquid/source_index/tag_entry'
18
+ require_relative 'platformos_liquid/source_index/base_state'
19
+ require_relative 'platformos_liquid/source_index/filter_state'
20
+ require_relative 'platformos_liquid/source_index/object_state'
21
+ require_relative 'platformos_liquid/source_index/tag_state'
@@ -0,0 +1,180 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PlatformosCheck
4
+ class Position
5
+ include PositionHelper
6
+
7
+ attr_reader :contents
8
+
9
+ def initialize(
10
+ needle_arg,
11
+ contents_arg,
12
+ line_number_1_indexed: nil,
13
+ node_markup: nil,
14
+ node_markup_offset: 0 # the index of markup inside the node_markup
15
+ )
16
+ @needle = needle_arg
17
+
18
+ @contents = if contents_arg.is_a?(String) && !contents_arg.empty?
19
+ contents_arg
20
+ else
21
+ ''
22
+ end
23
+
24
+ @line_number_1_indexed = line_number_1_indexed
25
+ @node_markup_offset = node_markup_offset
26
+ @node_markup = node_markup
27
+ end
28
+
29
+ def start_line_offset
30
+ @start_line_offset ||= from_row_column_to_index(contents, line_number, 0)
31
+ end
32
+
33
+ def start_offset
34
+ @start_offset ||= compute_start_offset
35
+ end
36
+
37
+ def strict_position
38
+ @strict_position ||= StrictPosition.new(
39
+ needle,
40
+ contents,
41
+ start_index
42
+ )
43
+ end
44
+
45
+ # 0-indexed, inclusive
46
+ def start_index
47
+ contents.index(needle, start_offset)
48
+ end
49
+
50
+ # 0-indexed, exclusive
51
+ def end_index
52
+ strict_position.end_index
53
+ end
54
+
55
+ # 0-indexed, inclusive
56
+ def start_row
57
+ strict_position.start_row
58
+ end
59
+
60
+ # 0-indexed, inclusive
61
+ def start_column
62
+ strict_position.start_column
63
+ end
64
+
65
+ # 0-indexed, exclusive (both taken together are) therefore you
66
+ # might end up on a newline character or the next line
67
+ def end_row
68
+ strict_position.end_row
69
+ end
70
+
71
+ def end_column
72
+ strict_position.end_column
73
+ end
74
+
75
+ def content_line_count
76
+ @content_line_count ||= contents.count("\n")
77
+ end
78
+
79
+ private
80
+
81
+ def compute_start_offset
82
+ return start_line_offset if @node_markup.nil?
83
+
84
+ node_markup_start = contents.index(@node_markup, start_line_offset)
85
+ return start_line_offset if node_markup_start.nil?
86
+
87
+ node_markup_start + @node_markup_offset
88
+ end
89
+
90
+ def line_number
91
+ return 0 if @line_number_1_indexed.nil?
92
+
93
+ bounded(0, @line_number_1_indexed - 1, content_line_count)
94
+ end
95
+
96
+ def needle
97
+ @cached_needle ||= if has_content_and_line_number_but_no_needle?
98
+ entire_line_needle
99
+ elsif contents.empty? || @needle.nil?
100
+ ''
101
+ elsif !can_find_needle?
102
+ entire_line_needle
103
+ else
104
+ @needle
105
+ end
106
+ end
107
+
108
+ def has_content_and_line_number_but_no_needle?
109
+ @needle.nil? && !contents.empty? && @line_number_1_indexed.is_a?(Integer)
110
+ end
111
+
112
+ def can_find_needle?
113
+ !!contents.index(@needle, start_offset)
114
+ end
115
+
116
+ def entire_line_needle
117
+ contents.lines(chomp: true)[line_number] || ''
118
+ end
119
+ end
120
+
121
+ # This method is stricter than Position in the sense that it doesn't
122
+ # accept invalid inputs. Makes for code that is easier to understand.
123
+ class StrictPosition
124
+ include PositionHelper
125
+
126
+ attr_reader :needle, :contents
127
+
128
+ def initialize(needle, contents, start_index)
129
+ raise ArgumentError, 'Bad start_index' unless start_index.is_a?(Integer)
130
+ raise ArgumentError, 'Bad contents' unless contents.is_a?(String)
131
+ raise ArgumentError, 'Bad needle' unless needle.is_a?(String) || !contents.index(needle, start_index)
132
+
133
+ @needle = needle
134
+ @contents = contents
135
+ @start_index = start_index
136
+ @start_row_column = nil
137
+ @end_row_column = nil
138
+ end
139
+
140
+ # 0-indexed, inclusive
141
+ def start_index
142
+ @contents.index(needle, @start_index)
143
+ end
144
+
145
+ # 0-indexed, exclusive
146
+ def end_index
147
+ start_index + needle.size
148
+ end
149
+
150
+ def start_row
151
+ start_row_column[0]
152
+ end
153
+
154
+ def start_column
155
+ start_row_column[1]
156
+ end
157
+
158
+ def end_row
159
+ end_row_column[0]
160
+ end
161
+
162
+ def end_column
163
+ end_row_column[1]
164
+ end
165
+
166
+ private
167
+
168
+ def start_row_column
169
+ return @start_row_column unless @start_row_column.nil?
170
+
171
+ @start_row_column = from_index_to_row_column(contents, start_index)
172
+ end
173
+
174
+ def end_row_column
175
+ return @end_row_column unless @end_row_column.nil?
176
+
177
+ @end_row_column = from_index_to_row_column(contents, end_index)
178
+ end
179
+ end
180
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ # NOTE: Everything is 0-indexed here.
4
+
5
+ module PlatformosCheck
6
+ module PositionHelper
7
+ # Apparently this old implementation is 2x slower (with benchmark/ips),
8
+ # so dropping with the following one... It's ugly af but
9
+ # this shit runs 100K+ times in one platformos-check so it gotta go
10
+ # fast!
11
+ #
12
+ def from_row_column_to_index(content, row, col)
13
+ return 0 unless content.is_a?(String) && !content.empty?
14
+ return 0 unless row.is_a?(Integer) && col.is_a?(Integer)
15
+
16
+ i = 0
17
+ safe_row = bounded(0, row, content.count("\n"))
18
+ scanner = StringScanner.new(content)
19
+ scanner.scan_until(/\n/) while i < safe_row && (i += 1)
20
+ result = scanner.charpos || 0
21
+ scanner.scan_until(/\n|\z/)
22
+ bounded(result, result + col, scanner.pre_match.size)
23
+ end
24
+
25
+ # def from_row_column_to_index(content, row, col)
26
+ # return 0 unless content.is_a?(String) && !content.empty?
27
+ # return 0 unless row.is_a?(Integer) && col.is_a?(Integer)
28
+ # i = 0
29
+ # safe_row = bounded(0, row, content.count("\n"))
30
+ # charpos = -1
31
+ # charpos = content.index("\n", charpos + 1) while i < safe_row && (i += 1) && charpos
32
+ # result = charpos ? charpos + 1 : 0
33
+ # next_line = content.index("\n", result)
34
+ # upper_bound = next_line ? next_line : content.size - 1
35
+ # bounded(result, result + col, upper_bound)
36
+ # end
37
+
38
+ def from_index_to_row_column(content, index)
39
+ return [0, 0] unless content.is_a?(String) && !content.empty?
40
+ return [0, 0] unless index.is_a?(Integer)
41
+
42
+ safe_index = bounded(0, index, content.size - 1)
43
+ content_up_to_index = content[0...safe_index]
44
+ row = content_up_to_index.count("\n")
45
+ col = 0
46
+ col += 1 while (safe_index -= 1) && safe_index >= 0 && content[safe_index] != "\n"
47
+ [row, col]
48
+ end
49
+
50
+ def bounded(a, x, b)
51
+ return a if x < a
52
+ return b if x > b
53
+
54
+ x
55
+ end
56
+ end
57
+ end