theme-check 1.2.0 → 1.5.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 +3 -3
- data/.gitignore +1 -0
- data/CHANGELOG.md +47 -0
- data/CONTRIBUTING.md +59 -1
- data/Gemfile +3 -0
- data/bin/theme-check +29 -0
- data/bin/theme-check-language-server +29 -0
- data/config/default.yml +15 -1
- data/config/theme_app_extension.yml +15 -0
- data/data/shopify_liquid/objects.yml +1 -0
- data/docs/checks/app_block_valid_tags.md +40 -0
- data/docs/checks/asset_size_app_block_css.md +1 -1
- data/docs/checks/deprecate_lazysizes.md +0 -3
- data/docs/checks/deprecated_global_app_block_type.md +65 -0
- data/docs/checks/missing_template.md +25 -0
- data/docs/checks/pagination_size.md +44 -0
- data/docs/checks/template_length.md +1 -1
- data/docs/checks/undefined_object.md +5 -0
- data/docs/flamegraph.svg +18488 -0
- data/lib/theme_check/analyzer.rb +1 -0
- data/lib/theme_check/check.rb +2 -2
- data/lib/theme_check/checks/app_block_valid_tags.rb +36 -0
- data/lib/theme_check/checks/asset_size_css.rb +3 -3
- data/lib/theme_check/checks/asset_size_javascript.rb +2 -2
- data/lib/theme_check/checks/convert_include_to_render.rb +3 -1
- data/lib/theme_check/checks/default_locale.rb +3 -1
- data/lib/theme_check/checks/deprecate_bgsizes.rb +1 -1
- data/lib/theme_check/checks/deprecate_lazysizes.rb +7 -4
- data/lib/theme_check/checks/deprecated_global_app_block_type.rb +57 -0
- data/lib/theme_check/checks/img_lazy_loading.rb +1 -1
- data/lib/theme_check/checks/img_width_and_height.rb +3 -3
- data/lib/theme_check/checks/missing_template.rb +21 -5
- data/lib/theme_check/checks/pagination_size.rb +84 -0
- data/lib/theme_check/checks/parser_blocking_javascript.rb +1 -1
- data/lib/theme_check/checks/remote_asset.rb +3 -3
- data/lib/theme_check/checks/space_inside_braces.rb +26 -6
- data/lib/theme_check/checks/template_length.rb +1 -1
- data/lib/theme_check/checks/undefined_object.rb +1 -1
- data/lib/theme_check/checks/valid_html_translation.rb +1 -1
- data/lib/theme_check/checks.rb +11 -1
- data/lib/theme_check/cli.rb +42 -3
- data/lib/theme_check/corrector.rb +9 -0
- data/lib/theme_check/file_system_storage.rb +12 -0
- data/lib/theme_check/html_check.rb +0 -1
- data/lib/theme_check/html_node.rb +37 -16
- data/lib/theme_check/html_visitor.rb +17 -3
- data/lib/theme_check/json_check.rb +2 -2
- data/lib/theme_check/json_file.rb +11 -0
- data/lib/theme_check/json_printer.rb +31 -0
- data/lib/theme_check/language_server/constants.rb +18 -11
- data/lib/theme_check/language_server/document_link_engine.rb +3 -67
- data/lib/theme_check/language_server/document_link_provider.rb +71 -0
- data/lib/theme_check/language_server/document_link_providers/asset_document_link_provider.rb +11 -0
- data/lib/theme_check/language_server/document_link_providers/include_document_link_provider.rb +11 -0
- data/lib/theme_check/language_server/document_link_providers/render_document_link_provider.rb +11 -0
- data/lib/theme_check/language_server/document_link_providers/section_document_link_provider.rb +11 -0
- data/lib/theme_check/language_server/handler.rb +17 -9
- data/lib/theme_check/language_server/server.rb +11 -13
- data/lib/theme_check/language_server/uri_helper.rb +37 -0
- data/lib/theme_check/language_server.rb +6 -0
- data/lib/theme_check/node.rb +6 -4
- data/lib/theme_check/offense.rb +56 -3
- data/lib/theme_check/parsing_helpers.rb +4 -3
- data/lib/theme_check/position.rb +98 -14
- data/lib/theme_check/printer.rb +9 -5
- data/lib/theme_check/regex_helpers.rb +5 -2
- data/lib/theme_check/theme.rb +3 -0
- data/lib/theme_check/version.rb +1 -1
- data/lib/theme_check.rb +1 -0
- data/theme-check.gemspec +1 -1
- metadata +21 -10
- data/bin/liquid-server +0 -4
- data/exe/theme-check-language-server.bat +0 -3
- data/exe/theme-check.bat +0 -3
@@ -5,7 +5,7 @@ module ThemeCheck
|
|
5
5
|
category :liquid
|
6
6
|
doc docs_url(__FILE__)
|
7
7
|
|
8
|
-
def initialize(max_length:
|
8
|
+
def initialize(max_length: 600, exclude_schema: true, exclude_stylesheet: true, exclude_javascript: true)
|
9
9
|
@max_length = max_length
|
10
10
|
@exclude_schema = exclude_schema
|
11
11
|
@exclude_stylesheet = exclude_stylesheet
|
data/lib/theme_check/checks.rb
CHANGED
@@ -29,8 +29,18 @@ module ThemeCheck
|
|
29
29
|
def call_check_method(check, method, *args)
|
30
30
|
return unless check.respond_to?(method) && !check.ignored?
|
31
31
|
|
32
|
-
|
32
|
+
# If you want to use binding.pry in unit tests, define the
|
33
|
+
# THEME_CHECK_DEBUG environment variable. e.g.
|
34
|
+
#
|
35
|
+
# $ export THEME_CHECK_DEBUG=true
|
36
|
+
# $ bundle exec rake tests:in_memory
|
37
|
+
#
|
38
|
+
if ENV['THEME_CHECK_DEBUG']
|
33
39
|
check.send(method, *args)
|
40
|
+
else
|
41
|
+
Timeout.timeout(CHECK_METHOD_TIMEOUT) do
|
42
|
+
check.send(method, *args)
|
43
|
+
end
|
34
44
|
end
|
35
45
|
rescue Liquid::Error
|
36
46
|
# Pass-through Liquid errors
|
data/lib/theme_check/cli.rb
CHANGED
@@ -5,6 +5,8 @@ module ThemeCheck
|
|
5
5
|
class Cli
|
6
6
|
class Abort < StandardError; end
|
7
7
|
|
8
|
+
FORMATS = [:text, :json]
|
9
|
+
|
8
10
|
attr_accessor :path
|
9
11
|
|
10
12
|
def initialize
|
@@ -15,6 +17,7 @@ module ThemeCheck
|
|
15
17
|
@auto_correct = false
|
16
18
|
@config_path = nil
|
17
19
|
@fail_level = :error
|
20
|
+
@format = :text
|
18
21
|
end
|
19
22
|
|
20
23
|
def option_parser(parser = OptionParser.new, help: true)
|
@@ -29,6 +32,10 @@ module ThemeCheck
|
|
29
32
|
"Use the config provided, overriding .theme-check.yml if present",
|
30
33
|
"Use :theme_app_extension to use default checks for theme app extensions"
|
31
34
|
) { |path| @config_path = path }
|
35
|
+
@option_parser.on(
|
36
|
+
"-o", "--output FORMAT", FORMATS,
|
37
|
+
"The output format to use. (text|json, default: text)"
|
38
|
+
) { |format| @format = format.to_sym }
|
32
39
|
@option_parser.on(
|
33
40
|
"-c", "--category CATEGORY", Check::CATEGORIES, "Only run this category of checks",
|
34
41
|
"Runs checks matching all categories when specified more than once"
|
@@ -71,6 +78,15 @@ module ThemeCheck
|
|
71
78
|
"Print Theme Check version"
|
72
79
|
) { @command = :version }
|
73
80
|
|
81
|
+
if ENV["THEME_CHECK_DEBUG"]
|
82
|
+
@option_parser.separator("")
|
83
|
+
@option_parser.separator("Debugging:")
|
84
|
+
@option_parser.on(
|
85
|
+
"--profile",
|
86
|
+
"Output a profile to STDOUT compatible with FlameGraph."
|
87
|
+
) { @command = :profile }
|
88
|
+
end
|
89
|
+
|
74
90
|
@option_parser.separator("")
|
75
91
|
@option_parser.separator(<<~EOS)
|
76
92
|
Description:
|
@@ -165,8 +181,8 @@ module ThemeCheck
|
|
165
181
|
puts option_parser.to_s
|
166
182
|
end
|
167
183
|
|
168
|
-
def check
|
169
|
-
puts "Checking #{@config.root} ..."
|
184
|
+
def check(out_stream = STDOUT)
|
185
|
+
STDERR.puts "Checking #{@config.root} ..."
|
170
186
|
storage = ThemeCheck::FileSystemStorage.new(@config.root, ignored_patterns: @config.ignored_patterns)
|
171
187
|
theme = ThemeCheck::Theme.new(storage)
|
172
188
|
if theme.all.empty?
|
@@ -175,10 +191,33 @@ module ThemeCheck
|
|
175
191
|
analyzer = ThemeCheck::Analyzer.new(theme, @config.enabled_checks, @config.auto_correct)
|
176
192
|
analyzer.analyze_theme
|
177
193
|
analyzer.correct_offenses
|
178
|
-
|
194
|
+
output_with_format(theme, analyzer, out_stream)
|
179
195
|
raise Abort, "" if analyzer.uncorrectable_offenses.any? do |offense|
|
180
196
|
offense.check.severity_value <= Check.severity_value(@fail_level)
|
181
197
|
end
|
182
198
|
end
|
199
|
+
|
200
|
+
def profile
|
201
|
+
require 'ruby-prof-flamegraph'
|
202
|
+
|
203
|
+
result = RubyProf.profile do
|
204
|
+
check(STDERR)
|
205
|
+
end
|
206
|
+
|
207
|
+
# Print a graph profile to text
|
208
|
+
printer = RubyProf::FlameGraphPrinter.new(result)
|
209
|
+
printer.print(STDOUT, {})
|
210
|
+
rescue LoadError
|
211
|
+
STDERR.puts "Profiling is only available in development"
|
212
|
+
end
|
213
|
+
|
214
|
+
def output_with_format(theme, analyzer, out_stream)
|
215
|
+
case @format
|
216
|
+
when :text
|
217
|
+
ThemeCheck::Printer.new(out_stream).print(theme, analyzer.offenses, @config.auto_correct)
|
218
|
+
when :json
|
219
|
+
ThemeCheck::JsonPrinter.new(out_stream).print(analyzer.offenses)
|
220
|
+
end
|
221
|
+
end
|
183
222
|
end
|
184
223
|
end
|
@@ -27,5 +27,14 @@ module ThemeCheck
|
|
27
27
|
line.insert(node.range[0], insert_before)
|
28
28
|
line.insert(node.range[1] + 1 + insert_before.length, insert_after)
|
29
29
|
end
|
30
|
+
|
31
|
+
def create(theme, relative_path, content)
|
32
|
+
theme.storage.write(relative_path, content)
|
33
|
+
end
|
34
|
+
|
35
|
+
def create_default_locale_json(theme)
|
36
|
+
theme.default_locale_json = JsonFile.new("locales/#{theme.default_locale}.default.json", theme.storage)
|
37
|
+
theme.default_locale_json.update_contents('{}')
|
38
|
+
end
|
30
39
|
end
|
31
40
|
end
|
@@ -20,6 +20,9 @@ module ThemeCheck
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def write(relative_path, content)
|
23
|
+
reset_memoizers unless file_exists?(relative_path)
|
24
|
+
|
25
|
+
file(relative_path).dirname.mkpath unless file(relative_path).dirname.directory?
|
23
26
|
file(relative_path).write(content)
|
24
27
|
end
|
25
28
|
|
@@ -36,6 +39,15 @@ module ThemeCheck
|
|
36
39
|
|
37
40
|
private
|
38
41
|
|
42
|
+
def file_exists?(relative_path)
|
43
|
+
!!@files[relative_path]
|
44
|
+
end
|
45
|
+
|
46
|
+
def reset_memoizers
|
47
|
+
@file_array = nil
|
48
|
+
@directories = nil
|
49
|
+
end
|
50
|
+
|
39
51
|
def glob(pattern)
|
40
52
|
@root.glob(pattern).reject do |path|
|
41
53
|
relative_path = path.relative_path_from(@root)
|
@@ -4,13 +4,14 @@ require "forwardable"
|
|
4
4
|
module ThemeCheck
|
5
5
|
class HtmlNode
|
6
6
|
extend Forwardable
|
7
|
-
|
7
|
+
include RegexHelpers
|
8
|
+
attr_reader :template, :parent
|
8
9
|
|
9
|
-
|
10
|
-
|
11
|
-
def initialize(value, template)
|
10
|
+
def initialize(value, template, placeholder_values = [], parent = nil)
|
12
11
|
@value = value
|
13
12
|
@template = template
|
13
|
+
@placeholder_values = placeholder_values
|
14
|
+
@parent = parent
|
14
15
|
end
|
15
16
|
|
16
17
|
def literal?
|
@@ -22,35 +23,55 @@ module ThemeCheck
|
|
22
23
|
end
|
23
24
|
|
24
25
|
def children
|
25
|
-
@
|
26
|
+
@children ||= @value
|
27
|
+
.children
|
28
|
+
.map { |child| HtmlNode.new(child, template, @placeholder_values, self) }
|
26
29
|
end
|
27
30
|
|
28
|
-
def
|
29
|
-
|
31
|
+
def attributes
|
32
|
+
@attributes ||= @value.attributes
|
33
|
+
.map { |k, v| [replace_placeholders(k), replace_placeholders(v.value)] }
|
34
|
+
.to_h
|
30
35
|
end
|
31
36
|
|
32
|
-
def
|
33
|
-
|
34
|
-
"document"
|
35
|
-
else
|
36
|
-
@value.name
|
37
|
-
end
|
37
|
+
def content
|
38
|
+
@content ||= replace_placeholders(@value.content)
|
38
39
|
end
|
39
40
|
|
41
|
+
# @value is not forwarded because we _need_ to replace the
|
42
|
+
# placeholders for the HtmlNode to make sense.
|
40
43
|
def value
|
41
44
|
if literal?
|
42
|
-
|
45
|
+
content
|
43
46
|
else
|
44
|
-
|
47
|
+
markup
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def name
|
52
|
+
if @value.name == "#document-fragment"
|
53
|
+
"document"
|
54
|
+
else
|
55
|
+
@value.name
|
45
56
|
end
|
46
57
|
end
|
47
58
|
|
48
59
|
def markup
|
49
|
-
@value.to_html
|
60
|
+
@markup ||= replace_placeholders(@value.to_html)
|
50
61
|
end
|
51
62
|
|
52
63
|
def line_number
|
53
64
|
@value.line
|
54
65
|
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def replace_placeholders(string)
|
70
|
+
# Replace all {%#{i}####%} with the actual content.
|
71
|
+
string.gsub(LIQUID_TAG) do |match|
|
72
|
+
key = /\d+/.match(match)[0]
|
73
|
+
@placeholder_values[key.to_i]
|
74
|
+
end
|
75
|
+
end
|
55
76
|
end
|
56
77
|
end
|
@@ -1,18 +1,20 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
require "
|
2
|
+
require "nokogiri"
|
3
3
|
require "forwardable"
|
4
4
|
|
5
5
|
module ThemeCheck
|
6
6
|
class HtmlVisitor
|
7
|
+
include RegexHelpers
|
7
8
|
attr_reader :checks
|
8
9
|
|
9
10
|
def initialize(checks)
|
10
11
|
@checks = checks
|
12
|
+
@placeholder_values = []
|
11
13
|
end
|
12
14
|
|
13
15
|
def visit_template(template)
|
14
16
|
doc = parse(template)
|
15
|
-
visit(HtmlNode.new(doc, template))
|
17
|
+
visit(HtmlNode.new(doc, template, @placeholder_values))
|
16
18
|
rescue ArgumentError => e
|
17
19
|
call_checks(:on_parse_error, e, template)
|
18
20
|
end
|
@@ -20,7 +22,19 @@ module ThemeCheck
|
|
20
22
|
private
|
21
23
|
|
22
24
|
def parse(template)
|
23
|
-
|
25
|
+
parseable_source = +template.source.clone
|
26
|
+
|
27
|
+
# Replace all liquid tags with {%#{i}######%} to prevent the HTML
|
28
|
+
# parser from freaking out. We transparently replace those placeholders in
|
29
|
+
# HtmlNode.
|
30
|
+
matches(parseable_source, LIQUID_TAG_OR_VARIABLE).each do |m|
|
31
|
+
value = m[0]
|
32
|
+
@placeholder_values.push(value)
|
33
|
+
key = (@placeholder_values.size - 1).to_s
|
34
|
+
parseable_source[m.begin(0)...m.end(0)] = "{%#{key.ljust(m.end(0) - m.begin(0) - 4, '#')}%}"
|
35
|
+
end
|
36
|
+
|
37
|
+
Nokogiri::HTML5.fragment(parseable_source, max_tree_depth: 400, max_attributes: 400)
|
24
38
|
end
|
25
39
|
|
26
40
|
def visit(node)
|
@@ -4,8 +4,8 @@ module ThemeCheck
|
|
4
4
|
class JsonCheck < Check
|
5
5
|
extend ChecksTracking
|
6
6
|
|
7
|
-
def add_offense(message, markup: nil, line_number: nil, template: nil)
|
8
|
-
offenses << Offense.new(check: self, message: message, markup: markup, line_number: line_number, template: template)
|
7
|
+
def add_offense(message, markup: nil, line_number: nil, template: nil, &block)
|
8
|
+
offenses << Offense.new(check: self, message: message, markup: markup, line_number: line_number, template: template, correction: block)
|
9
9
|
end
|
10
10
|
end
|
11
11
|
end
|
@@ -20,6 +20,17 @@ module ThemeCheck
|
|
20
20
|
@parser_error
|
21
21
|
end
|
22
22
|
|
23
|
+
def update_contents(new_content = '{}')
|
24
|
+
@content = new_content
|
25
|
+
end
|
26
|
+
|
27
|
+
def write
|
28
|
+
if source != @content
|
29
|
+
@storage.write(@relative_path, content)
|
30
|
+
@source = content
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
23
34
|
def json?
|
24
35
|
true
|
25
36
|
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module ThemeCheck
|
5
|
+
class JsonPrinter
|
6
|
+
def initialize(out_stream = STDOUT)
|
7
|
+
@out = out_stream
|
8
|
+
end
|
9
|
+
|
10
|
+
def print(offenses)
|
11
|
+
json = offenses_by_path(offenses)
|
12
|
+
@out.puts JSON.dump(json)
|
13
|
+
end
|
14
|
+
|
15
|
+
def offenses_by_path(offenses)
|
16
|
+
offenses
|
17
|
+
.map(&:to_h)
|
18
|
+
.group_by { |offense| offense[:path] }
|
19
|
+
.map do |(path, path_offenses)|
|
20
|
+
{
|
21
|
+
path: path,
|
22
|
+
offenses: path_offenses.map { |offense| offense.filter { |k, _v| k != :path } },
|
23
|
+
errorCount: path_offenses.count { |offense| offense[:severity] == Check::SEVERITY_VALUES[:error] },
|
24
|
+
suggestionCount: path_offenses.count { |offense| offense[:severity] == Check::SEVERITY_VALUES[:suggestion] },
|
25
|
+
styleCount: path_offenses.count { |offense| offense[:severity] == Check::SEVERITY_VALUES[:style] },
|
26
|
+
}
|
27
|
+
end
|
28
|
+
.sort_by { |o| o[:path] }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -2,21 +2,28 @@
|
|
2
2
|
|
3
3
|
module ThemeCheck
|
4
4
|
module LanguageServer
|
5
|
-
|
6
|
-
|
7
|
-
|
5
|
+
def self.partial_tag(tag)
|
6
|
+
%r{
|
7
|
+
\{\%-?\s*#{tag}\s+'(?<partial>[^']*)'|
|
8
|
+
\{\%-?\s*#{tag}\s+"(?<partial>[^"]*)"|
|
9
|
+
|
10
|
+
# in liquid tags the whole line is white space until the tag
|
11
|
+
^\s*#{tag}\s+'(?<partial>[^']*)'|
|
12
|
+
^\s*#{tag}\s+"(?<partial>[^"]*)"
|
13
|
+
}mix
|
14
|
+
end
|
15
|
+
|
16
|
+
PARTIAL_RENDER = partial_tag('render')
|
17
|
+
PARTIAL_INCLUDE = partial_tag('include')
|
18
|
+
PARTIAL_SECTION = partial_tag('section')
|
8
19
|
|
9
|
-
# in liquid tags the whole line is white space until render
|
10
|
-
^\s*render\s+'(?<partial>[^']*)'|
|
11
|
-
^\s*render\s+"(?<partial>[^"]*)"
|
12
|
-
}mix
|
13
20
|
ASSET_INCLUDE = %r{
|
14
|
-
\{
|
15
|
-
\{
|
21
|
+
\{\{-?\s*'(?<partial>[^']*)'\s*\|\s*asset_url|
|
22
|
+
\{\{-?\s*"(?<partial>[^"]*)"\s*\|\s*asset_url|
|
16
23
|
|
17
24
|
# in liquid tags the whole line is white space until the asset partial
|
18
|
-
^\s*'(?<partial>[^']*)'\s*\|\s*asset_url|
|
19
|
-
^\s*"(?<partial>[^"]*)"\s*\|\s*asset_url
|
25
|
+
^\s*(?:echo|assign[^=]*\=)\s*'(?<partial>[^']*)'\s*\|\s*asset_url|
|
26
|
+
^\s*(?:echo|assign[^=]*\=)\s*"(?<partial>[^"]*)"\s*\|\s*asset_url
|
20
27
|
}mix
|
21
28
|
end
|
22
29
|
end
|
@@ -3,81 +3,17 @@
|
|
3
3
|
module ThemeCheck
|
4
4
|
module LanguageServer
|
5
5
|
class DocumentLinkEngine
|
6
|
-
include PositionHelper
|
7
|
-
include RegexHelpers
|
8
|
-
|
9
6
|
def initialize(storage)
|
10
7
|
@storage = storage
|
8
|
+
@providers = DocumentLinkProvider.all.map { |x| x.new(storage) }
|
11
9
|
end
|
12
10
|
|
13
11
|
def document_links(relative_path)
|
14
12
|
buffer = @storage.read(relative_path)
|
15
13
|
return [] unless buffer
|
16
|
-
|
17
|
-
|
18
|
-
buffer,
|
19
|
-
match.begin(:partial),
|
20
|
-
)
|
21
|
-
|
22
|
-
end_line, end_character = from_index_to_row_column(
|
23
|
-
buffer,
|
24
|
-
match.end(:partial)
|
25
|
-
)
|
26
|
-
|
27
|
-
{
|
28
|
-
target: snippet_link(match[:partial]),
|
29
|
-
range: {
|
30
|
-
start: {
|
31
|
-
line: start_line,
|
32
|
-
character: start_character,
|
33
|
-
},
|
34
|
-
end: {
|
35
|
-
line: end_line,
|
36
|
-
character: end_character,
|
37
|
-
},
|
38
|
-
},
|
39
|
-
}
|
14
|
+
@providers.flat_map do |p|
|
15
|
+
p.document_links(buffer)
|
40
16
|
end
|
41
|
-
asset_matches = matches(buffer, ASSET_INCLUDE).map do |match|
|
42
|
-
start_line, start_character = from_index_to_row_column(
|
43
|
-
buffer,
|
44
|
-
match.begin(:partial),
|
45
|
-
)
|
46
|
-
|
47
|
-
end_line, end_character = from_index_to_row_column(
|
48
|
-
buffer,
|
49
|
-
match.end(:partial)
|
50
|
-
)
|
51
|
-
|
52
|
-
{
|
53
|
-
target: asset_link(match[:partial]),
|
54
|
-
range: {
|
55
|
-
start: {
|
56
|
-
line: start_line,
|
57
|
-
character: start_character,
|
58
|
-
},
|
59
|
-
end: {
|
60
|
-
line: end_line,
|
61
|
-
character: end_character,
|
62
|
-
},
|
63
|
-
},
|
64
|
-
}
|
65
|
-
end
|
66
|
-
snippet_matches + asset_matches
|
67
|
-
end
|
68
|
-
|
69
|
-
def snippet_link(partial)
|
70
|
-
file_link('snippets', partial, '.liquid')
|
71
|
-
end
|
72
|
-
|
73
|
-
def asset_link(partial)
|
74
|
-
file_link('assets', partial, '')
|
75
|
-
end
|
76
|
-
|
77
|
-
private
|
78
|
-
|
79
|
-
def file_link(directory, partial, extension)
|
80
|
-
"file://#{@storage.path(directory + '/' + partial + extension)}"
|
81
17
|
end
|
82
18
|
end
|
83
19
|
end
|