theme-check 1.7.2 → 1.9.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/CHANGELOG.md +47 -0
- data/README.md +10 -0
- data/RELEASING.md +13 -0
- data/config/default.yml +5 -0
- data/data/shopify_liquid/deprecated_filters.yml +4 -0
- data/data/shopify_liquid/filters.yml +3 -1
- data/docs/checks/TEMPLATE.md.erb +24 -19
- data/docs/checks/schema_json_format.md +76 -0
- data/docs/language_server/code-action-command-palette.png +0 -0
- data/docs/language_server/code-action-flow.png +0 -0
- data/docs/language_server/code-action-keyboard.png +0 -0
- data/docs/language_server/code-action-light-bulb.png +0 -0
- data/docs/language_server/code-action-problem.png +0 -0
- data/docs/language_server/code-action-quickfix.png +0 -0
- data/docs/language_server/how_to_correct_code_with_code_actions_and_execute_command.md +197 -0
- data/exe/theme-check-language-server +0 -4
- data/lib/theme_check/checks/asset_size_app_block_css.rb +2 -3
- data/lib/theme_check/checks/asset_size_app_block_javascript.rb +2 -3
- data/lib/theme_check/checks/asset_url_filters.rb +2 -0
- data/lib/theme_check/checks/default_locale.rb +1 -1
- data/lib/theme_check/checks/deprecated_filter.rb +81 -4
- data/lib/theme_check/checks/deprecated_global_app_block_type.rb +2 -3
- data/lib/theme_check/checks/matching_schema_translations.rb +14 -9
- data/lib/theme_check/checks/matching_translations.rb +1 -0
- data/lib/theme_check/checks/missing_required_template_files.rb +3 -3
- data/lib/theme_check/checks/missing_template.rb +1 -1
- data/lib/theme_check/checks/pagination_size.rb +2 -3
- data/lib/theme_check/checks/remote_asset.rb +5 -0
- data/lib/theme_check/checks/required_directories.rb +1 -1
- data/lib/theme_check/checks/required_layout_theme_object.rb +9 -4
- data/lib/theme_check/checks/schema_json_format.rb +29 -0
- data/lib/theme_check/checks/space_inside_braces.rb +132 -87
- data/lib/theme_check/checks/translation_key_exists.rb +33 -13
- data/lib/theme_check/checks/unused_assign.rb +3 -2
- data/lib/theme_check/checks/unused_snippet.rb +1 -1
- data/lib/theme_check/checks/valid_html_translation.rb +1 -1
- data/lib/theme_check/checks/valid_schema.rb +2 -2
- data/lib/theme_check/corrector.rb +34 -23
- data/lib/theme_check/file_system_storage.rb +4 -3
- data/lib/theme_check/html_node.rb +122 -6
- data/lib/theme_check/html_visitor.rb +1 -32
- data/lib/theme_check/in_memory_storage.rb +9 -0
- data/lib/theme_check/json_helpers.rb +14 -0
- data/lib/theme_check/language_server/bridge.rb +19 -5
- data/lib/theme_check/language_server/client_capabilities.rb +27 -0
- data/lib/theme_check/language_server/code_action_engine.rb +32 -0
- data/lib/theme_check/language_server/code_action_provider.rb +42 -0
- data/lib/theme_check/language_server/code_action_providers/quickfix_code_action_provider.rb +83 -0
- data/lib/theme_check/language_server/code_action_providers/source_fix_all_code_action_provider.rb +40 -0
- data/lib/theme_check/language_server/configuration.rb +69 -0
- data/lib/theme_check/language_server/diagnostic.rb +124 -0
- data/lib/theme_check/language_server/diagnostics_engine.rb +15 -60
- data/lib/theme_check/language_server/diagnostics_manager.rb +136 -0
- data/lib/theme_check/language_server/document_change_corrector.rb +267 -0
- data/lib/theme_check/language_server/document_link_provider.rb +6 -6
- data/lib/theme_check/language_server/execute_command_engine.rb +19 -0
- data/lib/theme_check/language_server/execute_command_provider.rb +30 -0
- data/lib/theme_check/language_server/execute_command_providers/correction_execute_command_provider.rb +48 -0
- data/lib/theme_check/language_server/execute_command_providers/run_checks_execute_command_provider.rb +22 -0
- data/lib/theme_check/language_server/handler.rb +83 -29
- data/lib/theme_check/language_server/io_messenger.rb +11 -1
- data/lib/theme_check/language_server/protocol.rb +4 -0
- data/lib/theme_check/language_server/server.rb +29 -11
- data/lib/theme_check/language_server/uri_helper.rb +1 -0
- data/lib/theme_check/language_server/versioned_in_memory_storage.rb +69 -0
- data/lib/theme_check/language_server.rb +23 -5
- data/lib/theme_check/liquid_node.rb +255 -12
- data/lib/theme_check/locale_diff.rb +39 -8
- data/lib/theme_check/node.rb +16 -0
- data/lib/theme_check/offense.rb +27 -23
- data/lib/theme_check/position.rb +4 -4
- data/lib/theme_check/regex_helpers.rb +1 -1
- data/lib/theme_check/schema_helper.rb +70 -0
- data/lib/theme_check/storage.rb +4 -0
- data/lib/theme_check/tags.rb +0 -1
- data/lib/theme_check/theme.rb +1 -1
- data/lib/theme_check/theme_file.rb +8 -1
- data/lib/theme_check/theme_file_rewriter.rb +28 -6
- data/lib/theme_check/version.rb +1 -1
- data/lib/theme_check.rb +11 -2
- metadata +26 -3
- data/lib/theme_check/language_server/diagnostics_tracker.rb +0 -66
@@ -37,12 +37,12 @@ module ThemeCheck
|
|
37
37
|
|
38
38
|
def document_links(buffer)
|
39
39
|
matches(buffer, partial_regexp).map do |match|
|
40
|
-
|
40
|
+
start_row, start_column = from_index_to_row_column(
|
41
41
|
buffer,
|
42
42
|
match.begin(:partial),
|
43
43
|
)
|
44
44
|
|
45
|
-
|
45
|
+
end_row, end_column = from_index_to_row_column(
|
46
46
|
buffer,
|
47
47
|
match.end(:partial)
|
48
48
|
)
|
@@ -51,12 +51,12 @@ module ThemeCheck
|
|
51
51
|
target: file_link(match[:partial]),
|
52
52
|
range: {
|
53
53
|
start: {
|
54
|
-
line:
|
55
|
-
character:
|
54
|
+
line: start_row,
|
55
|
+
character: start_column,
|
56
56
|
},
|
57
57
|
end: {
|
58
|
-
line:
|
59
|
-
character:
|
58
|
+
line: end_row,
|
59
|
+
character: end_column,
|
60
60
|
},
|
61
61
|
},
|
62
62
|
}
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ThemeCheck
|
4
|
+
module LanguageServer
|
5
|
+
class ExecuteCommandEngine
|
6
|
+
def initialize
|
7
|
+
@providers = {}
|
8
|
+
end
|
9
|
+
|
10
|
+
def <<(provider)
|
11
|
+
@providers[provider.command] = provider
|
12
|
+
end
|
13
|
+
|
14
|
+
def execute(command, arguments)
|
15
|
+
@providers[command].execute(arguments)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ThemeCheck
|
4
|
+
module LanguageServer
|
5
|
+
class ExecuteCommandProvider
|
6
|
+
class << self
|
7
|
+
def all
|
8
|
+
@all ||= []
|
9
|
+
end
|
10
|
+
|
11
|
+
def inherited(subclass)
|
12
|
+
all << subclass
|
13
|
+
end
|
14
|
+
|
15
|
+
def command(cmd = nil)
|
16
|
+
@command = cmd unless cmd.nil?
|
17
|
+
@command
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def execute(arguments)
|
22
|
+
raise NotImplementedError
|
23
|
+
end
|
24
|
+
|
25
|
+
def command
|
26
|
+
self.class.command
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ThemeCheck
|
4
|
+
module LanguageServer
|
5
|
+
class CorrectionExecuteCommandProvider < ExecuteCommandProvider
|
6
|
+
include URIHelper
|
7
|
+
|
8
|
+
command "correction"
|
9
|
+
|
10
|
+
attr_reader :storage, :bridge, :diagnostics_manager
|
11
|
+
|
12
|
+
def initialize(storage, bridge, diagnostics_manager)
|
13
|
+
@storage = storage
|
14
|
+
@bridge = bridge
|
15
|
+
@diagnostics_manager = diagnostics_manager
|
16
|
+
end
|
17
|
+
|
18
|
+
# The arguments passed to this method are the ones forwarded
|
19
|
+
# from the selected CodeAction by the client.
|
20
|
+
#
|
21
|
+
# @param diagnostic_hashes [Array] - of diagnostics
|
22
|
+
def execute(diagnostic_hashes)
|
23
|
+
# attempt to apply the document changes
|
24
|
+
workspace_edit = diagnostics_manager.workspace_edit(diagnostic_hashes)
|
25
|
+
result = bridge.send_request('workspace/applyEdit', {
|
26
|
+
label: 'Theme Check correction',
|
27
|
+
edit: workspace_edit,
|
28
|
+
})
|
29
|
+
|
30
|
+
# Bail if unable to apply changes
|
31
|
+
return unless result[:applied]
|
32
|
+
|
33
|
+
# Clean up internal representation of fixed diagnostics
|
34
|
+
diagnostics_update = diagnostics_manager.delete_applied(diagnostic_hashes)
|
35
|
+
|
36
|
+
# Send updated diagnostics to client
|
37
|
+
diagnostics_update
|
38
|
+
.map do |relative_path, diagnostics|
|
39
|
+
bridge.send_notification('textDocument/publishDiagnostics', {
|
40
|
+
uri: file_uri(storage.path(relative_path)),
|
41
|
+
diagnostics: diagnostics.map(&:to_h),
|
42
|
+
})
|
43
|
+
storage.path(relative_path)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ThemeCheck
|
4
|
+
module LanguageServer
|
5
|
+
class RunChecksExecuteCommandProvider < ExecuteCommandProvider
|
6
|
+
include URIHelper
|
7
|
+
|
8
|
+
command "runChecks"
|
9
|
+
|
10
|
+
def initialize(diagnostics_engine, root_path, root_config)
|
11
|
+
@diagnostics_engine = diagnostics_engine
|
12
|
+
@root_path = root_path
|
13
|
+
@root_config = root_config
|
14
|
+
end
|
15
|
+
|
16
|
+
def execute(_args)
|
17
|
+
@diagnostics_engine.analyze_and_send_offenses(@root_path, @root_config, force: true)
|
18
|
+
nil
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -17,7 +17,16 @@ module ThemeCheck
|
|
17
17
|
triggerCharacters: ['.', '{{ ', '{% '],
|
18
18
|
context: true,
|
19
19
|
},
|
20
|
+
codeActionProvider: {
|
21
|
+
codeActionKinds: CodeActionProvider.all.map(&:kind),
|
22
|
+
resolveProvider: false,
|
23
|
+
workDoneProgress: false,
|
24
|
+
},
|
20
25
|
documentLinkProvider: true,
|
26
|
+
executeCommandProvider: {
|
27
|
+
workDoneProgress: false,
|
28
|
+
commands: ExecuteCommandProvider.all.map(&:command),
|
29
|
+
},
|
21
30
|
textDocumentSync: {
|
22
31
|
openClose: true,
|
23
32
|
change: TextDocumentSyncKind::FULL,
|
@@ -36,52 +45,94 @@ module ThemeCheck
|
|
36
45
|
# Tell the client we don't support anything if there's no rootPath
|
37
46
|
return @bridge.send_response(id, { capabilities: {} }) if @root_path.nil?
|
38
47
|
|
39
|
-
@
|
48
|
+
@client_capabilities = ClientCapabilities.new(params.dig(:capabilities) || {})
|
49
|
+
@configuration = Configuration.new(@bridge, @client_capabilities)
|
50
|
+
@bridge.supports_work_done_progress = @client_capabilities.supports_work_done_progress?
|
40
51
|
@storage = in_memory_storage(@root_path)
|
52
|
+
@diagnostics_manager = DiagnosticsManager.new
|
41
53
|
@completion_engine = CompletionEngine.new(@storage)
|
42
54
|
@document_link_engine = DocumentLinkEngine.new(@storage)
|
43
|
-
@diagnostics_engine = DiagnosticsEngine.new(@bridge)
|
55
|
+
@diagnostics_engine = DiagnosticsEngine.new(@storage, @bridge, @diagnostics_manager)
|
56
|
+
@execute_command_engine = ExecuteCommandEngine.new
|
57
|
+
@execute_command_engine << CorrectionExecuteCommandProvider.new(@storage, @bridge, @diagnostics_manager)
|
58
|
+
@execute_command_engine << RunChecksExecuteCommandProvider.new(@diagnostics_engine, @root_path, config_for_path(@root_path))
|
59
|
+
@code_action_engine = CodeActionEngine.new(@storage, @diagnostics_manager)
|
44
60
|
@bridge.send_response(id, {
|
45
61
|
capabilities: CAPABILITIES,
|
46
62
|
serverInfo: SERVER_INFO,
|
47
63
|
})
|
48
64
|
end
|
49
65
|
|
66
|
+
def on_initialized(_id, _params)
|
67
|
+
@configuration.fetch
|
68
|
+
@configuration.register_did_change_capability
|
69
|
+
end
|
70
|
+
|
71
|
+
def on_shutdown(id, _params)
|
72
|
+
@bridge.send_response(id, nil)
|
73
|
+
end
|
74
|
+
|
50
75
|
def on_exit(_id, _params)
|
51
76
|
close!
|
52
77
|
end
|
53
|
-
alias_method :on_shutdown, :on_exit
|
54
78
|
|
55
|
-
def
|
79
|
+
def on_text_document_did_open(_id, params)
|
56
80
|
relative_path = relative_path_from_text_document_uri(params)
|
57
|
-
@storage.write(relative_path,
|
81
|
+
@storage.write(relative_path, text_document_text(params), text_document_version(params))
|
82
|
+
analyze_and_send_offenses(text_document_uri(params)) if @configuration.check_on_open?
|
58
83
|
end
|
59
84
|
|
60
|
-
def
|
85
|
+
def on_text_document_did_change(_id, params)
|
61
86
|
relative_path = relative_path_from_text_document_uri(params)
|
62
|
-
@storage.write(relative_path,
|
87
|
+
@storage.write(relative_path, content_changes_text(params), text_document_version(params))
|
88
|
+
analyze_and_send_offenses(text_document_uri(params)) if @configuration.check_on_change?
|
63
89
|
end
|
64
90
|
|
65
|
-
def
|
91
|
+
def on_text_document_did_close(_id, params)
|
66
92
|
relative_path = relative_path_from_text_document_uri(params)
|
67
|
-
|
68
|
-
|
93
|
+
file_system_content = Pathname.new(text_document_uri(params)).read(mode: 'rb', encoding: 'UTF-8')
|
94
|
+
# On close, the file system becomes the source of truth
|
95
|
+
@storage.write(relative_path, file_system_content, nil)
|
69
96
|
end
|
70
97
|
|
71
98
|
def on_text_document_did_save(_id, params)
|
72
|
-
analyze_and_send_offenses(text_document_uri(params))
|
99
|
+
analyze_and_send_offenses(text_document_uri(params)) if @configuration.check_on_save?
|
73
100
|
end
|
74
101
|
|
75
102
|
def on_text_document_document_link(id, params)
|
76
103
|
relative_path = relative_path_from_text_document_uri(params)
|
77
|
-
@bridge.send_response(id, document_links(relative_path))
|
104
|
+
@bridge.send_response(id, @document_link_engine.document_links(relative_path))
|
78
105
|
end
|
79
106
|
|
80
107
|
def on_text_document_completion(id, params)
|
81
108
|
relative_path = relative_path_from_text_document_uri(params)
|
82
|
-
line = params.dig(
|
83
|
-
col = params.dig(
|
84
|
-
@bridge.send_response(id, completions(relative_path, line, col))
|
109
|
+
line = params.dig(:position, :line)
|
110
|
+
col = params.dig(:position, :character)
|
111
|
+
@bridge.send_response(id, @completion_engine.completions(relative_path, line, col))
|
112
|
+
end
|
113
|
+
|
114
|
+
def on_text_document_code_action(id, params)
|
115
|
+
absolute_path = text_document_uri(params)
|
116
|
+
start_position = range_element(params, :start)
|
117
|
+
end_position = range_element(params, :end)
|
118
|
+
only_code_action_kinds = params.dig(:context, :only) || []
|
119
|
+
@bridge.send_response(id, @code_action_engine.code_actions(
|
120
|
+
absolute_path,
|
121
|
+
start_position,
|
122
|
+
end_position,
|
123
|
+
only_code_action_kinds,
|
124
|
+
))
|
125
|
+
end
|
126
|
+
|
127
|
+
def on_workspace_execute_command(id, params)
|
128
|
+
@bridge.send_response(id, @execute_command_engine.execute(
|
129
|
+
params[:command],
|
130
|
+
params[:arguments],
|
131
|
+
))
|
132
|
+
end
|
133
|
+
|
134
|
+
def on_workspace_did_change_configuration(_id, _params)
|
135
|
+
@configuration.fetch(force: true)
|
85
136
|
end
|
86
137
|
|
87
138
|
private
|
@@ -95,16 +146,16 @@ module ThemeCheck
|
|
95
146
|
ignored_patterns: config.ignored_patterns
|
96
147
|
)
|
97
148
|
|
98
|
-
# Turn that into a hash of
|
149
|
+
# Turn that into a hash of buffers
|
99
150
|
files = fs.files
|
100
|
-
.map { |fn| [fn,
|
151
|
+
.map { |fn| [fn, fs.read(fn)] }
|
101
152
|
.to_h
|
102
153
|
|
103
|
-
|
154
|
+
VersionedInMemoryStorage.new(files, config.root)
|
104
155
|
end
|
105
156
|
|
106
157
|
def text_document_uri(params)
|
107
|
-
file_path(params.dig(
|
158
|
+
file_path(params.dig(:textDocument, :uri))
|
108
159
|
end
|
109
160
|
|
110
161
|
def relative_path_from_text_document_uri(params)
|
@@ -112,8 +163,8 @@ module ThemeCheck
|
|
112
163
|
end
|
113
164
|
|
114
165
|
def root_path_from_params(params)
|
115
|
-
root_uri = params[
|
116
|
-
root_path = params[
|
166
|
+
root_uri = params[:rootUri]
|
167
|
+
root_path = params[:rootPath]
|
117
168
|
if root_uri
|
118
169
|
file_path(root_uri)
|
119
170
|
elsif root_path
|
@@ -122,11 +173,15 @@ module ThemeCheck
|
|
122
173
|
end
|
123
174
|
|
124
175
|
def text_document_text(params)
|
125
|
-
params.dig(
|
176
|
+
params.dig(:textDocument, :text)
|
177
|
+
end
|
178
|
+
|
179
|
+
def text_document_version(params)
|
180
|
+
params.dig(:textDocument, :version)
|
126
181
|
end
|
127
182
|
|
128
183
|
def content_changes_text(params)
|
129
|
-
params.dig(
|
184
|
+
params.dig(:contentChanges, 0, :text)
|
130
185
|
end
|
131
186
|
|
132
187
|
def config_for_path(path)
|
@@ -141,12 +196,11 @@ module ThemeCheck
|
|
141
196
|
)
|
142
197
|
end
|
143
198
|
|
144
|
-
def
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
@document_link_engine.document_links(relative_path)
|
199
|
+
def range_element(params, start_or_end)
|
200
|
+
[
|
201
|
+
params.dig(:range, start_or_end, :line),
|
202
|
+
params.dig(:range, start_or_end, :character),
|
203
|
+
]
|
150
204
|
end
|
151
205
|
|
152
206
|
def log(message)
|
@@ -3,10 +3,18 @@
|
|
3
3
|
module ThemeCheck
|
4
4
|
module LanguageServer
|
5
5
|
class IOMessenger < Messenger
|
6
|
+
def self.err_stream
|
7
|
+
if ThemeCheck.debug_log_file
|
8
|
+
File.open(ThemeCheck.debug_log_file, "w")
|
9
|
+
else
|
10
|
+
STDERR
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
6
14
|
def initialize(
|
7
15
|
in_stream: STDIN,
|
8
16
|
out_stream: STDOUT,
|
9
|
-
err_stream:
|
17
|
+
err_stream: IOMessenger.err_stream
|
10
18
|
)
|
11
19
|
validate!([in_stream, out_stream, err_stream])
|
12
20
|
|
@@ -40,6 +48,8 @@ module ThemeCheck
|
|
40
48
|
content += chunk
|
41
49
|
end
|
42
50
|
content.lstrip!
|
51
|
+
rescue IOError
|
52
|
+
raise DoneStreaming
|
43
53
|
end
|
44
54
|
|
45
55
|
def send_message(message_body)
|
@@ -37,7 +37,7 @@ module ThemeCheck
|
|
37
37
|
@handlers = []
|
38
38
|
|
39
39
|
# The error queue holds blocks the main thread. When filled, we exit the program.
|
40
|
-
@error = SizedQueue.new(
|
40
|
+
@error = SizedQueue.new(number_of_threads)
|
41
41
|
|
42
42
|
@should_raise_errors = should_raise_errors
|
43
43
|
end
|
@@ -55,9 +55,9 @@ module ThemeCheck
|
|
55
55
|
@json_rpc_thread = Thread.new do
|
56
56
|
loop do
|
57
57
|
message = @bridge.read_message
|
58
|
-
if message[
|
58
|
+
if message[:method] == 'initialize'
|
59
59
|
handle_message(message)
|
60
|
-
elsif message.key?(
|
60
|
+
elsif message.key?(:result)
|
61
61
|
# Responses are handled on the main thread to prevent
|
62
62
|
# a potential deadlock caused by all handlers waiting
|
63
63
|
# for a responses.
|
@@ -94,27 +94,36 @@ module ThemeCheck
|
|
94
94
|
|
95
95
|
rescue Exception => e # rubocop:disable Lint/RescueException
|
96
96
|
raise e if should_raise_errors
|
97
|
-
@bridge.log(e)
|
98
|
-
@bridge.log(e.backtrace)
|
97
|
+
@bridge.log("#{e.class}: #{e.message}\n#{e.backtrace.join("\n")}")
|
99
98
|
2
|
100
99
|
end
|
101
100
|
|
102
101
|
private
|
103
102
|
|
104
103
|
def handle_message(message)
|
105
|
-
id = message[
|
106
|
-
method_name = message[
|
104
|
+
id = message[:id]
|
105
|
+
method_name = message[:method]
|
107
106
|
method_name &&= "on_#{to_snake_case(method_name)}"
|
108
|
-
params = message[
|
107
|
+
params = message[:params]
|
109
108
|
|
110
109
|
if @handler.respond_to?(method_name)
|
111
110
|
@handler.send(method_name, id, params)
|
112
111
|
end
|
112
|
+
|
113
|
+
rescue DoneStreaming => e
|
114
|
+
raise e
|
115
|
+
rescue StandardError => e
|
116
|
+
is_request = id
|
117
|
+
raise e unless is_request
|
118
|
+
# Errors obtained in request handlers should be sent
|
119
|
+
# back as internal errors instead of closing the program.
|
120
|
+
@bridge.log("#{e.class}: #{e.message}\n#{e.backtrace.join("\n")}")
|
121
|
+
@bridge.send_internal_error(id, e)
|
113
122
|
end
|
114
123
|
|
115
124
|
def handle_response(message)
|
116
|
-
id = message[
|
117
|
-
result = message[
|
125
|
+
id = message[:id]
|
126
|
+
result = message[:result]
|
118
127
|
@bridge.receive_response(id, result)
|
119
128
|
end
|
120
129
|
|
@@ -135,7 +144,16 @@ module ThemeCheck
|
|
135
144
|
|
136
145
|
# Hijack the status_code if an error occurred while cleaning up.
|
137
146
|
# 👀 unit tests.
|
138
|
-
|
147
|
+
until @error.empty?
|
148
|
+
code = status_code_from_error(@error.pop)
|
149
|
+
# Promote the status_code to ERROR if one of the threads
|
150
|
+
# resulted in an error, otherwise leave the status_code as
|
151
|
+
# is. That's because one thread could end successfully in a
|
152
|
+
# DoneStreaming error while the other failed with an
|
153
|
+
# internal error. If we had an internal error, we should
|
154
|
+
# return with a status_code that fits.
|
155
|
+
status_code = code if code > status_code
|
156
|
+
end
|
139
157
|
status_code
|
140
158
|
ensure
|
141
159
|
@messenger.close_output
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ThemeCheck
|
4
|
+
class VersionedInMemoryStorage < InMemoryStorage
|
5
|
+
Version = Struct.new(:id, :version)
|
6
|
+
|
7
|
+
attr_reader :versions
|
8
|
+
|
9
|
+
def initialize(files, root = "/dev/null")
|
10
|
+
super(files, root)
|
11
|
+
@versions = {}
|
12
|
+
@mutex = Mutex.new
|
13
|
+
end
|
14
|
+
|
15
|
+
# Motivations:
|
16
|
+
# - Need way for LanguageServer to know on which version of a file
|
17
|
+
# the check was run on, because we need to know where the
|
18
|
+
# TextEdit goes. If the text changed, our TextEdit might not be
|
19
|
+
# in the right spot. e.g.
|
20
|
+
#
|
21
|
+
# Example:
|
22
|
+
#
|
23
|
+
# ```
|
24
|
+
# Hi
|
25
|
+
# {{world}}
|
26
|
+
# ```
|
27
|
+
#
|
28
|
+
# Would produce two "SpaceInsideBrace" errors:
|
29
|
+
#
|
30
|
+
# - One after {{ at index 5 to 6
|
31
|
+
# - One before }} at index 10 to 11
|
32
|
+
#
|
33
|
+
# If the user goes in and changes Hi to Sup, and _then_
|
34
|
+
# right clicks to apply the code edit at index 5 to 6, he'd
|
35
|
+
# get the following:
|
36
|
+
#
|
37
|
+
# ```
|
38
|
+
# Sup
|
39
|
+
# { {world}}
|
40
|
+
# ```
|
41
|
+
#
|
42
|
+
# Which is not a fix at all.
|
43
|
+
#
|
44
|
+
# Solution:
|
45
|
+
# - Have the LanguageServer store the version on textDocument/did{Open,Change,Close}
|
46
|
+
# - Have ThemeFile store the version right after @storage.read.
|
47
|
+
# - Add version to the diagnostic meta data
|
48
|
+
# - Use diagnostic meta data to determine if we can make a code edit or not
|
49
|
+
# - Only offer fixes on "clean" files (or offer the change but specify the version so the editor knows what to do with it)
|
50
|
+
def write(relative_path, content, version)
|
51
|
+
@mutex.synchronize do
|
52
|
+
@versions[relative_path] = version
|
53
|
+
super(relative_path, content)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def read_version(relative_path)
|
58
|
+
@mutex.synchronize { [read(relative_path), version(relative_path)] }
|
59
|
+
end
|
60
|
+
|
61
|
+
def versioned?
|
62
|
+
true
|
63
|
+
end
|
64
|
+
|
65
|
+
def version(relative_path)
|
66
|
+
@versions[relative_path.to_s]
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -1,31 +1,49 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
require_relative "language_server/protocol"
|
3
3
|
require_relative "language_server/constants"
|
4
|
+
require_relative "language_server/configuration"
|
4
5
|
require_relative "language_server/channel"
|
5
6
|
require_relative "language_server/messenger"
|
6
7
|
require_relative "language_server/io_messenger"
|
7
8
|
require_relative "language_server/bridge"
|
8
9
|
require_relative "language_server/uri_helper"
|
9
|
-
require_relative "language_server/handler"
|
10
10
|
require_relative "language_server/server"
|
11
11
|
require_relative "language_server/tokens"
|
12
12
|
require_relative "language_server/variable_lookup_finder"
|
13
|
+
require_relative "language_server/diagnostic"
|
14
|
+
require_relative "language_server/diagnostics_manager"
|
15
|
+
require_relative "language_server/diagnostics_engine"
|
16
|
+
require_relative "language_server/document_change_corrector"
|
17
|
+
require_relative "language_server/versioned_in_memory_storage"
|
18
|
+
require_relative "language_server/client_capabilities"
|
19
|
+
|
13
20
|
require_relative "language_server/completion_helper"
|
14
21
|
require_relative "language_server/completion_provider"
|
15
22
|
require_relative "language_server/completion_engine"
|
23
|
+
Dir[__dir__ + "/language_server/completion_providers/*.rb"].each do |file|
|
24
|
+
require file
|
25
|
+
end
|
26
|
+
|
16
27
|
require_relative "language_server/document_link_provider"
|
17
28
|
require_relative "language_server/document_link_engine"
|
18
|
-
|
19
|
-
|
29
|
+
Dir[__dir__ + "/language_server/document_link_providers/*.rb"].each do |file|
|
30
|
+
require file
|
31
|
+
end
|
20
32
|
|
21
|
-
|
33
|
+
require_relative "language_server/execute_command_provider"
|
34
|
+
require_relative "language_server/execute_command_engine"
|
35
|
+
Dir[__dir__ + "/language_server/execute_command_providers/*.rb"].each do |file|
|
22
36
|
require file
|
23
37
|
end
|
24
38
|
|
25
|
-
|
39
|
+
require_relative "language_server/code_action_provider"
|
40
|
+
require_relative "language_server/code_action_engine"
|
41
|
+
Dir[__dir__ + "/language_server/code_action_providers/*.rb"].each do |file|
|
26
42
|
require file
|
27
43
|
end
|
28
44
|
|
45
|
+
require_relative "language_server/handler"
|
46
|
+
|
29
47
|
module ThemeCheck
|
30
48
|
module LanguageServer
|
31
49
|
def self.start
|