theme-check 0.3.3 → 0.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b17d14eb5abc8211b6b6ae9f3a676c44770775821a87ce0909412d3eddb81c35
4
- data.tar.gz: 7bcad0d34832338dd8cd83f220b14c45b061b84b81c5ae2b3a18805aeb9400eb
3
+ metadata.gz: 5543690a150f2a864a5a9900feca80286ffe4c00d545885ee31dc02c9d389b71
4
+ data.tar.gz: c1c518e2013a1106dc7ace3a14ef5158853e06929694503777a37c0fc695025a
5
5
  SHA512:
6
- metadata.gz: 241dfdae6ec38c560ca4697c4b9256d4ceaed0300e9a575c3c92e864ea4fa2fd56420efd87ec2c0f744240fe9db5c6cc7df24c1da8d9606fab020bdd1d630450
7
- data.tar.gz: bbb52dcf01cf1dcbc3f73fe90d822b9ee22407a31525aab314ad0121e1bd9f52c786b6528594e5eb6224d97e567108bd7962a8c7a67d6e17702a976bab3edb1a
6
+ metadata.gz: 405832fbd93d17f3412abb83181b8c76c0aee43e2405bc7329b7ace746c20887c32ab638bf49c99adf4598532fc92d5840a46dc24d2a20b89909849b8c2813a3
7
+ data.tar.gz: 40316910f51d646017059d4256cba23dfcb9e20822a0987b65a04ab79c40c160c882d4bdf7e0cc4d646b08971ba04bb58999a7c5b93a4741233a8ce1c5ce4ad9
data/CHANGELOG.md CHANGED
@@ -1,4 +1,11 @@
1
1
 
