├── .gitignore ├── .sublimelinterrc ├── .travis.yml ├── LICENSE ├── README.md ├── linter.py ├── messages.json └── messages └── install.txt /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /.sublimelinterrc: -------------------------------------------------------------------------------- 1 | { 2 | "@python": 3, 3 | "linters": { 4 | "flake8": { 5 | "max-line-length": 120 6 | }, 7 | "pep8": { 8 | "max-line-length": 120 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "3.3" 4 | # command to install dependencies 5 | install: 6 | - pip install flake8 7 | - pip install pep257 8 | # command to run tests 9 | script: 10 | - flake8 *.py --max-line-length=120 11 | - pep257 *.py 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any person obtaining a copy 2 | of this software and associated documentation files (the "Software"), to deal 3 | in the Software without restriction, including without limitation the rights 4 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 5 | copies of the Software, and to permit persons to whom the Software is 6 | furnished to do so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in 9 | all copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 12 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 13 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 14 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 15 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 16 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 17 | THE SOFTWARE. 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | SublimeLinter-contrib-rustc 2 | ================================ 3 | 4 | [![Build Status](https://travis-ci.org/oschwald/SublimeLinter-contrib-rustc.svg?branch=master)](https://travis-ci.org/oschwald/SublimeLinter-contrib-rustc) 5 | 6 | This linter plugin for [SublimeLinter][docs] provides an interface to [rustc](http://www.rust-lang.org/). It will be used with files that have the "rust" syntax. 7 | 8 | ## Installation 9 | SublimeLinter 3 must be installed in order to use this plugin. If SublimeLinter 3 is not installed, please follow the instructions [here][installation]. 10 | 11 | ### Linter installation 12 | Before using this plugin, you must ensure that `rustc` is installed on your system. To install `rustc`, install Rust as directed in the [Rust tutorial](https://static.rust-lang.org/doc/master/book/getting-started.html). 13 | 14 | ### Linter configuration 15 | In order for `rustc` to be executed by SublimeLinter, you must ensure that its path is available to SublimeLinter. Before going any further, please read and follow the steps in ["Finding a linter executable"](http://sublimelinter.readthedocs.org/en/latest/troubleshooting.html#finding-a-linter-executable) through “Validating your PATH” in the documentation. 16 | 17 | Once you have installed and configured `rustc`, you can proceed to install the SublimeLinter-contrib-rustc plugin if it is not yet installed. 18 | 19 | ### Plugin installation 20 | Please use [Package Control][pc] to install the linter plugin. This will ensure that the plugin will be updated when new versions are available. If you want to install from source so you can modify the source code, you probably know what you are doing so we won’t cover that here. 21 | 22 | To install via Package Control, do the following: 23 | 24 | 1. Within Sublime Text, bring up the [Command Palette][cmd] and type `install`. Among the commands you should see `Package Control: Install Package`. If that command is not highlighted, use the keyboard or mouse to select it. There will be a pause of a few seconds while Package Control fetches the list of available plugins. 25 | 26 | 1. When the plugin list appears, type `rustc`. Among the entries you should see `SublimeLinter-contrib-rustc`. If that entry is not highlighted, use the keyboard or mouse to select it. 27 | 28 | ## Settings 29 | For general information on how SublimeLinter works with settings, please see [Settings][settings]. For information on generic linter settings, please see [Linter Settings][linter-settings]. 30 | 31 | In addition to the standard SublimeLinter settings, SublimeLinter-contrib-rustc provides its own settings. 32 | 33 | |Setting|Description| 34 | |:------|:----------| 35 | |use-cargo|Use `cargo` to run rustc if a Cargo.toml is defined for your project.| 36 | |use-cargo-check|Use `cargo check` to validate. Requires `cargo-check` and a Cargo.toml for your project.| 37 | |use-crate-root|Run rustc against the crate root to allow for multi-file module structures| 38 | |crate-root|Override the filename of the crate root. Defaults are `main.rs` and `lib.rs`| 39 | 40 | ## Contributing 41 | If you would like to contribute enhancements or fixes, please do the following: 42 | 43 | 1. Fork the plugin repository. 44 | 2. Hack on a separate topic branch created from the latest `master`. 45 | 3. Commit and push the topic branch. 46 | 4. Make a pull request. 47 | 5. Be patient. ;-) 48 | 49 | Please note that modification should follow these coding guidelines: 50 | 51 | - Indent is 4 spaces. 52 | - Code should pass flake8 and pep257 linters. 53 | - Vertical whitespace helps readability, don't be afraid to use it. 54 | - Please use descriptive variable names, no abbreviations unless they are very well known. 55 | 56 | Thank you for helping out! 57 | 58 | [docs]: http://sublimelinter.readthedocs.org 59 | [installation]: http://sublimelinter.readthedocs.org/en/latest/installation.html 60 | [locating-executables]: http://sublimelinter.readthedocs.org/en/latest/usage.html#how-linter-executables-are-located 61 | [pc]: https://sublime.wbond.net/installation 62 | [cmd]: http://docs.sublimetext.info/en/sublime-text-3/extensibility/command_palette.html 63 | [settings]: http://sublimelinter.readthedocs.org/en/latest/settings.html 64 | [linter-settings]: http://sublimelinter.readthedocs.org/en/latest/linter_settings.html 65 | [inline-settings]: http://sublimelinter.readthedocs.org/en/latest/settings.html#inline-settings 66 | -------------------------------------------------------------------------------- /linter.py: -------------------------------------------------------------------------------- 1 | # 2 | # linter.py 3 | # Linter for SublimeLinter3, a code checking framework for Sublime Text 3 4 | # 5 | # Written by Gregory Oschwald 6 | # Copyright (c) 2014 Gregory Oschwald 7 | # 8 | # License: MIT 9 | # 10 | """This module exports the Rustc plugin class.""" 11 | 12 | import os 13 | from SublimeLinter.lint import Linter, util, persist 14 | 15 | 16 | class Rust(Linter): 17 | """Provides an interface to Rust.""" 18 | 19 | defaults = { 20 | 'use-cargo': False, 21 | 'use-cargo-check': False, 22 | 'use-crate-root': False, 23 | 'crate-root': None, 24 | } 25 | cmd = ['rustc'] 26 | syntax = ('rust', 'rustenhanced') 27 | tempfile_suffix = 'rs' 28 | 29 | regex = r'''(?xi) 30 | ^(?:(?P(error|fatal error))|(?Pwarning)).*?:\s+(?P.+)\s*\r? 31 | -->\s+(?P.+?):(?P\d+):(?P\d+)$ 32 | ''' 33 | 34 | multiline = True 35 | use_cargo = False 36 | use_cargo_check = False 37 | use_crate_root = False 38 | cargo_config = None 39 | crate_root = None 40 | 41 | def run(self, cmd, code): 42 | """ 43 | Return a list with the command to execute. 44 | 45 | The command chosen is resolved as follows: 46 | 47 | If the `use-cargo` option is set, lint using a `cargo build`. 48 | If cargo is not used, and the `use-crate-root` option is set, lint 49 | the crate root. Finally, if the crate root cannot be determined, or the 50 | `use-crate-root` option is not set, lint the current file. 51 | 52 | Linting the crate (either through cargo or rustc) means that if 53 | errors are caught in other files, errors on the current file might 54 | not show up until these other errors are resolved. 55 | 56 | Linting a single file means that any imports from higher in the 57 | module hierarchy will probably cause an error and prevent proper 58 | linting in the rest of the file. 59 | """ 60 | self.use_cargo = self.get_view_settings().get('use-cargo', False) 61 | self.use_cargo_check = self.get_view_settings().get('use-cargo-check', 62 | False) 63 | self.use_crate_root = self.get_view_settings().get('use-crate-root', 64 | False) 65 | 66 | if self.use_cargo or self.use_cargo_check: 67 | cargo_cmd = ['check'] if self.use_cargo_check else self.cmd 68 | 69 | current_dir = os.path.dirname(self.filename) 70 | self.cargo_config = util.find_file(current_dir, 'Cargo.toml') 71 | 72 | if self.cargo_config: 73 | self.tempfile_suffix = '-' 74 | 75 | old_cwd = os.getcwd() 76 | os.chdir(os.path.dirname(self.cargo_config)) 77 | try: 78 | return util.communicate( 79 | ['cargo'] + cargo_cmd + ['--manifest-path', 80 | self.cargo_config], 81 | code=None, 82 | output_stream=self.error_stream, 83 | env=self.env) 84 | finally: 85 | os.chdir(old_cwd) 86 | 87 | if self.use_crate_root: 88 | self.crate_root = self.locate_crate_root() 89 | 90 | if self.crate_root: 91 | cmd.append(self.crate_root) 92 | self.tempfile_suffix = '-' 93 | 94 | return util.communicate(cmd, 95 | code=None, 96 | output_stream=self.error_stream, 97 | env=self.env) 98 | 99 | self.tempfile_suffix = 'rs' 100 | return self.tmpfile(cmd, code) 101 | 102 | def split_match(self, match): 103 | """ 104 | Return the components of the match. 105 | 106 | We override this because Cargo lints all referenced files, 107 | and we only want errors from the linted file. The same applies 108 | when linting from the crate root. Of course when linting a single 109 | file only, all the errors will be from that file because it is 110 | in a temporary directory. 111 | 112 | The matched file path is considered in the context of a working directory. 113 | If it is an absolute path, the working directory will be ignored. This 114 | working directory is not the same as the current Sublime Text process 115 | working directory -- it is the working directory of an external command. 116 | 117 | For Cargo, the working directory is the directory of Cargo.toml. 118 | When working with a crate root, the working directory is the directory of the 119 | crate root source file. 120 | """ 121 | # if match: 122 | # if os.path.basename(self.filename) != os.path.basename(match.group('file')): 123 | # match = None 124 | 125 | matched_file = match.group('file') if match else None 126 | 127 | if matched_file: 128 | if self.use_cargo: 129 | path = self.cargo_config 130 | elif self.use_crate_root: 131 | path = self.crate_root 132 | else: 133 | path = False 134 | 135 | if path: 136 | working_dir = os.path.dirname(path) 137 | if not self.is_current_file(working_dir, matched_file): 138 | match = None 139 | 140 | return super().split_match(match) 141 | 142 | def is_current_file(self, working_dir, matched_file): 143 | """ 144 | Return true if `matched_file` is logically the same file as `self.filename`. 145 | 146 | Cargo example demonstrating how matching is done: 147 | 148 | - os.getcwd() = '/Applications/Sublime Text.app/Contents/MacOS' 149 | - `working_dir` = '/path/to/project' 150 | - `matched_file` = 'src/foo.rs' 151 | - `self.filename` = '/path/to/project/src/foo.rs' 152 | 153 | The current OS directory is not considered at all -- comparison is only done 154 | relative to where Cargo.toml was found. `os.path.realpath` is used to 155 | normalize the filenames so that they can be directly compared after manipulation. 156 | """ 157 | abs_matched_file = os.path.join(working_dir, matched_file) 158 | 159 | persist.debug('Sublime Text cwd: ', os.getcwd()) 160 | persist.debug('Build cwd: ', working_dir) 161 | persist.debug('Current filename: ', self.filename) 162 | persist.debug('Matched filename: ', matched_file) 163 | persist.debug('Compared filename: ', abs_matched_file) 164 | 165 | return os.path.realpath(self.filename) == os.path.realpath( 166 | abs_matched_file) 167 | 168 | def locate_crate_root(self): 169 | """ 170 | Return the filename of the crate root. 171 | 172 | The filename may be manually set in a configuration file (highest priority), 173 | or it is located by convention. 174 | 175 | When no configuration is set, main.rs will take preference over lib.rs. 176 | If neither main.rs or lib.rs are found, give up. 177 | """ 178 | crate_root = self.get_view_settings().get('crate-root', None) 179 | 180 | if not crate_root: 181 | crate_root = util.find_file( 182 | os.path.dirname(self.filename), 'main.rs') 183 | 184 | if not crate_root: 185 | crate_root = util.find_file( 186 | os.path.dirname(self.filename), 'lib.rs') 187 | 188 | return crate_root 189 | -------------------------------------------------------------------------------- /messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "install": "messages/install.txt" 3 | } 4 | -------------------------------------------------------------------------------- /messages/install.txt: -------------------------------------------------------------------------------- 1 | SublimeLinter-contrib-rustc 2 | ------------------------------- 3 | This linter plugin for SublimeLinter provides an interface to rustc. 4 | 5 | ** IMPORTANT! ** 6 | 7 | Before this plugin will activate, you *must* 8 | follow the installation instructions here: 9 | 10 | https://github.com/oschwald/SublimeLinter-contrib-rustc 11 | --------------------------------------------------------------------------------