diff --git a/BeautifyRust.py b/BeautifyRust.py index 09dbf47..23b725a 100644 --- a/BeautifyRust.py +++ b/BeautifyRust.py @@ -1,11 +1,17 @@ +import os import os.path import sublime import sublime_plugin import subprocess +import tempfile SETTINGS_FILE = "BeautifyRust.sublime-settings" +def temp_opener(name, flag, mode=0o777): + return os.open(name, flag | os.O_TEMPORARY, mode) + + def which(program): def is_exe(fpath): return os.path.isfile(fpath) and os.access(fpath, os.X_OK) @@ -24,7 +30,7 @@ def is_exe(fpath): class BeautifyRustOnSave(sublime_plugin.EventListener): - def on_post_save(self, view): + def on_pre_save(self, view): if sublime.load_settings(SETTINGS_FILE).get("run_on_save", False): return view.run_command("beautify_rust") return @@ -32,6 +38,8 @@ def on_post_save(self, view): class BeautifyRustCommand(sublime_plugin.TextCommand): + ENCODING_UTF8 = "UTF-8" + def run(self, edit): self.filename = self.view.file_name() self.fname = os.path.basename(self.filename) @@ -52,7 +60,7 @@ def pipe(self, cmd): cmd, cwd=cwd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, startupinfo=startupinfo) (_, err) = beautifier.communicate() - return (beautifier.wait(), err.decode('utf8')) + return (beautifier.wait(), err.decode(self.ENCODING_UTF8)) def run_format(self, edit): buffer_region = sublime.Region(0, self.view.size()) @@ -63,17 +71,19 @@ def run_format(self, edit): if rustfmt_bin is None: return sublime.error_message( "Beautify rust: can not find {0} in path.".format(self.settings.get("rustfmt", "rustfmt"))) - cmd_list = [rustfmt_bin, self.filename, "--write-mode=overwrite"] + self.settings.get("args", []) - self.save_viewport_state() - (exit_code, err) = self.pipe(cmd_list) - if exit_code != 0 or (err != "" and not err.startswith("Using rustfmt")): + + try: + self.save_viewport_state() + formatted_source = self.format_in_temporary_file(rustfmt_bin, buffer_text) + self.view.replace(edit, buffer_region, formatted_source) + self.reset_viewport_state() + except Exception as ex: + (exit_code, err) = ex.args self.view.replace(edit, buffer_region, buffer_text) print("failed: exit_code: {0}\n{1}".format(exit_code, err)) if sublime.load_settings(SETTINGS_FILE).get("show_errors", True): sublime.error_message( "Beautify rust: rustfmt process call failed. See log (ctrl + `) for details.") - self.view.window().run_command("reload_all_files") - self.reset_viewport_state() def save_viewport_state(self): self.previous_selection = [(region.a, region.b) @@ -86,3 +96,17 @@ def reset_viewport_state(self): self.view.sel().clear() for a, b in self.previous_selection: self.view.sel().add(sublime.Region(a, b)) + + def format_in_temporary_file(self, rustfmt_bin, rust_source): + with tempfile.NamedTemporaryFile() as f: + f.write(bytes(rust_source, self.ENCODING_UTF8)) + f.flush() + + cmd_list = [rustfmt_bin, f.name, "--write-mode=overwrite"] + self.settings.get("args", []) + + (exit_code, err) = self.pipe(cmd_list) + if exit_code != 0 or (err != "" and not err.startswith("Using rustfmt")): + raise Exception(exit_code, err) + + with open(f.name, "rb", opener=temp_opener) as f: + return f.read().decode(self.ENCODING_UTF8)