2
+ v0.4.0 / 2021-02-25
3
+ ==================
4
+
5
+ * Add Completion Engine ([#161](https://github.com/shopify/theme-check/issues/161))
6
+ * Add init command to CLI ([#174](https://github.com/shopify/theme-check/issues/174))
7
+ * Refactor start and end Position logic ([#172](https://github.com/shopify/theme-check/issues/172))
8
+
2
9
  v0.3.3 / 2021-02-18
3
10
  ==================
4
11
 
data/README.md CHANGED
@@ -8,8 +8,6 @@ Theme Check is also available [inside some code editors](https://github.com/Shop
8
8
 
9
9
  ![](docs/preview.png)
10
10
 
11
- _Disclaimer: This tool is not supported as part of the Partners program._
12
-
13
11
  ## Supported Checks
14
12
 
15
13
  Theme Check currently checks for the following:
@@ -0,0 +1,26 @@
1
+ ---
2
+ - assign
3
+ - break
4
+ - capture
5
+ - case
6
+ - comment
7
+ - continue
8
+ - cycle
9
+ - decrement
10
+ - echo
11
+ - for
12
+ - form
13
+ - if
14
+ - ifchanged
15
+ - increment
16
+ - javascript
17
+ - layout
18
+ - paginate
19
+ - raw
20
+ - render
21
+ - schema
22
+ - section
23
+ - style
24
+ - stylesheet
25
+ - tablerow
26
+ - unless
@@ -11,6 +11,7 @@ module ThemeCheck
11
11
  -x, [--exclude-category] # Exclude this category of checks
12
12
  -l, [--list] # List enabled checks
13
13
  -a, [--auto-correct] # Automatically fix offenses
14
+ --init # Generate a .theme-check.yml file in the current directory
14
15
  -h, [--help] # Show this. Hi!
15
16
  -v, [--version] # Print Theme Check version
16
17
 
@@ -22,7 +23,7 @@ module ThemeCheck
22
23
  END
23
24
 
24
25
  def run(argv)
25
- path = "."
26
+ @path = "."
26
27
 
27
28
  command = :check
28
29
  only_categories = []
@@ -44,15 +45,19 @@ module ThemeCheck
44
45
  command = :list
45
46
  when "--auto-correct", "-a"
46
47
  auto_correct = true
48
+ when "--init"
49
+ command = :init
47
50
  else
48
- path = arg
51
+ @path = arg
49
52
  end
50
53
  end
51
54
 
52
- @config = ThemeCheck::Config.from_path(path)
53
- @config.only_categories = only_categories
54
- @config.exclude_categories = exclude_categories
55
- @config.auto_correct = auto_correct
55
+ unless [:version, :init].include?(command)
56
+ @config = ThemeCheck::Config.from_path(@path)
57
+ @config.only_categories = only_categories
58
+ @config.exclude_categories = exclude_categories
59
+ @config.auto_correct = auto_correct
60
+ end
56
61
 
57
62
  send(command)
58
63
  end
@@ -75,6 +80,17 @@ module ThemeCheck
75
80
  puts ThemeCheck::VERSION
76
81
  end
77
82
 
83
+ def init
84
+ dotfile_path = ThemeCheck::Config.find(@path)
85
+ if dotfile_path.nil?
86
+ File.write(File.join(@path, ThemeCheck::Config::DOTFILE), File.read(ThemeCheck::Config::DEFAULT_CONFIG))
87
+
88
+ puts "Writing new #{ThemeCheck::Config::DOTFILE} to #{@path}"
89
+ else
90
+ raise Abort, "#{ThemeCheck::Config::DOTFILE} already exists at #{@path}."
91
+ end
92
+ end
93
+
78
94
  def check
79
95
  puts "Checking #{@config.root} ..."
80
96
  storage = ThemeCheck::FileSystemStorage.new(@config.root, ignored_patterns: @config.ignored_patterns)
@@ -6,7 +6,7 @@
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 = {})
10
10
  @files = files
11
11
  end
12
12
 
@@ -1,6 +1,16 @@
1
1
  # frozen_string_literal: true
2
+ require_relative "language_server/protocol"
2
3
  require_relative "language_server/handler"
3
4
  require_relative "language_server/server"
5
+ require_relative "language_server/tokens"
6
+ require_relative "language_server/position_helper"
7
+ require_relative "language_server/completion_helper"
8
+ require_relative "language_server/completion_provider"
9
+ require_relative "language_server/completion_engine"
10
+
11
+ Dir[__dir__ + "/language_server/completion_providers/*.rb"].each do |file|
12
+ require file
13
+ end
4
14
 
5
15
  module ThemeCheck
6
16
  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(&:new)
11
+ end
12
+
13
+ def completions(name, line, col)
14
+ buffer = @storage.read(name)
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,35 @@
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
+
24
+ def matches(s, re)
25
+ start_at = 0
26
+ matches = []
27
+ while (m = s.match(re, start_at))
28
+ matches.push(m)
29
+ start_at = m.end(0)
30
+ end
31
+ matches
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ThemeCheck
4
+ module LanguageServer
5
+ class CompletionProvider
6
+ include CompletionHelper
7
+
8
+ class << self
9
+ def all
10
+ @all ||= []
11
+ end
12
+
13
+ def inherited(subclass)
14
+ all << subclass
15
+ end
16
+ end
17
+
18
+ def completions(content, cursor)
19
+ raise NotImplementedError
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ThemeCheck
4
+ module LanguageServer
5
+ class FilterCompletionProvider < CompletionProvider
6
+ NAMED_FILTER = /#{Liquid::FilterSeparator}\s*(\w+)/o
7
+
8
+ def completions(content, cursor)
9
+ return [] unless can_complete?(content, cursor)
10
+ ShopifyLiquid::Filter.labels
11
+ .select { |w| w.starts_with?(partial(content, cursor)) }
12
+ .map { |filter| filter_to_completion(filter) }
13
+ end
14
+
15
+ def can_complete?(content, cursor)
16
+ content.match?(Liquid::FilterSeparator) && (
17
+ cursor_on_start_content?(content, cursor, Liquid::FilterSeparator) ||
18
+ cursor_on_filter?(content, cursor)
19
+ )
20
+ end
21
+
22
+ private
23
+
24
+ def cursor_on_filter?(content, cursor)
25
+ return false unless content.match?(NAMED_FILTER)
26
+ matches(content, NAMED_FILTER).any? do |match|
27
+ match.begin(1) <= cursor && cursor < match.end(1) + 1 # including next character
28
+ end
29
+ end
30
+
31
+ def partial(content, cursor)
32
+ return '' unless content.match?(NAMED_FILTER)
33
+ partial_match = matches(content, NAMED_FILTER).find do |match|
34
+ match.begin(1) <= cursor && cursor < match.end(1) + 1 # including next character
35
+ end
36
+ partial_match[1]
37
+ end
38
+
39
+ def filter_to_completion(filter)
40
+ {
41
+ label: filter,
42
+ kind: CompletionItemKinds::FUNCTION,
43
+ }
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ThemeCheck
4
+ module LanguageServer
5
+ class ObjectCompletionProvider < CompletionProvider
6
+ def completions(content, cursor)
7
+ return [] unless can_complete?(content, cursor)
8
+ partial = first_word(content) || ''
9
+ ShopifyLiquid::Object.labels
10
+ .select { |w| w.starts_with?(partial) }
11
+ .map { |object| object_to_completion(object) }
12
+ end
13
+
14
+ def can_complete?(content, cursor)
15
+ content.match?(Liquid::VariableStart) && (
16
+ cursor_on_first_word?(content, cursor) ||
17
+ cursor_on_start_content?(content, cursor, Liquid::VariableStart)
18
+ )
19
+ end
20
+
21
+ private
22
+
23
+ def object_to_completion(object)
24
+ {
25
+ label: object,
26
+ kind: CompletionItemKinds::VARIABLE,
27
+ }
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ThemeCheck
4
+ module LanguageServer
5
+ class TagCompletionProvider < CompletionProvider
6
+ def completions(content, cursor)
7
+ return [] unless can_complete?(content, cursor)
8
+ partial = first_word(content) || ''
9
+ ShopifyLiquid::Tag.labels
10
+ .select { |w| w.starts_with?(partial) }
11
+ .map { |tag| tag_to_completion(tag) }
12
+ end
13
+
14
+ def can_complete?(content, cursor)
15
+ content.starts_with?(Liquid::TagStart) && (
16
+ cursor_on_first_word?(content, cursor) ||
17
+ cursor_on_start_content?(content, cursor, Liquid::TagStart)
18
+ )
19
+ end
20
+
21
+ private
22
+
23
+ def tag_to_completion(tag)
24
+ {
25
+ label: tag,
26
+ kind: CompletionItemKinds::KEYWORD,
27
+ }
28
+ end
29
+ end
30
+ end
31
+ end
@@ -4,9 +4,13 @@ module ThemeCheck
4
4
  module LanguageServer
5
5
  class Handler
6
6
  CAPABILITIES = {
7
+ completionProvider: {
8
+ triggerCharacters: ['.', '{{ ', '{% '],
9
+ context: true,
10
+ },
7
11
  textDocumentSync: {
8
12
  openClose: true,
9
- change: false,
13
+ change: TextDocumentSyncKind::FULL,
10
14
  willSave: false,
11
15
  save: true,
12
16
  },
@@ -15,6 +19,8 @@ module ThemeCheck
15
19
  def initialize(server)
16
20
  @server = server
17
21
  @previously_reported_files = Set.new
22
+ @storage = InMemoryStorage.new
23
+ @completion_engine = CompletionEngine.new(@storage)
18
24
  end
19
25
 
20
26
  def on_initialize(id, params)
@@ -31,14 +37,52 @@ module ThemeCheck
31
37
  def on_exit(_id, _params)
32
38
  close!
33
39
  end
40
+ alias_method :on_shutdown, :on_exit
41
+
42
+ def on_text_document_did_change(_id, params)
43
+ uri = text_document_uri(params)
44
+ @storage.write(uri, content_changes_text(params))
45
+ end
46
+
47
+ def on_text_document_did_close(_id, params)
48
+ uri = text_document_uri(params)
49
+ @storage.write(uri, nil)
50
+ end
34
51
 
35
52
  def on_text_document_did_open(_id, params)
36
- analyze_and_send_offenses(params.dig('textDocument', 'uri').sub('file://', ''))
53
+ uri = text_document_uri(params)
54
+ @storage.write(uri, text_document_text(params))
55
+ analyze_and_send_offenses(uri)
56
+ end
57
+
58
+ def on_text_document_did_save(_id, params)
59
+ analyze_and_send_offenses(text_document_uri(params))
60
+ end
61
+
62
+ def on_text_document_completion(id, params)
63
+ uri = text_document_uri(params)
64
+ line = params.dig('position', 'line')
65
+ col = params.dig('position', 'character')
66
+ send_response(
67
+ id: id,
68
+ result: completions(uri, line, col)
69
+ )
37
70
  end
38
- alias_method :on_text_document_did_save, :on_text_document_did_open
39
71
 
40
72
  private
41
73
 
74
+ def text_document_uri(params)
75
+ params.dig('textDocument', 'uri').sub('file://', '')
76
+ end
77
+
78
+ def text_document_text(params)
79
+ params.dig('textDocument', 'text')
80
+ end
81
+
82
+ def content_changes_text(params)
83
+ params.dig('contentChanges', 0, 'text')
84
+ end
85
+
42
86
  def analyze_and_send_offenses(file_path)
43
87
  root = ThemeCheck::Config.find(file_path) || @root_path
44
88
  config = ThemeCheck::Config.from_path(root)
@@ -60,6 +104,10 @@ module ThemeCheck
60
104
  analyzer.offenses
61
105
  end
62
106
 
107
+ def completions(uri, line, col)
108
+ @completion_engine.completions(uri, line, col)
109
+ end
110
+
63
111
  def send_diagnostics(offenses)
64
112
  reported_files = Set.new
65
113
 
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+ # Note: Everything is 0-indexed here.
3
+
4
+ module ThemeCheck
5
+ module LanguageServer
6
+ module PositionHelper
7
+ def from_line_column_to_index(content, row, col)
8
+ i = 0
9
+ result = 0
10
+ lines = content.lines
11
+ while i < row
12
+ result += lines[i].size
13
+ i += 1
14
+ end
15
+ result += col
16
+ result
17
+ end
18
+
19
+ def from_index_to_line_column(content, index)
20
+ lines = content[0..index].lines
21
+ row = lines.size - 1
22
+ col = lines.last.size - 1
23
+ [row, col]
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+ # Here we define the Language Server Protocol Constants we're using.
3
+ # For complete docs, see the following:
4
+ # https://microsoft.github.io/language-server-protocol/specifications/specification-current
5
+ module ThemeCheck
6
+ module LanguageServer
7
+ module CompletionItemKinds
8
+ TEXT = 1
9
+ METHOD = 2
10
+ FUNCTION = 3
11
+ CONSTRUCTOR = 4
12
+ FIELD = 5
13
+ VARIABLE = 6
14
+ CLASS = 7
15
+ INTERFACE = 8
16
+ MODULE = 9
17
+ PROPERTY = 10
18
+ UNIT = 11
19
+ VALUE = 12
20
+ ENUM = 13
21
+ KEYWORD = 14
22
+ SNIPPET = 15
23
+ COLOR = 16
24
+ FILE = 17
25
+ REFERENCE = 18
26
+ FOLDER = 19
27
+ ENUM_MEMBER = 20
28
+ CONSTANT = 21
29
+ STRUCT = 22
30
+ EVENT = 23
31
+ OPERATOR = 24
32
+ TYPE_PARAMETER = 25
33
+ end
34
+
35
+ module TextDocumentSyncKind
36
+ NONE = 0
37
+ FULL = 1
38
+ INCREMENTAL = 2
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ThemeCheck
4
+ Token = Struct.new(
5
+ :content,
6
+ :start, # inclusive
7
+ :end, # exclusive
8
+ )
9
+
10
+ TAG_START = Liquid::TagStart
11
+ TAG_END = Liquid::TagEnd
12
+ VARIABLE_START = Liquid::VariableStart
13
+ VARIABLE_END = Liquid::VariableEnd
14
+ SPLITTER = %r{
15
+ (?=(?:#{TAG_START}|#{VARIABLE_START}))| # positive lookahead on tag/variable start
16
+ (?<=(?:#{TAG_END}|#{VARIABLE_END})) # positive lookbehind on tag/variable end
17
+ }xom
18
+
19
+ # Implemented as an Enumerable so we stop iterating on the find once
20
+ # we have what we want. Kind of a perf thing.
21
+ class Tokens
22
+ include Enumerable
23
+
24
+ def initialize(buffer)
25
+ @buffer = buffer
26
+ end
27
+
28
+ def each(&block)
29
+ return to_enum(:each) unless block_given?
30
+
31
+ chunks = @buffer.split(SPLITTER)
32
+ chunks.shift if chunks[0]&.empty?
33
+
34
+ prev = Token.new('', 0, 0)
35
+ curr = Token.new('', 0, 0)
36
+
37
+ while (content = chunks.shift)
38
+
39
+ curr.start = prev.end
40
+ curr.end = curr.start + content.size
41
+
42
+ block.call(Token.new(
43
+ content,
44
+ curr.start,
45
+ curr.end,
46
+ ))
47
+
48
+ # recycling structs
49
+ tmp = prev
50
+ prev = curr
51
+ curr = tmp
52
+ end
53
+ end
54
+ end
55
+ end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  module ThemeCheck
3
+ Position = Struct.new(:line, :column)
4
+
3
5
  class Offense
4
6
  MAX_SOURCE_EXCERPT_SIZE = 120
5
7
 
@@ -35,6 +37,9 @@ module ThemeCheck
35
37
  elsif @node
36
38
  @node.line_number
37
39
  end
40
+
41
+ @start_position = nil
42
+ @end_position = nil
38
43
  end
39
44
 
40
45
  def source_excerpt
@@ -50,29 +55,19 @@ module ThemeCheck
50
55
  end
51
56
 
52
57
  def start_line
53
- return 0 unless line_number
54
- line_number - 1
58
+ start_position.line
55
59
  end
56
60
 
57
- def end_line
58
- if markup&.ends_with?("\n")
59
- start_line + markup.count("\n") - 1
60
- elsif markup
61
- start_line + markup.count("\n")
62
- else
63
- start_line
64
- end
61
+ def start_column
62
+ start_position.column
65
63
  end
66
64
 
67
- def start_column
68
- return 0 unless line_number && markup
69
- template.full_line(start_line + 1).index(markup.split("\n", 2).first)
65
+ def end_line
66
+ end_position.line
70
67
  end
71
68
 
72
69
  def end_column
73
- return 0 unless line_number && markup
74
- markup_end = markup.split("\n").last
75
- template.full_line(end_line + 1).index(markup_end) + markup_end.size
70
+ end_position.column
76
71
  end
77
72
 
78
73
  def code_name
@@ -118,5 +113,45 @@ module ThemeCheck
118
113
  message
119
114
  end
120
115
  end
116
+
117
+ private
118
+
119
+ def full_line(line)
120
+ # Liquid::Template is 1-indexed.
121
+ template.full_line(line + 1)
122
+ end
123
+
124
+ def lines_of_content
125
+ @lines ||= markup.lines.map { |x| x.sub(/\n$/, '') }
126
+ end
127
+
128
+ # 0-indexed, inclusive
129
+ def start_position
130
+ return @start_position if @start_position
131
+ return @start_position = Position.new(0, 0) unless line_number && markup
132
+
133
+ position = Position.new
134
+ position.line = line_number - 1
135
+ position.column = full_line(position.line).index(lines_of_content.first) || 0
136
+
137
+ @start_position = position
138
+ end
139
+
140
+ # 0-indexed, exclusive. It's the line + col that are exclusive.
141
+ # This is why it doesn't make sense to calculate them separately.
142
+ def end_position
143
+ return @end_position if @end_position
144
+ return @end_position = Position.new(0, 0) unless line_number && markup
145
+
146
+ position = Position.new
147
+ position.line = start_line + lines_of_content.size - 1
148
+ position.column = if start_line == position.line
149
+ start_column + markup.size
150
+ else
151
+ lines_of_content.last.size
152
+ end
153
+
154
+ @end_position = position
155
+ end
121
156
  end
122
157
  end
@@ -2,3 +2,4 @@
2
2
  require_relative 'shopify_liquid/deprecated_filter'
3
3
  require_relative 'shopify_liquid/filter'
4
4
  require_relative 'shopify_liquid/object'
5
+ require_relative 'shopify_liquid/tag'
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+ require 'yaml'
3
+
4
+ module ThemeCheck
5
+ module ShopifyLiquid
6
+ module Tag
7
+ extend self
8
+
9
+ def labels
10
+ @tags ||= begin
11
+ YAML.load(File.read("#{__dir__}/../../../data/shopify_liquid/tags.yml"))
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module ThemeCheck
3
- VERSION = "0.3.3"
3
+ VERSION = "0.4.0"
4
4
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: theme-check
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.3
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marc-André Cournoyer
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-02-18 00:00:00.000000000 Z
11
+ date: 2021-02-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: liquid
@@ -80,6 +80,7 @@ files:
80
80
  - data/shopify_liquid/filters.yml
81
81
  - data/shopify_liquid/objects.yml
82
82
  - data/shopify_liquid/plus_objects.yml
83
+ - data/shopify_liquid/tags.yml
83
84
  - dev.yml
84
85
  - docs/preview.png
85
86
  - exe/theme-check
@@ -123,8 +124,17 @@ files:
123
124
  - lib/theme_check/json_file.rb
124
125
  - lib/theme_check/json_helpers.rb
125
126
  - lib/theme_check/language_server.rb
127
+ - lib/theme_check/language_server/completion_engine.rb
128
+ - lib/theme_check/language_server/completion_helper.rb
129
+ - lib/theme_check/language_server/completion_provider.rb
130
+ - lib/theme_check/language_server/completion_providers/filter_completion_provider.rb
131
+ - lib/theme_check/language_server/completion_providers/object_completion_provider.rb
132
+ - lib/theme_check/language_server/completion_providers/tag_completion_provider.rb
126
133
  - lib/theme_check/language_server/handler.rb
134
+ - lib/theme_check/language_server/position_helper.rb
135
+ - lib/theme_check/language_server/protocol.rb
127
136
  - lib/theme_check/language_server/server.rb
137
+ - lib/theme_check/language_server/tokens.rb
128
138
  - lib/theme_check/liquid_check.rb
129
139
  - lib/theme_check/locale_diff.rb
130
140
  - lib/theme_check/node.rb
@@ -136,6 +146,7 @@ files:
136
146
  - lib/theme_check/shopify_liquid/deprecated_filter.rb
137
147
  - lib/theme_check/shopify_liquid/filter.rb
138
148
  - lib/theme_check/shopify_liquid/object.rb
149
+ - lib/theme_check/shopify_liquid/tag.rb
139
150
  - lib/theme_check/storage.rb
140
151
  - lib/theme_check/tags.rb
141
152
  - lib/theme_check/template.rb