theme-check 0.3.3 → 0.7.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 +4 -4
- data/.github/workflows/theme-check.yml +10 -3
- data/.rubocop.yml +12 -3
- data/CHANGELOG.md +42 -0
- data/CONTRIBUTING.md +5 -2
- data/Gemfile +5 -3
- data/LICENSE.md +2 -0
- data/README.md +12 -4
- data/RELEASING.md +10 -3
- data/Rakefile +6 -0
- data/config/default.yml +16 -0
- data/data/shopify_liquid/tags.yml +27 -0
- data/data/shopify_translation_keys.yml +850 -0
- data/docs/checks/CHECK_DOCS_TEMPLATE.md +47 -0
- data/docs/checks/asset_size_css.md +52 -0
- data/docs/checks/asset_size_javascript.md +79 -0
- data/docs/checks/convert_include_to_render.md +48 -0
- data/docs/checks/default_locale.md +46 -0
- data/docs/checks/deprecated_filter.md +46 -0
- data/docs/checks/img_width_and_height.md +79 -0
- data/docs/checks/liquid_tag.md +65 -0
- data/docs/checks/matching_schema_translations.md +93 -0
- data/docs/checks/matching_translations.md +72 -0
- data/docs/checks/missing_enable_comment.md +50 -0
- data/docs/checks/missing_required_template_files.md +26 -0
- data/docs/checks/missing_template.md +40 -0
- data/docs/checks/nested_snippet.md +69 -0
- data/docs/checks/parser_blocking_javascript.md +97 -0
- data/docs/checks/remote_asset.md +82 -0
- data/docs/checks/required_directories.md +25 -0
- data/docs/checks/required_layout_theme_object.md +28 -0
- data/docs/checks/space_inside_braces.md +63 -0
- data/docs/checks/syntax_error.md +49 -0
- data/docs/checks/template_length.md +50 -0
- data/docs/checks/translation_key_exists.md +63 -0
- data/docs/checks/undefined_object.md +53 -0
- data/docs/checks/unknown_filter.md +45 -0
- data/docs/checks/unused_assign.md +47 -0
- data/docs/checks/unused_snippet.md +32 -0
- data/docs/checks/valid_html_translation.md +53 -0
- data/docs/checks/valid_json.md +60 -0
- data/docs/checks/valid_schema.md +50 -0
- data/lib/theme_check.rb +4 -0
- data/lib/theme_check/asset_file.rb +34 -0
- data/lib/theme_check/check.rb +20 -10
- data/lib/theme_check/checks/asset_size_css.rb +89 -0
- data/lib/theme_check/checks/asset_size_javascript.rb +68 -0
- data/lib/theme_check/checks/convert_include_to_render.rb +1 -1
- data/lib/theme_check/checks/default_locale.rb +1 -0
- data/lib/theme_check/checks/deprecated_filter.rb +1 -1
- data/lib/theme_check/checks/img_width_and_height.rb +74 -0
- data/lib/theme_check/checks/liquid_tag.rb +3 -3
- data/lib/theme_check/checks/matching_schema_translations.rb +1 -0
- data/lib/theme_check/checks/matching_translations.rb +2 -1
- data/lib/theme_check/checks/missing_enable_comment.rb +1 -0
- data/lib/theme_check/checks/missing_required_template_files.rb +1 -2
- data/lib/theme_check/checks/missing_template.rb +1 -0
- data/lib/theme_check/checks/nested_snippet.rb +1 -0
- data/lib/theme_check/checks/parser_blocking_javascript.rb +8 -15
- data/lib/theme_check/checks/remote_asset.rb +98 -0
- data/lib/theme_check/checks/required_directories.rb +1 -1
- data/lib/theme_check/checks/required_layout_theme_object.rb +1 -1
- data/lib/theme_check/checks/space_inside_braces.rb +1 -0
- data/lib/theme_check/checks/syntax_error.rb +1 -0
- data/lib/theme_check/checks/template_length.rb +1 -0
- data/lib/theme_check/checks/translation_key_exists.rb +14 -1
- data/lib/theme_check/checks/undefined_object.rb +11 -5
- data/lib/theme_check/checks/unknown_filter.rb +1 -0
- data/lib/theme_check/checks/unused_assign.rb +1 -0
- data/lib/theme_check/checks/unused_snippet.rb +1 -0
- data/lib/theme_check/checks/valid_html_translation.rb +2 -1
- data/lib/theme_check/checks/valid_json.rb +1 -0
- data/lib/theme_check/checks/valid_schema.rb +1 -0
- data/lib/theme_check/cli.rb +49 -13
- data/lib/theme_check/config.rb +5 -2
- data/lib/theme_check/disabled_checks.rb +2 -2
- data/lib/theme_check/in_memory_storage.rb +13 -8
- data/lib/theme_check/language_server.rb +12 -0
- data/lib/theme_check/language_server/completion_engine.rb +38 -0
- data/lib/theme_check/language_server/completion_helper.rb +25 -0
- data/lib/theme_check/language_server/completion_provider.rb +28 -0
- data/lib/theme_check/language_server/completion_providers/filter_completion_provider.rb +51 -0
- data/lib/theme_check/language_server/completion_providers/object_completion_provider.rb +31 -0
- data/lib/theme_check/language_server/completion_providers/render_snippet_completion_provider.rb +43 -0
- data/lib/theme_check/language_server/completion_providers/tag_completion_provider.rb +31 -0
- data/lib/theme_check/language_server/constants.rb +10 -0
- data/lib/theme_check/language_server/document_link_engine.rb +48 -0
- data/lib/theme_check/language_server/handler.rb +105 -10
- data/lib/theme_check/language_server/position_helper.rb +27 -0
- data/lib/theme_check/language_server/protocol.rb +41 -0
- data/lib/theme_check/language_server/server.rb +9 -4
- data/lib/theme_check/language_server/tokens.rb +55 -0
- data/lib/theme_check/liquid_check.rb +11 -0
- data/lib/theme_check/node.rb +1 -2
- data/lib/theme_check/offense.rb +52 -17
- data/lib/theme_check/packager.rb +1 -1
- data/lib/theme_check/regex_helpers.rb +15 -0
- data/lib/theme_check/releaser.rb +39 -0
- data/lib/theme_check/remote_asset_file.rb +44 -0
- data/lib/theme_check/shopify_liquid.rb +1 -0
- data/lib/theme_check/shopify_liquid/deprecated_filter.rb +10 -8
- data/lib/theme_check/shopify_liquid/filter.rb +3 -5
- data/lib/theme_check/shopify_liquid/object.rb +2 -6
- data/lib/theme_check/shopify_liquid/tag.rb +14 -0
- data/lib/theme_check/storage.rb +3 -3
- data/lib/theme_check/string_helpers.rb +47 -0
- data/lib/theme_check/tags.rb +1 -2
- data/lib/theme_check/theme.rb +7 -1
- data/lib/theme_check/version.rb +1 -1
- data/packaging/homebrew/theme_check.base.rb +1 -1
- data/theme-check.gemspec +1 -2
- metadata +57 -18
|
@@ -1,8 +1,21 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
module ThemeCheck
|
|
3
|
+
module SystemTranslations
|
|
4
|
+
extend self
|
|
5
|
+
|
|
6
|
+
def translations
|
|
7
|
+
@translations ||= YAML.load(File.read("#{__dir__}/../../../data/shopify_translation_keys.yml")).to_set
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def include?(key)
|
|
11
|
+
translations.include?(key)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
3
15
|
class TranslationKeyExists < LiquidCheck
|
|
4
16
|
severity :error
|
|
5
17
|
category :translation
|
|
18
|
+
doc docs_url(__FILE__)
|
|
6
19
|
|
|
7
20
|
def on_variable(node)
|
|
8
21
|
return unless @theme.default_locale_json&.content&.is_a?(Hash)
|
|
@@ -11,7 +24,7 @@ module ThemeCheck
|
|
|
11
24
|
return unless (key_node = node.children.first)
|
|
12
25
|
return unless key_node.value.is_a?(String)
|
|
13
26
|
|
|
14
|
-
unless key_exists?(key_node.value)
|
|
27
|
+
unless key_exists?(key_node.value) || SystemTranslations.include?(key_node.value)
|
|
15
28
|
add_offense(
|
|
16
29
|
"'#{key_node.value}' does not have a matching entry in '#{@theme.default_locale_json.relative_path}'",
|
|
17
30
|
node: node,
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
module ThemeCheck
|
|
3
3
|
class UndefinedObject < LiquidCheck
|
|
4
4
|
category :liquid
|
|
5
|
-
doc
|
|
5
|
+
doc docs_url(__FILE__)
|
|
6
6
|
severity :error
|
|
7
7
|
|
|
8
8
|
class TemplateInfo
|
|
@@ -21,7 +21,13 @@ module ThemeCheck
|
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
def add_variable_lookup(name:, node:)
|
|
24
|
-
|
|
24
|
+
parent = node
|
|
25
|
+
line_number = nil
|
|
26
|
+
loop do
|
|
27
|
+
line_number = parent.line_number
|
|
28
|
+
parent = parent.parent
|
|
29
|
+
break unless line_number.nil? && parent
|
|
30
|
+
end
|
|
25
31
|
key = [name, line_number]
|
|
26
32
|
@all_variable_lookups[key] = node
|
|
27
33
|
end
|
|
@@ -129,7 +135,7 @@ module ThemeCheck
|
|
|
129
135
|
|
|
130
136
|
def each_template
|
|
131
137
|
@files.each do |(name, info)|
|
|
132
|
-
next if name.
|
|
138
|
+
next if name.start_with?('snippets/')
|
|
133
139
|
yield [name, info]
|
|
134
140
|
end
|
|
135
141
|
end
|
|
@@ -154,7 +160,7 @@ module ThemeCheck
|
|
|
154
160
|
all_variables = info.all_variables
|
|
155
161
|
|
|
156
162
|
info.each_variable_lookup(!!render_node) do |(key, node)|
|
|
157
|
-
name,
|
|
163
|
+
name, line_number = key
|
|
158
164
|
next if all_variables.include?(name)
|
|
159
165
|
next if all_global_objects.include?(name)
|
|
160
166
|
|
|
@@ -164,7 +170,7 @@ module ThemeCheck
|
|
|
164
170
|
if render_node
|
|
165
171
|
add_offense("Missing argument `#{name}`", node: render_node)
|
|
166
172
|
else
|
|
167
|
-
add_offense("Undefined object `#{name}`", node: node)
|
|
173
|
+
add_offense("Undefined object `#{name}`", node: node, line_number: line_number)
|
|
168
174
|
end
|
|
169
175
|
end
|
|
170
176
|
end
|
|
@@ -5,9 +5,10 @@ require 'nokogumbo'
|
|
|
5
5
|
module ThemeCheck
|
|
6
6
|
class ValidHTMLTranslation < JsonCheck
|
|
7
7
|
severity :suggestion
|
|
8
|
+
doc docs_url(__FILE__)
|
|
8
9
|
|
|
9
10
|
def on_file(file)
|
|
10
|
-
return unless file.name.
|
|
11
|
+
return unless file.name.start_with?("locales/")
|
|
11
12
|
return unless file.content.is_a?(Hash)
|
|
12
13
|
|
|
13
14
|
visit_nested(file.content)
|
data/lib/theme_check/cli.rb
CHANGED
|
@@ -6,13 +6,18 @@ module ThemeCheck
|
|
|
6
6
|
USAGE = <<~END
|
|
7
7
|
Usage: theme-check [options] /path/to/your/theme
|
|
8
8
|
|
|
9
|
-
Options:
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-a,
|
|
14
|
-
|
|
15
|
-
|
|
9
|
+
Basic Options:
|
|
10
|
+
-C, --config <path> Use the config provided, overriding .theme-check.yml if present
|
|
11
|
+
-c, --category <category> Only run this category of checks
|
|
12
|
+
-x, --exclude-category <category> Exclude this category of checks
|
|
13
|
+
-a, --auto-correct Automatically fix offenses
|
|
14
|
+
|
|
15
|
+
Miscellaneous:
|
|
16
|
+
--init Generate a .theme-check.yml file
|
|
17
|
+
--print-config Output active config to STDOUT
|
|
18
|
+
-h, --help Show this. Hi!
|
|
19
|
+
-l, --list List enabled checks
|
|
20
|
+
-v, --version Print Theme Check version
|
|
16
21
|
|
|
17
22
|
Description:
|
|
18
23
|
Theme Check helps you follow Shopify Themes & Liquid best practices by analyzing the
|
|
@@ -22,12 +27,13 @@ module ThemeCheck
|
|
|
22
27
|
END
|
|
23
28
|
|
|
24
29
|
def run(argv)
|
|
25
|
-
path = "."
|
|
30
|
+
@path = "."
|
|
26
31
|
|
|
27
32
|
command = :check
|
|
28
33
|
only_categories = []
|
|
29
34
|
exclude_categories = []
|
|
30
35
|
auto_correct = false
|
|
36
|
+
config_path = nil
|
|
31
37
|
|
|
32
38
|
args = argv.dup
|
|
33
39
|
while (arg = args.shift)
|
|
@@ -36,6 +42,8 @@ module ThemeCheck
|
|
|
36
42
|
raise Abort, USAGE
|
|
37
43
|
when "--version", "-v"
|
|
38
44
|
command = :version
|
|
45
|
+
when "--config", "-C"
|
|
46
|
+
config_path = Pathname.new(args.shift)
|
|
39
47
|
when "--category", "-c"
|
|
40
48
|
only_categories << args.shift.to_sym
|
|
41
49
|
when "--exclude-category", "-x"
|
|
@@ -44,15 +52,28 @@ module ThemeCheck
|
|
|
44
52
|
command = :list
|
|
45
53
|
when "--auto-correct", "-a"
|
|
46
54
|
auto_correct = true
|
|
55
|
+
when "--init"
|
|
56
|
+
command = :init
|
|
57
|
+
when "--print"
|
|
58
|
+
command = :print
|
|
47
59
|
else
|
|
48
|
-
path = arg
|
|
60
|
+
@path = arg
|
|
49
61
|
end
|
|
50
62
|
end
|
|
51
63
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
64
|
+
unless [:version, :init].include?(command)
|
|
65
|
+
@config = if config_path
|
|
66
|
+
ThemeCheck::Config.new(
|
|
67
|
+
root: @path,
|
|
68
|
+
configuration: ThemeCheck::Config.load_file(config_path)
|
|
69
|
+
)
|
|
70
|
+
else
|
|
71
|
+
ThemeCheck::Config.from_path(@path)
|
|
72
|
+
end
|
|
73
|
+
@config.only_categories = only_categories
|
|
74
|
+
@config.exclude_categories = exclude_categories
|
|
75
|
+
@config.auto_correct = auto_correct
|
|
76
|
+
end
|
|
56
77
|
|
|
57
78
|
send(command)
|
|
58
79
|
end
|
|
@@ -75,6 +96,21 @@ module ThemeCheck
|
|
|
75
96
|
puts ThemeCheck::VERSION
|
|
76
97
|
end
|
|
77
98
|
|
|
99
|
+
def init
|
|
100
|
+
dotfile_path = ThemeCheck::Config.find(@path)
|
|
101
|
+
if dotfile_path.nil?
|
|
102
|
+
File.write(File.join(@path, ThemeCheck::Config::DOTFILE), File.read(ThemeCheck::Config::DEFAULT_CONFIG))
|
|
103
|
+
|
|
104
|
+
puts "Writing new #{ThemeCheck::Config::DOTFILE} to #{@path}"
|
|
105
|
+
else
|
|
106
|
+
raise Abort, "#{ThemeCheck::Config::DOTFILE} already exists at #{@path}."
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def print
|
|
111
|
+
puts YAML.dump(@config.to_h)
|
|
112
|
+
end
|
|
113
|
+
|
|
78
114
|
def check
|
|
79
115
|
puts "Checking #{@config.root} ..."
|
|
80
116
|
storage = ThemeCheck::FileSystemStorage.new(@config.root, ignored_patterns: @config.ignored_patterns)
|
data/lib/theme_check/config.rb
CHANGED
|
@@ -10,6 +10,8 @@ module ThemeCheck
|
|
|
10
10
|
attr_accessor :only_categories, :exclude_categories, :auto_correct
|
|
11
11
|
|
|
12
12
|
class << self
|
|
13
|
+
attr_reader :last_loaded_config
|
|
14
|
+
|
|
13
15
|
def from_path(path)
|
|
14
16
|
if (filename = find(path))
|
|
15
17
|
new(root: filename.dirname, configuration: load_file(filename))
|
|
@@ -36,6 +38,7 @@ module ThemeCheck
|
|
|
36
38
|
end
|
|
37
39
|
|
|
38
40
|
def load_file(absolute_path)
|
|
41
|
+
@last_loaded_config = absolute_path
|
|
39
42
|
YAML.load_file(absolute_path)
|
|
40
43
|
end
|
|
41
44
|
|
|
@@ -83,8 +86,8 @@ module ThemeCheck
|
|
|
83
86
|
|
|
84
87
|
check_class = ThemeCheck.const_get(check_name)
|
|
85
88
|
|
|
86
|
-
next if exclude_categories.include?(
|
|
87
|
-
next if only_categories.any? &&
|
|
89
|
+
next if check_class.categories.any? { |category| exclude_categories.include?(category) }
|
|
90
|
+
next if only_categories.any? && check_class.categories.none? { |category| only_categories.include?(category) }
|
|
88
91
|
|
|
89
92
|
options_for_check = options.transform_keys(&:to_sym)
|
|
90
93
|
options_for_check.delete(:enabled)
|
|
@@ -61,11 +61,11 @@ module ThemeCheck
|
|
|
61
61
|
end
|
|
62
62
|
|
|
63
63
|
def start_disabling?(text)
|
|
64
|
-
text.strip.
|
|
64
|
+
text.strip.start_with?(DISABLE_START)
|
|
65
65
|
end
|
|
66
66
|
|
|
67
67
|
def stop_disabling?(text)
|
|
68
|
-
text.strip.
|
|
68
|
+
text.strip.start_with?(DISABLE_END)
|
|
69
69
|
end
|
|
70
70
|
|
|
71
71
|
# Return a list of checks from a theme-check-disable comment
|
|
@@ -6,24 +6,25 @@
|
|
|
6
6
|
# as a big hash already, leave it like that and save yourself some IO.
|
|
7
7
|
module ThemeCheck
|
|
8
8
|
class InMemoryStorage < Storage
|
|
9
|
-
def initialize(files)
|
|
9
|
+
def initialize(files = {}, root = "/dev/null")
|
|
10
10
|
@files = files
|
|
11
|
+
@root = Pathname.new(root)
|
|
11
12
|
end
|
|
12
13
|
|
|
13
|
-
def path(
|
|
14
|
-
|
|
14
|
+
def path(relative_path)
|
|
15
|
+
@root.join(relative_path)
|
|
15
16
|
end
|
|
16
17
|
|
|
17
|
-
def read(
|
|
18
|
-
@files[
|
|
18
|
+
def read(relative_path)
|
|
19
|
+
@files[relative_path]
|
|
19
20
|
end
|
|
20
21
|
|
|
21
|
-
def write(
|
|
22
|
-
@files[
|
|
22
|
+
def write(relative_path, content)
|
|
23
|
+
@files[relative_path] = content
|
|
23
24
|
end
|
|
24
25
|
|
|
25
26
|
def files
|
|
26
|
-
@
|
|
27
|
+
@files.keys
|
|
27
28
|
end
|
|
28
29
|
|
|
29
30
|
def directories
|
|
@@ -33,5 +34,9 @@ module ThemeCheck
|
|
|
33
34
|
.map(&:to_s)
|
|
34
35
|
.uniq
|
|
35
36
|
end
|
|
37
|
+
|
|
38
|
+
def relative_path(absolute_path)
|
|
39
|
+
Pathname.new(absolute_path).relative_path_from(@root).to_s
|
|
40
|
+
end
|
|
36
41
|
end
|
|
37
42
|
end
|
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
require_relative "language_server/protocol"
|
|
3
|
+
require_relative "language_server/constants"
|
|
2
4
|
require_relative "language_server/handler"
|
|
3
5
|
require_relative "language_server/server"
|
|
6
|
+
require_relative "language_server/tokens"
|
|
7
|
+
require_relative "language_server/position_helper"
|
|
8
|
+
require_relative "language_server/completion_helper"
|
|
9
|
+
require_relative "language_server/completion_provider"
|
|
10
|
+
require_relative "language_server/completion_engine"
|
|
11
|
+
require_relative "language_server/document_link_engine"
|
|
12
|
+
|
|
13
|
+
Dir[__dir__ + "/language_server/completion_providers/*.rb"].each do |file|
|
|
14
|
+
require file
|
|
15
|
+
end
|
|
4
16
|
|
|
5
17
|
module ThemeCheck
|
|
6
18
|
module LanguageServer
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ThemeCheck
|
|
4
|
+
module LanguageServer
|
|
5
|
+
class CompletionEngine
|
|
6
|
+
include PositionHelper
|
|
7
|
+
|
|
8
|
+
def initialize(storage)
|
|
9
|
+
@storage = storage
|
|
10
|
+
@providers = CompletionProvider.all.map { |x| x.new(storage) }
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def completions(relative_path, line, col)
|
|
14
|
+
buffer = @storage.read(relative_path)
|
|
15
|
+
cursor = from_line_column_to_index(buffer, line, col)
|
|
16
|
+
token = find_token(buffer, cursor)
|
|
17
|
+
return [] if token.nil?
|
|
18
|
+
|
|
19
|
+
@providers.flat_map do |p|
|
|
20
|
+
p.completions(
|
|
21
|
+
token.content,
|
|
22
|
+
cursor - token.start
|
|
23
|
+
)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def find_token(buffer, cursor)
|
|
28
|
+
Tokens.new(buffer).find do |token|
|
|
29
|
+
# Here we include the next character and exclude the first
|
|
30
|
+
# one becase when we want to autocomplete inside a token
|
|
31
|
+
# and at most 1 outside it since the cursor could be placed
|
|
32
|
+
# at the end of the token.
|
|
33
|
+
token.start < cursor && cursor <= token.end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ThemeCheck
|
|
4
|
+
module LanguageServer
|
|
5
|
+
module CompletionHelper
|
|
6
|
+
WORD = /\w+/
|
|
7
|
+
|
|
8
|
+
def cursor_on_start_content?(content, cursor, regex)
|
|
9
|
+
content.slice(0, cursor).match?(/#{regex}(?:\s|\n)*$/m)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def cursor_on_first_word?(content, cursor)
|
|
13
|
+
word = content.match(WORD)
|
|
14
|
+
return false if word.nil?
|
|
15
|
+
word_start = word.begin(0)
|
|
16
|
+
word_end = word.end(0)
|
|
17
|
+
word_start <= cursor && cursor <= word_end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def first_word(content)
|
|
21
|
+
return content.match(WORD)[0] if content.match?(WORD)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ThemeCheck
|
|
4
|
+
module LanguageServer
|
|
5
|
+
class CompletionProvider
|
|
6
|
+
include CompletionHelper
|
|
7
|
+
include RegexHelpers
|
|
8
|
+
|
|
9
|
+
class << self
|
|
10
|
+
def all
|
|
11
|
+
@all ||= []
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def inherited(subclass)
|
|
15
|
+
all << subclass
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def initialize(storage = InMemoryStorage.new)
|
|
20
|
+
@storage = storage
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def completions(content, cursor)
|
|
24
|
+
raise NotImplementedError
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|