theme-check 0.3.2 → 0.7.0
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 +16 -7
- data/lib/theme_check/checks/unknown_filter.rb +1 -0
- data/lib/theme_check/checks/unused_assign.rb +5 -3
- 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 -15
- 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/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,12 +135,12 @@ 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
|
|
136
142
|
|
|
137
|
-
def check_object(info, all_global_objects, render_node = nil)
|
|
143
|
+
def check_object(info, all_global_objects, render_node = nil, visited_snippets = Set.new)
|
|
138
144
|
check_undefined(info, all_global_objects, render_node)
|
|
139
145
|
|
|
140
146
|
info.each_snippet do |(snippet_name, node)|
|
|
@@ -143,7 +149,10 @@ module ThemeCheck
|
|
|
143
149
|
|
|
144
150
|
snippet_variables = node.value.attributes.keys +
|
|
145
151
|
Array[node.value.instance_variable_get("@alias_name")]
|
|
146
|
-
|
|
152
|
+
unless visited_snippets.include?(snippet_name)
|
|
153
|
+
visited_snippets << snippet_name
|
|
154
|
+
check_object(snippet_info, all_global_objects + snippet_variables, node, visited_snippets)
|
|
155
|
+
end
|
|
147
156
|
end
|
|
148
157
|
end
|
|
149
158
|
|
|
@@ -151,7 +160,7 @@ module ThemeCheck
|
|
|
151
160
|
all_variables = info.all_variables
|
|
152
161
|
|
|
153
162
|
info.each_variable_lookup(!!render_node) do |(key, node)|
|
|
154
|
-
name,
|
|
163
|
+
name, line_number = key
|
|
155
164
|
next if all_variables.include?(name)
|
|
156
165
|
next if all_global_objects.include?(name)
|
|
157
166
|
|
|
@@ -161,7 +170,7 @@ module ThemeCheck
|
|
|
161
170
|
if render_node
|
|
162
171
|
add_offense("Missing argument `#{name}`", node: render_node)
|
|
163
172
|
else
|
|
164
|
-
add_offense("Undefined object `#{name}`", node: node)
|
|
173
|
+
add_offense("Undefined object `#{name}`", node: node, line_number: line_number)
|
|
165
174
|
end
|
|
166
175
|
end
|
|
167
176
|
end
|
|
@@ -4,14 +4,16 @@ module ThemeCheck
|
|
|
4
4
|
class UnusedAssign < LiquidCheck
|
|
5
5
|
severity :suggestion
|
|
6
6
|
category :liquid
|
|
7
|
+
doc docs_url(__FILE__)
|
|
7
8
|
|
|
8
9
|
class TemplateInfo < Struct.new(:used_assigns, :assign_nodes, :includes)
|
|
9
|
-
def collect_used_assigns(templates)
|
|
10
|
+
def collect_used_assigns(templates, visited = Set.new)
|
|
10
11
|
collected = used_assigns
|
|
11
12
|
# Check recursively inside included snippets for use
|
|
12
13
|
includes.each do |name|
|
|
13
|
-
if templates[name]
|
|
14
|
-
|
|
14
|
+
if templates[name] && !visited.include?(name)
|
|
15
|
+
visited << name
|
|
16
|
+
collected += templates[name].collect_used_assigns(templates, visited)
|
|
15
17
|
end
|
|
16
18
|
end
|
|
17
19
|
collected
|
|
@@ -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
|