forked from rust-lang/rust-enhanced
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathSyntaxCheckPlugin.py
executable file
·181 lines (154 loc) · 6.71 KB
/
SyntaxCheckPlugin.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
import sublime
import sublime_plugin
import os
from .rust import (messages, rust_proc, rust_thread, util, target_detect,
cargo_settings, semver)
from pprint import pprint
"""On-save syntax checking.
This contains the code for displaying message phantoms for errors/warnings
whenever you save a Rust file.
"""
class RustSyntaxCheckEvent(sublime_plugin.EventListener):
# Beware: This gets called multiple times if the same buffer is opened in
# multiple views (with the same view passed in each time). See:
# https://github.com/SublimeTextIssues/Core/issues/289
def on_post_save(self, view):
# Are we in rust scope and is it switched on?
# We use phantoms which were added in 3118
if int(sublime.version()) < 3118:
return
enabled = util.get_setting('rust_syntax_checking', True)
if enabled and util.active_view_is_rust(view=view):
t = RustSyntaxCheckThread(view)
t.start()
elif not enabled:
# If the user has switched OFF the plugin, remove any phantom
# lines.
messages.clear_messages(view.window())
class RustSyntaxCheckThread(rust_thread.RustThread, rust_proc.ProcListener):
# Thread name.
name = 'Syntax Check'
# The Sublime view that triggered the check.
view = None
# The Sublime window that triggered the check.
window = None
# Absolute path to the view that triggered the check.
triggered_file_name = None
# Directory where cargo will be run.
cwd = None
# Base path for relative paths in messages.
msg_rel_path = None
# This flag is used to terminate early. In situations where we can't
# auto-detect the appropriate Cargo target, we compile multiple targets.
# If we receive any messages for the current view, we might as well stop.
# Otherwise, you risk displaying duplicate messages for shared modules.
this_view_found = False
# The path to the top-level Cargo target filename (like main.rs or
# lib.rs).
current_target_src = None
done = False
def __init__(self, view):
self.view = view
self.window = view.window()
super(RustSyntaxCheckThread, self).__init__(view.window())
def run(self):
self.triggered_file_name = os.path.abspath(self.view.file_name())
self.cwd = util.find_cargo_manifest(self.triggered_file_name)
if self.cwd is None:
# A manifest is required.
print('Rust Enhanced skipping on-save syntax check.')
print('Failed to find Cargo.toml from %r' % self.triggered_file_name)
print('A Cargo.toml manifest is required.')
return
self.update_status()
self.this_view_found = False
try:
messages.clear_messages(self.window)
try:
self.get_rustc_messages()
except rust_proc.ProcessTerminatedError:
return
messages.messages_finished(self.window)
finally:
self.done = True
self.window.status_message('')
def update_status(self, count=0):
if self.done:
return
num = count % 4
if num == 3:
num = 1
num += 1
msg = 'Rust check running' + '.' * num
self.window.status_message(msg)
sublime.set_timeout(lambda: self.update_status(count + 1), 200)
def get_rustc_messages(self):
"""Top-level entry point for generating messages for the given
filename.
:raises rust_proc.ProcessTerminatedError: Check was canceled.
"""
method = util.get_setting('rust_syntax_checking_method', 'check')
settings = cargo_settings.CargoSettings(self.window)
settings.load()
command_info = cargo_settings.CARGO_COMMANDS[method]
if method == 'clippy':
# Clippy does not support cargo target filters, must be run for
# all targets.
cmd = settings.get_command(method, command_info, self.cwd,
self.cwd, force_json=True)
self.msg_rel_path = cmd['msg_rel_path']
p = rust_proc.RustProc()
p.run(self.window, cmd['command'], self.cwd, self, env=cmd['env'])
p.wait()
return
if method == 'no-trans':
print('rust_syntax_checking_method == "no-trans" is no longer supported.')
print('Please change the config setting to "check".')
method = 'check'
if method != 'check':
print('Unknown setting for `rust_syntax_checking_method`: %r' % (method,))
return
td = target_detect.TargetDetector(self.window)
targets = td.determine_targets(self.triggered_file_name)
for (target_src, target_args) in targets:
cmd = settings.get_command(method, command_info, self.cwd, self.cwd,
initial_settings={'target': ' '.join(target_args)},
force_json=True)
self.msg_rel_path = cmd['msg_rel_path']
if (util.get_setting('rust_syntax_checking_include_tests', True) and
semver.match(cmd['rustc_version'], '>=1.23.0')):
# Including the test harness has a few drawbacks.
# missing_docs lint is disabled (see
# https://github.com/rust-lang/sublime-rust/issues/156)
# It also disables the "main function not found" error for
# binaries.
cmd['command'].append('--profile=test')
p = rust_proc.RustProc()
self.current_target_src = target_src
p.run(self.window, cmd['command'], self.cwd, self, env=cmd['env'])
p.wait()
if self.this_view_found:
break
#########################################################################
# ProcListner methods
#########################################################################
def on_begin(self, proc):
pass
def on_data(self, proc, data):
# Debugging on-save checking problems requires viewing output here,
# but it is difficult to segregate useful messages (like "thread
# 'main' panicked") from all the other output. Perhaps make a debug
# print setting?
pass
def on_error(self, proc, message):
print('Rust Error: %s' % message)
def on_json(self, proc, obj):
messages.add_rust_messages(self.window, self.msg_rel_path, obj,
self.current_target_src, msg_cb=None)
if messages.has_message_for_path(self.window,
self.triggered_file_name):
self.this_view_found = True
def on_finished(self, proc, rc):
pass
def on_terminated(self, proc):
pass