diff --git a/RustEnhanced.sublime-commands b/RustEnhanced.sublime-commands index 2ba0b82..429cc9a 100644 --- a/RustEnhanced.sublime-commands +++ b/RustEnhanced.sublime-commands @@ -56,6 +56,10 @@ "caption": "Rust: Open Debug Log", "command": "rust_open_log" }, + { + "caption": "Rust: Open Last Build Output As View", + "command": "rust_show_build_output" + }, { "caption": "Rust: Popup Message At Cursor", "command": "rust_message_popup" diff --git a/SyntaxCheckPlugin.py b/SyntaxCheckPlugin.py index 722c97c..d839319 100755 --- a/SyntaxCheckPlugin.py +++ b/SyntaxCheckPlugin.py @@ -67,6 +67,7 @@ class RustSyntaxCheckThread(rust_thread.RustThread, rust_proc.ProcListener): def __init__(self, view): self.view = view self.window = view.window() + self.rendered = [] super(RustSyntaxCheckThread, self).__init__(view.window()) def run(self): @@ -190,23 +191,37 @@ def get_rustc_messages(self): ######################################################################### def on_begin(self, proc): - pass + self.rendered.append('[Running: %s]' % (' '.join(proc.cmd),)) def on_data(self, proc, data): log.log(self.window, data) + self.rendered.append(data) def on_error(self, proc, message): log.critical(self.window, 'Rust Error: %s', message) + self.rendered.append(message) def on_json(self, proc, obj): - messages.add_rust_messages(self.window, self.msg_rel_path, obj, + try: + message = obj['message'] + except KeyError: + return + messages.add_rust_messages(self.window, self.msg_rel_path, message, self.current_target_src, msg_cb=None) if messages.has_message_for_path(self.window, self.triggered_file_name): self.this_view_found = True + try: + self.rendered.append(message['rendered']) + except KeyError: + pass def on_finished(self, proc, rc): log.log(self.window, 'On-save check finished.') + # TODO: Also put message in self.rendered about [Finished in …] + # TODO: Figure out how to share all this code between here and opanel + win_info = messages.get_or_init_window_info(self.window) + win_info['rendered'] = ''.join(self.rendered) def on_terminated(self, proc): log.log(self.window, 'Process Interrupted') diff --git a/cargo_build.py b/cargo_build.py index ae443b2..fc67e8c 100644 --- a/cargo_build.py +++ b/cargo_build.py @@ -243,7 +243,10 @@ def on_load_async(self): class NextPrevBase(sublime_plugin.WindowCommand): def _has_inline(self): - return self.window.id() in messages.WINDOW_MESSAGES + try: + return messages.WINDOW_MESSAGES[self.window.id()]['has_inline'] + except KeyError: + return False class RustNextMessageCommand(NextPrevBase): @@ -521,6 +524,22 @@ def on_selection_modified_async(self): messages.update_status(view) +class RustShowBuildOutput(sublime_plugin.WindowCommand): + + """Opens a view with the rustc-rendered compiler output.""" + + def run(self): + view = self.window.new_file() + view.set_scratch(True) + view.set_name('Rust Enhanced Build Output') + view.assign_syntax('Cargo.sublime-syntax') + win_info = messages.get_or_init_window_info(self.window) + output = win_info['rendered'] + if output == '': + output = "No output available for this window." + view.run_command('append', {'characters': output}) + + class RustEventListener(sublime_plugin.EventListener): def on_activated_async(self, view): diff --git a/rust/messages.py b/rust/messages.py index c7cb0c2..51ef4ca 100644 --- a/rust/messages.py +++ b/rust/messages.py @@ -21,11 +21,15 @@ # Value is a dictionary: { # 'paths': {path: [MessageBatch, ...]}, # 'batch_index': (path_idx, message_idx), -# 'hidden': bool +# 'hidden': bool, +# 'rendered': string, +# 'has_inline': False, # } # `paths` is an OrderedDict to handle next/prev message. # `path` is the absolute path to the file. # `hidden` indicates that all messages have been dismissed. +# `rendered` is the rustc-rendered output +# 'has_inline' is a boolean indicating if inline messages were added WINDOW_MESSAGES = {} @@ -302,9 +306,8 @@ def _draw_region_highlights(view, batch): def batches_at_point(view, point, hover_zone): """Return a list of message batches at the given point.""" - try: - winfo = WINDOW_MESSAGES[view.window().id()] - except KeyError: + winfo = get_window_info_for_view(view) + if winfo is None: return if winfo['hidden']: return @@ -615,9 +618,8 @@ def redraw_all_open_views(window): def show_messages_for_view(view): """Adds all phantoms and region outlines for a view.""" - try: - winfo = WINDOW_MESSAGES[view.window().id()] - except KeyError: + winfo = get_window_info_for_view(view) + if winfo is None: return if winfo['hidden']: return @@ -628,9 +630,8 @@ def show_messages_for_view(view): def draw_regions_if_missing(view): - try: - winfo = WINDOW_MESSAGES[view.window().id()] - except KeyError: + winfo = get_window_info_for_view(view) + if winfo is None: return if winfo['hidden']: return @@ -1180,16 +1181,9 @@ def _save_batches(window, batches, msg_cb): - Displays phantoms if a view is already open. - Calls `msg_cb` for each individual message. """ - wid = window.id() - try: - path_to_batches = WINDOW_MESSAGES[wid]['paths'] - except KeyError: - path_to_batches = collections.OrderedDict() - WINDOW_MESSAGES[wid] = { - 'paths': path_to_batches, - 'batch_index': (-1, -1), - 'hidden': False, - } + win_info = get_or_init_window_info(window) + win_info['has_inline'] = True + path_to_batches = win_info['paths'] for batch in batches: path_batches = path_to_batches.setdefault(batch.path(), []) @@ -1198,7 +1192,7 @@ def _save_batches(window, batches, msg_cb): path_batches.append(batch) for i, msg in enumerate(batch): msg.region_key = 'rust-%i' % (num + i,) - if not WINDOW_MESSAGES[wid]['hidden']: + if not win_info['hidden']: views = util.open_views_for_file(window, batch.path()) if views: # Phantoms seem to be attached to the buffer. @@ -1208,3 +1202,32 @@ def _save_batches(window, batches, msg_cb): if msg_cb: for msg in batch: msg_cb(msg) + + +def get_or_init_window_info(window): + """Returns the window info for the given window, creating it if it hasn't been set.""" + wid = window.id() + try: + return WINDOW_MESSAGES[wid] + except KeyError: + win_info = { + 'paths': collections.OrderedDict(), + 'batch_index': (-1, -1), + 'hidden': False, + 'rendered': '', + 'has_inline': False, + } + WINDOW_MESSAGES[wid] = win_info + return win_info + + +def get_window_info_for_view(view): + """Returns the window info for the given view, or None if not available.""" + window = view.window() + if window is None: + # I'm not entire sure why this happens sometimes. + return None + try: + return WINDOW_MESSAGES[window.id()] + except KeyError: + return None diff --git a/rust/opanel.py b/rust/opanel.py index 57a54d5..3a2d14f 100644 --- a/rust/opanel.py +++ b/rust/opanel.py @@ -71,6 +71,7 @@ def __init__(self, window, base_path, command_name, rustc_version): self.base_path = base_path self.command_name = command_name self.rustc_version = rustc_version + self.rendered = [] def on_begin(self, proc): self.output_view = create_output_panel(self.window, self.base_path) @@ -114,9 +115,16 @@ def on_error(self, proc, message): self._append(message) def on_json(self, proc, obj): - if 'message' in obj: - messages.add_rust_messages(self.window, self.base_path, obj['message'], - None, self.msg_cb) + try: + message = obj['message'] + except KeyError: + return + messages.add_rust_messages(self.window, self.base_path, message, + None, self.msg_cb) + try: + self.rendered.append(message['rendered']) + except KeyError: + pass def msg_cb(self, message): """Display the message in the output panel. Also marks the message @@ -155,6 +163,8 @@ def on_finished(self, proc, rc): # Tell Sublime to find all of the lines with pattern from # result_file_regex. self.output_view.find_all_results() + win_info = messages.get_or_init_window_info(self.window) + win_info['rendered'] = ''.join(self.rendered) def on_terminated(self, proc): self._append('[Build interrupted]') @@ -163,6 +173,7 @@ def _append(self, message, nl=True): if nl: message += '\n' _append(self.output_view, message) + self.rendered.append(message) def _display_debug(self, proc): # Display some information to help the user debug any build problems.