platformos-check 0.1.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -16,7 +16,7 @@ module PlatformosCheck
16
16
  @app_file = app_file
17
17
  end
18
18
 
19
- attr_reader :all_assigns, :all_captures, :all_forloops, :app_file
19
+ attr_reader :all_assigns, :all_captures, :all_forloops, :app_file, :all_renders
20
20
 
21
21
  def add_render(name:, node:)
22
22
  @all_renders[name] = node
@@ -58,39 +58,32 @@ module PlatformosCheck
58
58
  end
59
59
  end
60
60
 
61
- def initialize(config_type: :default, exclude_partials: true)
61
+ def self.single_file(**_args)
62
+ true
63
+ end
64
+
65
+ def initialize(config_type: :default)
62
66
  @config_type = config_type
63
- @exclude_partials = exclude_partials
64
67
  @files = {}
65
68
  end
66
69
 
67
70
  def on_document(node)
68
- return if ignore?(node)
69
-
70
71
  @files[node.app_file.name] = TemplateInfo.new(app_file: node.app_file)
71
72
  end
72
73
 
73
74
  def on_assign(node)
74
- return if ignore?(node)
75
-
76
75
  @files[node.app_file.name].all_assigns[node.value.to] = node
77
76
  end
78
77
 
79
78
  def on_capture(node)
80
- return if ignore?(node)
81
-
82
79
  @files[node.app_file.name].all_captures[node.value.instance_variable_get(:@to)] = node
83
80
  end
84
81
 
85
82
  def on_parse_json(node)
86
- return if ignore?(node)
87
-
88
83
  @files[node.app_file.name].all_captures[node.value.to] = node
89
84
  end
90
85
 
91
86
  def on_for(node)
92
- return if ignore?(node)
93
-
94
87
  @files[node.app_file.name].all_forloops[node.value.variable_name] = node
95
88
  end
96
89
 
@@ -99,7 +92,6 @@ module PlatformosCheck
99
92
  end
100
93
 
101
94
  def on_render(node)
102
- return if ignore?(node)
103
95
  return unless node.value.template_name_expr.is_a?(String)
104
96
 
105
97
  partial_name = node.value.template_name_expr
@@ -110,45 +102,52 @@ module PlatformosCheck
110
102
  end
111
103
 
112
104
  def on_function(node)
113
- return if ignore?(node)
105
+ @files[node.app_file.name].all_assigns[node.value.to] = node
106
+
107
+ return unless node.value.from.is_a?(String)
114
108
 
115
- name = node.value.from.is_a?(String) ? node.value.from : node.value.from.name
116
109
  @files[node.app_file.name].add_render(
117
- name:,
110
+ name: node.value.from,
118
111
  node:
119
112
  )
120
-
121
- @files[node.app_file.name].all_assigns[node.value.to] = node
122
113
  end
123
114
 
124
115
  def on_graphql(node)
125
- return if ignore?(node)
126
-
127
116
  @files[node.app_file.name].all_assigns[node.value.to] = node
128
117
  end
129
118
 
130
119
  def on_background(node)
131
- return if ignore?(node)
132
120
  return unless node.value.partial_syntax
121
+
122
+ @files[node.app_file.name].all_assigns[node.value.to] = node
123
+
133
124
  return unless node.value.partial_name.is_a?(String)
134
125
 
135
126
  @files[node.app_file.name].add_render(
136
127
  name: node.value.partial_name,
137
128
  node:
138
129
  )
139
-
140
- @files[node.app_file.name].all_assigns[node.value.to] = node
141
130
  end
142
131
 
143
132
  def on_variable_lookup(node)
144
- return if ignore?(node)
145
-
146
133
  @files[node.app_file.name].add_variable_lookup(
147
134
  name: node.value.name,
148
135
  node:
149
136
  )
150
137
  end
151
138
 
139
+ def single_file_end_dependencies(app_file)
140
+ @files[app_file.name].all_renders.keys.map do |partial_name|
141
+ next if @files[partial_name]
142
+
143
+ partial_file = @platformos_app.partials.detect { |p| p.name == partial_name } # NOTE: undefined partial
144
+
145
+ next unless partial_file
146
+
147
+ partial_file
148
+ end.compact
149
+ end
150
+
152
151
  def on_end
