Skip to content

Commit 2f263c3

Browse files
committed
Add "Automatic" build variant.
1 parent 6d6a8e8 commit 2f263c3

File tree

6 files changed

+124
-8
lines changed

6 files changed

+124
-8
lines changed

RustEnhanced.sublime-build

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44
"command": "build",
55

66
"variants": [
7+
{
8+
"name": "Automatic",
9+
"command": "auto",
10+
},
711
{
812
"name": "Run",
913
"command": "run",

cargo_build.py

Lines changed: 54 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import sublime
55
import sublime_plugin
66
from .rust import (rust_proc, rust_thread, opanel, util, messages,
7-
cargo_settings)
7+
cargo_settings, target_detect)
88
from .rust.cargo_config import *
99

1010
# Maps command to an input string. Used to pre-populate the input panel with
@@ -43,15 +43,62 @@ class CargoExecCommand(sublime_plugin.WindowCommand):
4343
def run(self, command=None, command_info=None, settings=None):
4444
if command is None:
4545
return self.window.run_command('build', {'select': True})
46-
self.command = command
47-
self.command_info = cargo_settings.CARGO_COMMANDS\
48-
.get(command, {}).copy()
49-
if command_info:
50-
self.command_info.update(command_info)
5146
self.initial_settings = settings if settings else {}
5247
self.settings = cargo_settings.CargoSettings(self.window)
5348
self.settings.load()
54-
self._determine_working_path(self._run_check_for_args)
49+
if command == 'auto':
50+
self._detect_auto_build()
51+
else:
52+
self.command = command
53+
self.command_info = cargo_settings.CARGO_COMMANDS\
54+
.get(command, {}).copy()
55+
if command_info:
56+
self.command_info.update(command_info)
57+
self._determine_working_path(self._run_check_for_args)
58+
59+
def _detect_auto_build(self):
60+
"""Handle the "auto" build variant, which automatically picks a build
61+
command based on the current view."""
62+
if not util.active_view_is_rust():
63+
sublime.error_message(util.multiline_fix("""
64+
Error: Could not determine what to build.
65+
66+
Open a Rust source file as the active Sublime view.
67+
"""))
68+
return
69+
td = target_detect.TargetDetector(self.window)
70+
view = self.window.active_view()
71+
targets = td.determine_targets(view.file_name())
72+
if len(targets) == 0:
73+
sublime.error_message(util.multiline_fix("""
74+
Error: Could not determine what to build.
75+
76+
Try using one of the explicit build variants.
77+
"""))
78+
return
79+
80+
elif len(targets) == 1:
81+
self._auto_choice_made(targets, 0)
82+
83+
else:
84+
# Can't determine a single target, let the user choose one.
85+
display_items = [' '.join(x[1]) for x in targets]
86+
on_done = functools.partial(self._auto_choice_made, targets)
87+
self.window.show_quick_panel(display_items, on_done)
88+
89+
def _auto_choice_made(self, targets, index):
90+
if index != -1:
91+
src_path, cmd_line = targets[index]
92+
actions = {
93+
'--bin': 'run',
94+
'--example': 'run',
95+
'--lib': 'build',
96+
'--bench': 'bench',
97+
'--test': 'test',
98+
}
99+
cmd = actions[cmd_line[0]]
100+
self.initial_settings['target'] = ' '.join(cmd_line)
101+
self.run(command=cmd, settings=self.initial_settings)
55102

56103
def _determine_working_path(self, on_done):
57104
"""Determine where Cargo should be run.

docs/build.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ Ctrl-Shift-B / ⌘-Shift-B). They are:
3131
Variant | Command | Description
3232
------- | ------- | -----------
3333
(Default) | <code>cargo&nbsp;build</code> | Builds the project.
34+
Automatic | | Automatically detect the command to run based on the currently active view (tests do `test`, binaries and examples do `run`, libraries do `build`, benches do `bench`).
3435
Run | <code>cargo&nbsp;run</code> | Runs the binary.
3536
Run (with args)... | <code>cargo&nbsp;run&nbsp;-&#8288;-&#8288;&nbsp;*args*</code> | Runs the binary with optional arguments you specify.
3637
Test | <code>cargo&nbsp;test</code> | Runs unit and integration tests.

rust/cargo_settings.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,15 @@
2222
from . import util, target_detect
2323

2424
CARGO_COMMANDS = {
25+
'auto': {
26+
'name': 'Automatic',
27+
'command': 'auto',
28+
'allows_target': True,
29+
'allows_target_triple': True,
30+
'allows_release': True,
31+
'allows_features': True,
32+
'allows_json': True,
33+
},
2534
'build': {
2635
'name': 'Build',
2736
'command': 'build',

rust/target_detect.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ def _targets_longest_matches(self, targets, file_name):
153153
break
154154
path_match = p
155155
# If the match is both --bin and --lib in the same directory,
156-
# just do --lib.
156+
# just do --bin.
157157
if found_bin and found_lib:
158158
result = [x for x in result if x[1][0] != '--bin']
159159
return result

tests/test_cargo_build.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,3 +386,58 @@ def _test_build_env(self, view):
386386
self._run_build_wait('run')
387387
output = self._get_build_output(window)
388388
self.assertRegex(output, '(?m)^RUST_BUILD_ENV_TEST=abcdef$')
389+
390+
def test_auto_build(self):
391+
"""Test "auto" build."""
392+
tests = [
393+
# This should probably automatically use nightly?
394+
('benches/bench1.rs', r'may not be used on the stable release channel'),
395+
('examples/ex1.rs', r'(?m)^ex1$'),
396+
('src/bin/bin1.rs', r'(?m)^bin1$'),
397+
('src/altmain.rs', r'(?m)^altmain$'),
398+
('src/lib.rs', r'\[Running: cargo build --lib'),
399+
('src/lmod1.rs', r'\[Running: cargo build --lib'),
400+
('src/main.rs', r'(?m)^Hello$'),
401+
('tests/test1.rs', r'(?m)^test sample_test1 \.\.\. ok$'),
402+
]
403+
for path, pattern in tests:
404+
self._with_open_file('tests/multi-targets/' + path,
405+
self._test_auto_build, pattern=pattern)
406+
407+
def _test_auto_build(self, view, pattern=None):
408+
window = view.window()
409+
self._run_build_wait('auto')
410+
output = self._get_build_output(window)
411+
self.assertRegex(output, pattern)
412+
413+
def test_ambiguous_auto_build(self):
414+
"""Test "auto" build with indeterminate target."""
415+
self._with_open_file('tests/multi-targets/tests/common/helpers.rs',
416+
self._test_ambiguous_auto_build)
417+
418+
def _test_ambiguous_auto_build(self, view):
419+
window = view.window()
420+
sqp = window.__class__.show_quick_panel
421+
window.__class__.show_quick_panel = self._quick_panel
422+
try:
423+
self._test_ambiguous_auto_build2(view)
424+
finally:
425+
window.__class__.show_quick_panel = sqp
426+
427+
def _quick_panel(self, items, on_done, flags=0,
428+
selected_index=-1, on_highlighted=None):
429+
self.assertEqual(items, self.quick_panel_items)
430+
on_done(self.quick_panel_index)
431+
432+
def _test_ambiguous_auto_build2(self, view):
433+
window = view.window()
434+
self.quick_panel_items = ['--test test1', '--test test2']
435+
self.quick_panel_index = 0
436+
self._run_build_wait('auto')
437+
output = self._get_build_output(window)
438+
self.assertRegex(output, r'\[Running: cargo test --test test1 ')
439+
440+
self.quick_panel_index = 1
441+
self._run_build_wait('auto')
442+
output = self._get_build_output(window)
443+
self.assertRegex(output, r'\[Running: cargo test --test test2 ')

0 commit comments

Comments
 (0)