├── Context.sublime-menu ├── Default.sublime-commands ├── LICENSE ├── Readme.md ├── auto_import.py └── demo └── rust_auto_import_demo_3.gif /Context.sublime-menu: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "caption": "Rust: auto-import", 4 | "mnemonic": "a", 5 | "command": "rust_auto_import", 6 | // the below doesn't actually do anything sadly, hopefully this will get added at some point 7 | "context": [ 8 | { "key": "selector", "operator": "equal", "operand": "source.rust - string - comment", "match_all": true }, 9 | ] 10 | } 11 | ] 12 | -------------------------------------------------------------------------------- /Default.sublime-commands: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "caption": "Rust: Auto-Import", 4 | "command": "rust_auto_import", 5 | // the below doesn't actually do anything sadly, hopefully this will get added at some point 6 | "context": [ 7 | { "key": "selector", "operator": "equal", "operand": "source.rust - string - comment", "match_all": true }, 8 | ] 9 | } 10 | ] 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2019 Tristan Hume 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # Sublime Rust Auto-Import 2 | 3 | This is a Sublime Text 3 plugin for automatically creating imports in Rust. 4 | It provides the `rust_auto_import` command which you can bind to a key (or use from the context menu or command pallete). When used with the cursor on an identifier, it automatically `use`s the (hopefully) correct path at the top of the file. 5 | 6 | ## Features 7 | 8 | - Uses Sublime's index to find the module for items in the current crate 9 | - Has a pre-programmed list of common `std` imports like `HashMap` 10 | - Even some traits like `std::str::From` with your cursor on `from_str` 11 | - Places new imports under the existing import with the most similar prefix 12 | - Adds in brackets if an import from that module already exists 13 | - Adds the old position to the jump stack so you can hit `ctrl+-` to jump back to where you were 14 | 15 | ## Demo GIF 16 | 17 | ![Demo GIF](demo/rust_auto_import_demo_3.gif) 18 | 19 | ## Installation 20 | 21 | Install [RustAutoImport](https://packagecontrol.io/packages/RustAutoImport) from Package Control. 22 | 23 | Then add a keybinding of your choice for the `rust_auto_import` command, by adding a line like this to your keybindings file: 24 | 25 | ```json 26 | { "keys": ["ctrl+a"], "command": "rust_auto_import" }, 27 | ``` 28 | 29 | You can also try it out immediately by choosing "Rust: auto-import" from the context menu or command pallete. 30 | 31 | ## Disclaimers 32 | 33 | Feel free to send a PR if you want to fix one of these. 34 | 35 | - Currently uses the 2018 edition import style with no setting to change it 36 | - Only includes a limited set of standard library includes 37 | - Uses hacky regexes instead of real parsing 38 | - Doesn't fully support nested braced imports 39 | - Can only add to top-level imports not imports in a test module 40 | -------------------------------------------------------------------------------- /auto_import.py: -------------------------------------------------------------------------------- 1 | import sublime, sublime_plugin 2 | import re 3 | import os 4 | 5 | from Default import history_list 6 | 7 | COMMON_PATHS = { 8 | "HashMap": "std::collections::HashMap", 9 | "HashSet": "std::collections::HashSet", 10 | "BTreeMap": "std::collections::BTreeMap", 11 | "BTreeSet": "std::collections::BTreeSet", 12 | "VecDeque": "std::collections::VecDeque", 13 | "BinaryHeap": "std::collections::BinaryHeap", 14 | 15 | "Range": "std::ops::Range", 16 | 17 | "Path": "std::path::Path", 18 | "PathBuf": "std::path::PathBuf", 19 | "File": "std::fs::File", 20 | 21 | "Command": "std::process::Command", 22 | "Output": "std::process::Output", 23 | 24 | "Reader": "std::io::Reader", 25 | "BufReader": "std::io::BufReader", 26 | "Writer": "std::io::Writer", 27 | "BufWriter": "std::io::BufWriter", 28 | 29 | "mem": "std::mem", 30 | "io": "std::io", 31 | "fmt": "std::fmt", 32 | 33 | "Rc": "std::rc::Rc", 34 | "RefCell": "std::cell::RefCell", 35 | "Cell": "std::cell::Cell", 36 | 37 | "Arc": "std::sync::Arc", 38 | "Mutex": "std::sync::Mutex", 39 | "RwLock": "std::sync::RwLock", 40 | "Once": "std::sync::Once", 41 | 42 | "Sender": "std::sync::mpsc::Sender", 43 | "Receiver": "std::sync::mpsc::Receiver", 44 | "channel": "std::sync::mpsc::channel", 45 | 46 | "Error": "std::error::Error", 47 | "Display": "std::fmt::Display", 48 | 49 | "Ordering": "std::cmp::Ordering", 50 | 51 | "from_str": "std::str::FromStr", 52 | "borrow": "std::borrow::Borrow", 53 | "borrow_mut": "std::borrow::BorrowMut", 54 | "read_line": "std::io::BufRead", 55 | "lines": "std::io::BufRead", 56 | "read_to_end": "std::io::Read", 57 | "read_to_string": "std::io::Read", 58 | } 59 | 60 | def extract_path(loc, symbol): 61 | plain_path = os.path.splitext(loc[1])[0] 62 | split = plain_path.split("/") 63 | 64 | if split[-1] == "mod" or split[-1] == "lib": 65 | split.pop() 66 | 67 | return ["crate"] + split[1:] + [symbol] 68 | 69 | def find_common_path(symbol): 70 | base_path = COMMON_PATHS.get(symbol) 71 | if base_path is None: 72 | return ["std", "x", symbol] 73 | 74 | return base_path.split("::") 75 | 76 | def matchiness(a,b): 77 | m = 0 78 | for sa, sb in zip(a,b): 79 | if sa != sb: 80 | return m 81 | m += 1 82 | return m 83 | 84 | class RustAutoImportCommand(sublime_plugin.TextCommand): 85 | def _existing_insert_point(self, import_path, edit): 86 | stem = "::".join(import_path[:-1]) 87 | query = "^use {}::\\{{[^}}]+\\}};$".format(stem) 88 | 89 | use_statements = self.view.find_all(query) 90 | 91 | if len(use_statements) > 0: 92 | # inside the brackets 93 | return use_statements[0].b - 2 94 | 95 | query = "^use {}::\\w+;$".format(stem) 96 | use_statements = self.view.find_all(query) 97 | 98 | if len(use_statements) == 0: 99 | return None 100 | 101 | r = use_statements[0] 102 | self.view.insert(edit, r.b - 1, "}") 103 | self.view.insert(edit, r.a + len("use {}::".format(stem)), "{") 104 | return r.b 105 | 106 | 107 | def _new_insert_point(self, import_path): 108 | paths = [] 109 | use_statements = self.view.find_all("^use ((?:\w|:)+)(?:;|::\\{|::\\*)", 0, "\\1", paths) 110 | 111 | if len(use_statements) == 0: 112 | return 0 113 | 114 | best_i = 0 115 | best_m = 0 116 | for i,path_s in enumerate(paths): 117 | path = path_s.split("::") 118 | m = matchiness(path, import_path) 119 | if m >= best_m: 120 | best_i, best_m = i,m 121 | 122 | use_start = use_statements[best_i].a 123 | r,c = self.view.rowcol(use_start) 124 | return self.view.text_point(r+1,c) 125 | 126 | def run(self, edit, **args): 127 | history_list.get_jump_history_for_view(self.view).push_selection(self.view) 128 | symbol = self.view.substr(self.view.word(self.view.sel()[0])) 129 | 130 | locs = self.view.window().lookup_symbol_in_index(symbol) 131 | locs = [l for l in locs if l[1].endswith(".rs")] 132 | 133 | if len(locs) > 0: 134 | import_path = extract_path(locs[0], symbol) 135 | else: 136 | import_path = find_common_path(symbol) 137 | 138 | if import_path is None: 139 | return 140 | 141 | existing_insert_pt = self._existing_insert_point(import_path, edit) 142 | if existing_insert_pt is not None: 143 | new_import = ", {}".format(import_path[-1]) 144 | self.view.insert(edit, existing_insert_pt, new_import) 145 | sel_i = existing_insert_pt + 2 146 | else: 147 | # insert the new use statement 148 | insert_pt = self._new_insert_point(import_path) 149 | new_import = "use {};\n".format("::".join(import_path)) 150 | self.view.insert(edit, insert_pt, new_import) 151 | sel_i = insert_pt + len(new_import) - 2 - len(import_path[-1]) 152 | 153 | # Select just after the end of the statement 154 | sel = self.view.sel() 155 | sel.clear() 156 | sel.add(sublime.Region(sel_i, sel_i)) 157 | 158 | # scroll t show it 159 | self.view.show(sel_i) 160 | -------------------------------------------------------------------------------- /demo/rust_auto_import_demo_3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trishume/SublimeRustAutoImport/f8a9ffb8224815db7f15eeaaeeac75cbfd6dd834/demo/rust_auto_import_demo_3.gif --------------------------------------------------------------------------------