153
152
  all_global_objects = PlatformosCheck::PlatformosLiquid::Object.labels
154
153
  all_global_objects.freeze
@@ -170,31 +169,28 @@ module PlatformosCheck
170
169
 
171
170
  attr_reader :config_type
172
171
 
173
- def ignore?(node)
174
- @exclude_partials && node.app_file.partial?
175
- end
176
-
177
172
  def each_template
178
173
  @files.each do |(name, info)|
179
- next if info.app_file.partial?
180
-
181
174
  yield [name, info]
182
175
  end
183
176
  end
184
177
 
185
- def check_object(info, all_global_objects, render_node = nil, visited_partials = Set.new)
186
- check_undefined(info, all_global_objects, render_node)
178
+ def check_object(info, all_global_objects, render_node = nil, visited_partials = Set.new, level = 0)
179
+ return if level > 1
180
+
181
+ check_undefined(info, all_global_objects, render_node) unless info.app_file.partial? && render_node.nil? # ||
187
182
 
188
183
  info.each_partial do |(partial_name, node)|
184
+ next if visited_partials.include?(partial_name)
185
+
189
186
  partial_info = @files[partial_name]
187
+
190
188
  next unless partial_info # NOTE: undefined partial
191
189
 
192
190
  partial_variables = node.value.attributes.keys +
193
191
  [node.value.instance_variable_get(:@alias_name)]
194
- unless visited_partials.include?(partial_name)
195
- visited_partials << partial_name
196
- check_object(partial_info, all_global_objects + partial_variables, node, visited_partials)
197
- end
192
+ visited_partials << partial_name
193
+ check_object(partial_info, all_global_objects + partial_variables, node, visited_partials, level + 1)
198
194
  end
199
195
  end
200
196
 
@@ -213,7 +209,7 @@ module PlatformosCheck
213
209
 
214
210
  if render_node
215
211
  add_offense("Missing argument `#{name}`", node: render_node)
216
- else
212
+ elsif !info.app_file.partial?
217
213
  add_offense("Undefined object `#{name}`", node:, line_number:)
218
214
  end
219
215
  end
@@ -21,6 +21,10 @@ module PlatformosCheck
21
21
  end
22
22
  end
23
23
 
24
+ def self.single_file(**_args)
25
+ true
26
+ end
27
+
24
28
  def initialize
25
29
  @templates = {}
26
30
  end
@@ -25,6 +25,12 @@ module PlatformosCheck
25
25
  @single_file ||= self.class.new(select(&:single_file?))
26
26
  end
27
27
 
28
+ def single_file_end_dependencies(app_file)
29
+ map do |check|
30
+ check.respond_to?(:single_file_end_dependencies) ? check.single_file_end_dependencies(app_file) : []
31
+ end.flatten.compact.uniq
32
+ end
33
+
28
34
  private
29
35
 
30
36
  def call_check_method(check, method, *args)
@@ -13,6 +13,7 @@ module PlatformosCheck
13
13
 
14
14
  PlatformosLiquid::SourceIndex
15
15
  .objects
16
+ .select(&:global?)
16
17
  .select { |object| object.name.start_with?(partial(variable_lookup)) }
17
18
  .map { |object| object_to_completion(object) }
18
19
  end
@@ -5,34 +5,29 @@ module PlatformosCheck
5
5
  class TagCompletionProvider < CompletionProvider
6
6
  def completions(context)
7
7
  content = context.content
8
- cursor = context.cursor
9
8
 
10
9
  return [] if content.nil?
11
- return [] unless can_complete?(content, cursor)
10
+ return [] unless can_complete?(context)
12
11
 
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) }
12
+ partial = first_word(context.buffer.lines[context.line]) || ''
13
+ PlatformosLiquid::SourceIndex.tags.select { |tag| tag.name.start_with?(partial) }
14
+ .map { |tag| tag_to_completion(tag) }
19
15
  end
20
16
 
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
- )
17
+ def can_complete?(context)
18
+ context.content.start_with?(Liquid::TagStart) && (cursor_on_first_word?(context.buffer.lines[context.line], context.col) || cursor_on_start_content?(context.buffer.lines[context.line], context.col, Liquid::TagStart))
26
19
  end
27
20
 
28
21
  private
29
22
 
30
23
  def tag_to_completion(tag)
31
- content = PlatformosLiquid::Documentation.tag_doc(tag)
24
+ content = PlatformosLiquid::Documentation.tag_doc(tag.name)
32
25
 
33
26
  {
34
- label: tag,
27
+ contents: content,
28
+ label: tag.name,
35
29
  kind: CompletionItemKinds::KEYWORD,
30
+ **format_hash(tag),
36
31
  **doc_hash(content)
37
32
  }
38
33
  end
@@ -24,7 +24,7 @@ module PlatformosCheck
24
24
 
25
25
  CAPABILITIES = {
26
26
  completionProvider: {
27
- triggerCharacters: ['.', '{{ ', '{% '],
27
+ triggerCharacters: ['.', '{{ ', '{% ', '/'],
28
28
  context: true
29
29
  },
30
30
  codeActionProvider: {
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PlatformosCheck
4
+ module LanguageServer
5
+ class TagHoverProvider < HoverProvider
6
+ def completions(context)
7
+ content = context.content
8
+
9
+ return [] if content.nil?
10
+ return [] unless can_complete?(context)
11
+
12
+ partial = first_word(context.buffer.lines[context.line]) || ''
13
+ PlatformosLiquid::SourceIndex.tags.select { |tag| tag.name.start_with?(partial) }
14
+ .map { |tag| tag_to_completion(tag) }.first
15
+ end
16
+
17
+ def can_complete?(context)
18
+ context.content.start_with?(Liquid::TagStart) && (cursor_on_first_word?(context.buffer.lines[context.line], context.col) || cursor_on_start_content?(context.buffer.lines[context.line], context.col, Liquid::TagStart))
19
+ end
20
+
21
+ private
22
+
23
+ def tag_to_completion(tag)
24
+ content = PlatformosLiquid::Documentation.tag_doc(tag.name)
25
+
26
+ {
27
+ contents: content,
28
+ label: tag.name,
29
+ kind: CompletionItemKinds::KEYWORD,
30
+ **format_hash(tag),
31
+ **doc_hash(content)
32
+ }
33
+ end
34
+ end
35
+ end
36
+ end
@@ -13,18 +13,18 @@ module PlatformosCheck
13
13
 
14
14
  files
15
15
  .select { |x| x.name.start_with?(@file_name) }
16
- .map { |x| file_to_completion(x) }
16
+ .map { |x| file_to_completion(x, context) }
17
17
  end
18
18
 
19
19
  private
20
20
 
21
21
  def cursor_on_quoted_argument?(content, cursor)
22
- match = content.match(regexp)
23
- return false if match.nil?
22
+ @match = content.match(regexp)
23
+ return false if @match.nil?
24
24
 
25
- return false unless match.begin(:partial) <= cursor && cursor <= match.end(:partial)
25
+ return false unless @match.begin(:partial) <= cursor && cursor <= @match.end(:partial)
26
26
 
27
- @file_name = match[:partial]
27
+ @file_name = @match[:partial][0, cursor - @match.begin(:partial)]
28
28
  true
29
29
  end
30
30
 
@@ -36,11 +36,34 @@ module PlatformosCheck
36
36
  raise NotImplementedError
37
37
  end
38
38
 
39
- def file_to_completion(file)
39
+ def file_to_completion(file, context)
40
40
  {
41
41
  label: file.name,
42
- kind: CompletionItemKinds::SNIPPET,
43
- detail: file.source
42
+ kind: CompletionItemKinds::TEXT,
43
+ detail: file.source,
44
+ textEdit: {
45
+ newText: file.name,
46
+ insert: {
47
+ start: {
48
+ line: context.line,
49
+ character: @match.begin(:partial)
50
+ },
51
+ end: {
52
+ line: context.line,
53
+ character: @match.end(:partial)
54
+ }
55
+ },
56
+ replace: {
57
+ start: {
58
+ line: context.line,
59
+ character: @match.begin(:partial)
60
+ },
61
+ end: {
62
+ line: context.line,
63
+ character: @match.end(:partial)
64
+ }
65
+ }
66
+ }
44
67
  }
45
68
  end
46
69
  end
@@ -4,13 +4,20 @@ module PlatformosCheck
4
4
  class LiquidVisitor
5
5
  attr_reader :checks
6
6
 
7
- def initialize(checks, disabled_checks)
7
+ def initialize(checks, disabled_checks, only_single_file: false)
8
8
  @checks = checks
9
9
  @disabled_checks = disabled_checks
10
+ @only_single_file = only_single_file
10
11
  end
11
12
 
12
13
  def visit_liquid_file(liquid_file)
13
14
  visit(LiquidNode.new(liquid_file.root, nil, liquid_file))
15
+
16
+ if @only_single_file
17
+ checks.single_file_end_dependencies(liquid_file).each do |file|
18
+ visit(LiquidNode.new(file.root, nil, file))
19
+ end
20
+ end
14
21
  rescue Liquid::Error => e
15
22
  e.template_name = liquid_file.relative_path
16
23
  call_checks(:on_error, e)
@@ -14,6 +14,10 @@ module PlatformosCheck
14
14
  def platformos_documentation_url
15
15
  "#{PLATFORMOS_DOCUMENTATION_URL}/developer-guide/variables/context-variable##{hash['name']}"
16
16
  end
17
+
18
+ def global?
19
+ hash.dig('access', 'global')
20
+ end
17
21
  end
18
22
  end
19
23
  end
@@ -15,6 +15,34 @@ module PlatformosCheck
15
15
  }
16
16
  end
17
17
 
18
+ def description
19
+ @descritpion = begin
20
+ desc = hash['description']&.strip || ''
21
+ desc = '' if desc == 'returns'
22
+ if parameters.any?
23
+ desc += "\n\n---\n\nParameters:"
24
+ parameters.each { |p| desc += "\n- #{p.full_summary}" }
25
+ end
26
+ if hash['return_type']&.any?
27
+ rt = hash['return_type'].first
28
+ rt['description'] = nil if rt['description']&.strip == ''
29
+ desc += "\n\nReturns:"
30
+ desc += "\n- #{[rt['type'], rt['description']].compact.join(': ')}\n"
31
+ end
32
+ if hash['examples']
33
+ desc += "\n\n---\n\n"
34
+ hash['examples'].each_with_index do |e, i|
35
+ example = e['raw_liquid'].gsub(/[\n]+/, "\n").strip.split('=>')
36
+ input = example[0]&.strip
37
+ output = example[1]&.strip
38
+ desc += "\n - Example #{i}:\n\n```liquid\n#{input}\n```"
39
+ desc += "\n##\nOutput: #{output}" if output
40
+ end
41
+ end
42
+ end
43
+ desc
44
+ end
45
+
18
46
  def platformos_documentation_url
19
47
  "#{PLATFORMOS_DOCUMENTATION_URL}/api-reference/liquid/tags/#{hash['name']}"
20
48
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PlatformosCheck
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.1"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: platformos-check
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Piotr Bliszczyk
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: exe
12
12
  cert_chain: []
13
- date: 2023-09-13 00:00:00.000000000 Z
13
+ date: 2023-09-14 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: graphql
@@ -237,6 +237,7 @@ files:
237
237
  - lib/platformos_check/language_server/hover_engine.rb
238
238
  - lib/platformos_check/language_server/hover_provider.rb
239
239
  - lib/platformos_check/language_server/hover_providers/filter_hover_provider.rb
240
+ - lib/platformos_check/language_server/hover_providers/tag_hover_provider.rb
240
241
  - lib/platformos_check/language_server/io_messenger.rb
241
242
  - lib/platformos_check/language_server/messenger.rb
242
243
  - lib/platformos_check/language_server/partial_completion_provider.rb