class EditorContext(object): search_id_lock = threading.Lock() # FIXME(guillermooo): This is utterly wrong. This needs to be a singleton. def __init__(self): self._search_id = None self.results_panel = None @property def search_id(self): with EditorContext.search_id_lock: return self._search_id @search_id.setter def search_id(self, value): with EditorContext.search_id_lock: if self._search_id == value: return self.results_panel = OutputPanel('dart.search.results') self.results_panel.set('result_file_regex', r'^\w+\s+-\s+(.*?):(\d+):(\d+)') self._search_id = value @search_id.deleter def search_id(self): with EditorContext.search_id_lock: self._search_id = None def check_token(self, action, token): if action == 'search': if self.search_id is None: return return (token == self.search_id) def append_search_results(self, items): items = list(items) with EditorContext.search_id_lock: if not self.results_panel: return for item in items: self.results_panel.write(item.to_encoded_pos()) self.results_panel.show()
def show_errors(errors): '''Show errors in the ui. @errors An instance of `ErrorInfoCollection`. ''' v = sublime.active_window().active_view() # TODO(guillermooo): Use tokens to identify requests:file. if errors.file != v.file_name(): _logger.debug('different view active - aborting') return if len(errors) == 0: clear_ui() return _logger.debug('displaying errors to the user') v.add_regions('dart.infos', list(errors.infos_to_regions(v)), scope='dartlint.mark.info', icon="Packages/Dart/gutter/dartlint-simple-info.png", flags=_flags) v.add_regions('dart.warnings', list(errors.warnings_to_regions(v)), scope='dartlint.mark.warning', icon="Packages/Dart/gutter/dartlint-simple-warning.png", flags=_flags) v.add_regions('dart.errors', list(errors.errors_to_regions(v)), scope='dartlint.mark.error', icon='Packages/Dart/gutter/dartlint-simple-error.png', flags=_flags) # TODO(guillermooo): Add a logger attrib to the OutputPanel. panel = OutputPanel('dart.analyzer') errors_pattern = r'^\w+\|\w+\|(.+)\|(\d+)\|(\d+)\|(.+)$' panel.set('result_file_regex', errors_pattern) panel.write('\n'.join(set(errors.to_compact_text())))
class EditorContext(object): write_lock = threading.Lock() search_id_lock = threading.Lock() # FIXME(guillermooo): This is utterly wrong. This needs to be a singleton. def __init__(self): self._search_id = None self.results_panel = None self._navigation = None self._errors = [] self._errors_index = -1 @property def search_id(self): with EditorContext.search_id_lock: return self._search_id @search_id.setter def search_id(self, value): with EditorContext.search_id_lock: if self._search_id == value: return self.results_panel = OutputPanel('dart.search.results') self.results_panel.set('result_file_regex', r'^\w+\s+-\s+(.*?):(\d+):(\d+)') self._search_id = value @search_id.deleter def search_id(self): with EditorContext.search_id_lock: self._search_id = None @property def navigation(self): with EditorContext.write_lock: return self._navigation @navigation.setter def navigation(self, value): # TODO(guillermooo): store this data by file with EditorContext.write_lock: self._navigation = value @property def errors(self): with EditorContext.write_lock: return self._errors @errors.setter def errors(self, values): with EditorContext.write_lock: self._errors_index = -1 self._errors = list(values) @property def errors_index(self): with EditorContext.write_lock: return self._errors_index def increment_error_index(self): with EditorContext.write_lock: if self._errors_index == len(self._errors) - 1: raise IndexError('end of errors list') self._errors_index += 1 def decrement_error_index(self): with EditorContext.write_lock: if self._errors_index == 0: raise IndexError('start of errors list') self._errors_index -= 1 def get_current_error(self): return self.errors[self.errors_index] def check_token(self, action, token): if action == 'search': if self.search_id is None: return return (token == self.search_id) def append_search_results(self, items): items = list(items) with EditorContext.search_id_lock: if not self.results_panel: return for item in items: self.results_panel.write(item.to_encoded_pos()) self.results_panel.show()
def show_errors(errors): '''Show errors in the ui. @errors An instance of `ErrorInfoCollection`. ''' v = sublime.active_window().active_view() # TODO(guillermooo): Use tokens to identify requests:file. # todo (pp): notifications don't have id; process all if os.path.realpath(errors.file) != os.path.realpath(v.file_name()): _logger.debug('different view active - aborting') return analysis_errors = list(errors.errors) if analysis_errors == 0: clear_ui() return infos = [ ae for ae in analysis_errors if (ae.severity == AnalysisErrorSeverity.INFO) ] warns = [ ae for ae in analysis_errors if (ae.severity == AnalysisErrorSeverity.WARNING) ] erros = [ ae for ae in analysis_errors if (ae.severity == AnalysisErrorSeverity.ERROR) ] def error_to_region(view, error): '''Converts location data to region data. ''' loc = error.location pt = view.text_point(loc.startLine - 1, loc.startColumn - 1) return sublime.Region(pt, pt + loc.length) info_regs = [error_to_region(v, item) for item in infos] warn_regs = [error_to_region(v, item) for item in warns] errs_regs = [error_to_region(v, item) for item in erros] _logger.debug('displaying errors to the user') v.add_regions(DAS_UI_REGIONS_INFOS, info_regs, scope=DAS_SCOPE_INFO, icon="Packages/Dart/gutter/dartlint-simple-info.png", flags=_flags) v.add_regions(DAS_UI_REGIONS_WARNINGS, warn_regs, scope=DAS_SCOPE_WARNING, icon="Packages/Dart/gutter/dartlint-simple-warning.png", flags=_flags) v.add_regions(DAS_UI_REGIONS_ERRORS, errs_regs, scope=DAS_SCOPE_ERROR, icon='Packages/Dart/gutter/dartlint-simple-error.png', flags=_flags) def to_compact_text(error): return ("{error.severity}|{error.type}|{loc.file}|" "{loc.startLine}|{loc.startColumn}|{error.message}").format( error=error, loc=error.location) info_patts = [to_compact_text(item) for item in infos] warn_patts = [to_compact_text(item) for item in warns] errs_patts = [to_compact_text(item) for item in erros] all_errs = set(errs_patts + warn_patts + info_patts) panel = OutputPanel('dart.analyzer') errors_pattern = r'^\w+\|\w+\|(.+)\|(\d+)\|(\d+)\|(.+)$' panel.set('result_file_regex', errors_pattern) panel.write('\n'.join(all_errs))
def __call__(self, errors): '''Show errors in the ui. @errors An instance of `ErrorInfoCollection`. ''' view = get_active_view() # TODO(guillermooo): Use tokens to identify requests:file. if not self.compare_paths(errors.file, view.file_name()): _logger.debug('different view active - aborting') return panel = OutputPanel('dart.analyzer') analysis_errors = list(errors.errors) infos, warns, erros = self.group(analysis_errors) if len(infos + warns + erros) == 0: clear_ui() panel.hide() return info_regs = [self.error_to_region(view, item) for item in infos] warn_regs = [self.error_to_region(view, item) for item in warns] errs_regs = [self.error_to_region(view, item) for item in erros] _logger.debug('displaying errors to the user') self.add_regions(view, info_regs, warn_regs, errs_regs) all_sorted = sorted(infos + warns + erros, key=lambda x: x.location.offset) all_errs = (self.to_compact_text(item) for item in all_sorted) # TODO(guillermooo): abstract out the panel stuff into a DartErrorPanel class. panel = OutputPanel('dart.analyzer') # Tried to use .sublime-settings for this, but it won't work well. errors_pattern = r'^\w+\|\w+\|(.+)\|(\d+)\|(\d+)\|(.+)$' panel.set('result_file_regex', errors_pattern) all_errs = list(all_errs) # Overwrite any previous text in the panel. # We get errors sometimes when writing error lines here. panel.write('\n'.join(all_errs)) # TODO(guillermooo): remove this when .sublime-syntax has been fully # adopted. if sublime.version() >= '3084': panel.view.set_syntax_file( 'Packages/Dart/Support/Analyzer Output.sublime-syntax') else: panel.view.set_syntax_file( 'Packages/Dart/Support/Analyzer Output.tmLanguage') editor_context.errors = all_errs panel.show() try: view.show(view.sel()[0]) except IndexError: pass sublime.status_message("Dart: Errors found")
def run(self): """Runs forever checking for new linter results and displaying them to the user. """ while True: # Run at intervals. time.sleep(0.250) # We've got results for this buffer. Reset its version count so # the linting cycle can start again. # TODO(guillermooo): It's possible that we'll miss some edits (?). with g_edits_lock: DartLint.edits[self.view.buffer_id()] = 0 lines = self.get_data() if lines is None: continue # TODO(guillermooo): Compose a DartLintOutputPanel from a plain # OutputPanel to abstract all of this away. # Show errors in output panel and enable error navigation via F4. panel = OutputPanel('dart.analyzer') # Capture file name, rowcol and error message information. errors_pattern = r'^\w+\|\w+\|\w+\|(.+)\|(\d+)\|(\d+)\|\d+\|(.+)' panel.set('result_file_regex', errors_pattern) panel.write('\n'.join(lines)) panel.show() pattern = (r'^(?P<severity>\w+)\|(?P<type>\w+)\|(?P<code>\w+)\|' + r'(?P<file_name>.+)\|(?P<line>\d+)\|(?P<col>\d+)\|' + r'(?P<err_length>\d+)\|(?P<message>.+)') msg_pattern_machine = re.compile(pattern) # Collect data needed to generate error messages lint_data = [] lines_out = '' err_count = 0 culp_regions = {} for line in lines: line_out = '' line_data = {} line_groups = msg_pattern_machine.match(line) if line_groups is not None: if line_groups.group('file_name') != self.fileName: # output is for a different file continue line_out = '%s: %s on line %s, col %s: %s\n' % \ (line_groups.group('severity'), line_groups.group('code'), line_groups.group('line'), line_groups.group('col'), line_groups.group('message')) line_data['severity'] = line_groups.group('severity') line_data['col'] = line_groups.group('col') line_data['line'] = line_groups.group('line') line_data['msg'] = line_groups.group('message') line_data['code'] = line_groups.group('code') line_data['type'] = line_groups.group('type') line_data['err_length'] = line_groups.group('err_length') line_data['lint_out'] = line_out line_data['line_pt'] = self.view.text_point( int(line_data['line']) - 1, 0) line_data['point'] = self.view.text_point( int(line_data['line']) - 1, int(line_data['col'])) next_line = self.view.text_point(int(line_data['line']), 0) # Add a region (gutter mark and underline) if int(line_data['err_length']) > 0 and \ int(line_data['point']) + \ (int(line_data['err_length']) - 1) < next_line: # Set the error region line_data['culp_region'] = sublime.Region( int(line_data['point']) - 1, int(line_data['point']) + (int(line_data['err_length']) - 1)) else: # Set the line as the error region line_data['culp_region'] = self.view.line( line_data['line_pt']) # Add the region to the apropriate region collection if ('dartlint_' + line_data['severity']) not in \ culp_regions.keys(): culp_regions['dartlint_%s' % line_data['severity']] = [] culp_regions['dartlint_%s' % line_data['severity']].append( line_data['culp_region']) lines_out += line_out lint_data.append(line_data) err_count += 1 for reg_id in culp_regions.keys(): # set the scope name reg_list = culp_regions[reg_id] this_scope = 'dartlint.mark.warning' if reg_id.endswith('ERROR') is True: this_scope = 'dartlint.mark.error' if reg_id.endswith('INFO') is True: this_scope = 'dartlint.mark.info' # Seperate gutter and underline regions gutter_reg = [] for reg in reg_list: gutter_reg.append(self.view.line(reg.begin())) self.view.add_regions( reg_id + '_gutter', gutter_reg, # set this to this_scope for tinted gutter icons 'dartlint.mark.gutter', icon=GUTTER_Icon[reg_id], flags=SCOPES_Dartlint['dartlint.mark.gutter']['flags']) self.view.add_regions( reg_id, reg_list, this_scope, flags=SCOPES_Dartlint[this_scope]['flags']) # Set icon presidence? if lines_out is '': self.output = None print('No errors.') self.view.set_status('dartlint', 'Dartlint: No errors') else: # Sort list idx = 0 err_keys = [] for entry in lint_data: line_val = '{0:{fill}{align}16}'.format( entry['line'], fill='0', align='>') col_val = '{0:{fill}{align}16}'.format( entry['col'], fill='0', align='>') list_val = '%s-%s-%s' % (line_val, col_val, str(idx)) err_keys.append(list_val) idx += 1 new_err_list = [] err_keys.sort() for ek in err_keys: new_err_list.append(lint_data[int(ek.split('-')[2])]) self.output = new_err_list # Out to console print('\n' + lines_out)
class DartExecCommand(sublime_plugin.WindowCommand, ProcessListener): def run(self, cmd=None, shell_cmd=None, file_regex="", line_regex="", working_dir="", encoding="utf-8", env={}, quiet=False, kill=False, word_wrap=True, syntax="Packages/Text/Plain text.tmLanguage", preamble='', panel_name='dart.out', # Catches "path" and "shell" **kwargs): if kill: if hasattr(self, 'proc') and self.proc: self.proc.kill() self.proc = None self.append_string(None, "[Cancelled]") return # TODO(guillermooo): We cannot have multiple processes running at the # same time, or processes that use separate output panels. if not hasattr(self, 'out_panel'): # Try not to call get_output_panel until the regexes are assigned self.out_panel = OutputPanel(panel_name) # Default to the current files directory if no working directory was given if (not working_dir and self.window.active_view() and self.window.active_view().file_name()): working_dir = os.path.dirname( self.window.active_view().file_name()) self.out_panel.set("result_file_regex", file_regex) self.out_panel.set("result_line_regex", line_regex) self.out_panel.set("result_base_dir", working_dir) self.out_panel.set("word_wrap", word_wrap) self.out_panel.set("line_numbers", False) self.out_panel.set("gutter", False) self.out_panel.set("scroll_past_end", False) self.out_panel.view.assign_syntax(syntax) self.encoding = encoding self.quiet = quiet self.proc = None if not self.quiet: if shell_cmd: print("Running " + shell_cmd) else: print("Running " + " ".join(cmd)) sublime.status_message("Building") if preamble: self.append_string(self.proc, preamble) show_panel_on_build = sublime.load_settings( "Dart - Plugin Settings.sublime-settings").get("show_panel_on_build", True) if show_panel_on_build: self.out_panel.show() merged_env = env.copy() if self.window.active_view(): user_env = self.window.active_view().settings().get('build_env') if user_env: merged_env.update(user_env) # Change to the working dir, rather than spawning the process with it, # so that emitted working dir relative path names make sense if working_dir: os.chdir(working_dir) self.debug_text = "" if shell_cmd: self.debug_text += "[shell_cmd: " + shell_cmd + "]\n" else: self.debug_text += "[cmd: " + str(cmd) + "]\n" self.debug_text += "[dir: " + str(os.getcwd()) + "]\n" if "PATH" in merged_env: self.debug_text += "[path: " + str(merged_env["PATH"]) + "]" else: self.debug_text += "[path: " + str(os.environ["PATH"]) + "]" try: # Forward kwargs to AsyncProcess self.proc = AsyncProcess(cmd, shell_cmd, merged_env, self, **kwargs) except Exception as e: self.append_string(None, str(e) + "\n") self.append_string(None, self.debug_text + "\n") if not self.quiet: self.append_string(None, "[Finished]") def append_data(self, proc, data): if proc != self.proc: # a second call to exec has been made before the first one # finished, ignore it instead of intermingling the output. if proc: proc.kill() return try: str_ = data.decode(self.encoding) except UnicodeEncodeError: str_ = "[Decode error - output not " + self.encoding + "]\n" proc = None return # Normalize newlines, Sublime Text always uses a single \n separator # in memory. str_ = str_.replace('\r\n', '\n').replace('\r', '\n') self.out_panel.write(str_) def append_string(self, proc, str): self.append_data(proc, str.encode(self.encoding)) def finish(self, proc): if not self.quiet: elapsed = time.time() - proc.start_time exit_code = proc.exit_code() if (exit_code == 0) or (exit_code == None): self.append_string(proc, "[Finished in %.1fs]" % (elapsed)) else: self.append_string(proc, "[Finished in %.1fs with exit code %d]\n" % (elapsed, exit_code)) self.append_string(proc, self.debug_text) if proc != self.proc: return # XXX: What's this for? errs = self.out_panel.view.find_all_results() if len(errs) == 0: sublime.status_message("Build finished") else: sublime.status_message(("Build finished with %d errors") % len(errs)) def on_data(self, proc, data): after(0, functools.partial(self.append_data, proc, data)) def on_finished(self, proc): after(0, functools.partial(self.finish, proc))
def show_errors(errors): '''Show errors in the ui. @errors An instance of `ErrorInfoCollection`. ''' v = sublime.active_window().active_view() # TODO(guillermooo): Use tokens to identify requests:file. # todo (pp): notifications don't have id; process all if os.path.realpath(errors.file) != os.path.realpath(v.file_name()): _logger.debug('different view active - aborting') return analysis_errs = list(errors.errors) if analysis_errs == 0: clear_ui() return infos = [ae for ae in analysis_errs if (ae.severity == AnalysisErrorSeverity.INFO)] warns = [ae for ae in analysis_errs if (ae.severity == AnalysisErrorSeverity.WARNING)] erros = [ae for ae in analysis_errs if (ae.severity == AnalysisErrorSeverity.ERROR)] def error_to_region(view, error): '''Converts location data to region data. ''' loc = Location(error.location) pt = view.text_point(loc.startLine - 1, loc.startColumn - 1) return sublime.Region(pt, pt + loc.length) info_regs = [error_to_region(v, item) for item in infos] warn_regs = [error_to_region(v, item) for item in warns] errs_regs = [error_to_region(v, item) for item in erros] _logger.debug('displaying errors to the user') v.add_regions('dart.infos', info_regs, scope='dartlint.mark.info', icon="Packages/Dart/gutter/dartlint-simple-info.png", flags=_flags) v.add_regions('dart.warnings', warn_regs, scope='dartlint.mark.warning', icon="Packages/Dart/gutter/dartlint-simple-warning.png", flags=_flags) v.add_regions('dart.errors', errs_regs, scope='dartlint.mark.error', icon='Packages/Dart/gutter/dartlint-simple-error.png', flags=_flags) def to_compact_text(error): return ("{error.severity}|{error.type}|{loc.file}|" "{loc.startLine}|{loc.startColumn}|{error.message}").format( error=error, loc=Location(error.location)) info_patts = [to_compact_text(item) for item in infos] warn_patts = [to_compact_text(item) for item in warns] errs_patts = [to_compact_text(item) for item in erros] all_errs = set(errs_patts + warn_patts + info_patts) panel = OutputPanel('dart.analyzer') errors_pattern = r'^\w+\|\w+\|(.+)\|(\d+)\|(\d+)\|(.+)$' panel.set('result_file_regex', errors_pattern) panel.write('\n'.join(all_errs))
class DartExecCommand(sublime_plugin.WindowCommand, ProcessListener): def run( self, cmd=None, shell_cmd=None, file_regex="", line_regex="", working_dir="", encoding="utf-8", env={}, quiet=False, kill=False, word_wrap=True, syntax="Packages/Text/Plain text.tmLanguage", preamble='', panel_name='dart.out', # Catches "path" and "shell" **kwargs): if kill: if hasattr(self, 'proc') and self.proc: self.proc.kill() self.proc = None self.append_string(None, "[Cancelled]") return # TODO(guillermooo): We cannot have multiple processes running at the # same time, or processes that use separate output panels. if not hasattr(self, 'out_panel'): # Try not to call get_output_panel until the regexes are assigned self.out_panel = OutputPanel(panel_name) # Default to the current files directory if no working directory was given if (not working_dir and self.window.active_view() and self.window.active_view().file_name()): working_dir = os.path.dirname( self.window.active_view().file_name()) self.out_panel.set("result_file_regex", file_regex) self.out_panel.set("result_line_regex", line_regex) self.out_panel.set("result_base_dir", working_dir) self.out_panel.set("word_wrap", word_wrap) self.out_panel.set("line_numbers", False) self.out_panel.set("gutter", False) self.out_panel.set("scroll_past_end", False) self.out_panel.view.assign_syntax(syntax) self.encoding = encoding self.quiet = quiet self.proc = None if not self.quiet: if shell_cmd: print("Running " + shell_cmd) else: print("Running " + " ".join(cmd)) sublime.status_message("Building") if preamble: self.append_string(self.proc, preamble) show_panel_on_build = sublime.load_settings( "Dart - Plugin Settings.sublime-settings").get( "show_panel_on_build", True) if show_panel_on_build: self.out_panel.show() merged_env = env.copy() if self.window.active_view(): user_env = self.window.active_view().settings().get('build_env') if user_env: merged_env.update(user_env) # Change to the working dir, rather than spawning the process with it, # so that emitted working dir relative path names make sense if working_dir: os.chdir(working_dir) self.debug_text = "" if shell_cmd: self.debug_text += "[shell_cmd: " + shell_cmd + "]\n" else: self.debug_text += "[cmd: " + str(cmd) + "]\n" self.debug_text += "[dir: " + str(os.getcwd()) + "]\n" if "PATH" in merged_env: self.debug_text += "[path: " + str(merged_env["PATH"]) + "]" else: self.debug_text += "[path: " + str(os.environ["PATH"]) + "]" try: # Forward kwargs to AsyncProcess self.proc = AsyncProcess(cmd, shell_cmd, merged_env, self, **kwargs) except Exception as e: self.append_string(None, str(e) + "\n") self.append_string(None, self.debug_text + "\n") if not self.quiet: self.append_string(None, "[Finished]") def append_data(self, proc, data): if proc != self.proc: # a second call to exec has been made before the first one # finished, ignore it instead of intermingling the output. if proc: proc.kill() return try: str_ = data.decode(self.encoding) except UnicodeEncodeError: str_ = "[Decode error - output not " + self.encoding + "]\n" proc = None return # Normalize newlines, Sublime Text always uses a single \n separator # in memory. str_ = str_.replace('\r\n', '\n').replace('\r', '\n') self.out_panel.write(str_) def append_string(self, proc, str): self.append_data(proc, str.encode(self.encoding)) def finish(self, proc): if not self.quiet: elapsed = time.time() - proc.start_time exit_code = proc.exit_code() if (exit_code == 0) or (exit_code == None): self.append_string(proc, "[Finished in %.1fs]" % (elapsed)) else: self.append_string( proc, "[Finished in %.1fs with exit code %d]\n" % (elapsed, exit_code)) self.append_string(proc, self.debug_text) if proc != self.proc: return # XXX: What's this for? errs = self.out_panel.view.find_all_results() if len(errs) == 0: sublime.status_message("Build finished") else: sublime.status_message( ("Build finished with %d errors") % len(errs)) def on_data(self, proc, data): after(0, functools.partial(self.append_data, proc, data)) def on_finished(self, proc): after(0, functools.partial(self.finish, proc))
def show_errors(errors): '''Show errors in the ui. @errors An instance of `ErrorInfoCollection`. ''' v = sublime.active_window().active_view() # TODO(guillermooo): Use tokens to identify requests:file. # todo (pp): notifications don't have id; process all if os.path.realpath(errors.file) != os.path.realpath(v.file_name()): _logger.debug('different view active - aborting') return analysis_errors = list(errors.errors) if analysis_errors == 0: clear_ui() return infos = [ ae for ae in analysis_errors if (ae.severity == AnalysisErrorSeverity.INFO) and ( ae.type != AnalysisErrorType.TODO) ] warns = [ ae for ae in analysis_errors if (ae.severity == AnalysisErrorSeverity.WARNING) ] erros = [ ae for ae in analysis_errors if (ae.severity == AnalysisErrorSeverity.ERROR) ] def error_to_region(view, error): '''Converts location data to region data. ''' pass loc = error.location pt = view.text_point(loc.startLine - 1, loc.startColumn - 1) return sublime.Region(pt, pt + loc.length) info_regs = [error_to_region(v, item) for item in infos] warn_regs = [error_to_region(v, item) for item in warns] errs_regs = [error_to_region(v, item) for item in erros] _logger.debug('displaying errors to the user') v.add_regions(DAS_UI_REGIONS_INFOS, info_regs, scope=DAS_SCOPE_INFO, icon="Packages/Dart/gutter/dartlint-simple-info.png", flags=_flags) v.add_regions(DAS_UI_REGIONS_WARNINGS, warn_regs, scope=DAS_SCOPE_WARNING, icon="Packages/Dart/gutter/dartlint-simple-warning.png", flags=_flags) v.add_regions(DAS_UI_REGIONS_ERRORS, errs_regs, scope=DAS_SCOPE_ERROR, icon='Packages/Dart/gutter/dartlint-simple-error.png', flags=_flags) def to_compact_text(error): return ("{error.severity}|{error.type}|{loc.file}|" "{loc.startLine}|{loc.startColumn}|{error.message}").format( error=error, loc=error.location) info_patts = [to_compact_text(item) for item in infos] warn_patts = [to_compact_text(item) for item in warns] errs_patts = [to_compact_text(item) for item in erros] all_errs = set(errs_patts + warn_patts + info_patts) panel = OutputPanel('dart.analyzer') if not all_errs: editor_context.errors = [] panel.hide() return errors_pattern = r'^\w+\|\w+\|(.+)\|(\d+)\|(\d+)\|(.+)$' panel.set('result_file_regex', errors_pattern) # This will overwrite any previous text. panel.write('\n' + '\n'.join(all_errs)) # FIXME: It appears that if ST dev find a .sublime-syntax and a .tmLanguage # file, it will load # the first one. But how do we refer to the file then? if sublime.version() >= '3084': panel.view.set_syntax_file( 'Packages/Dart/Support/Analyzer Output.sublime-syntax') else: panel.view.set_syntax_file( 'Packages/Dart/Support/Analyzer Output.tmLanguage') panel.view.settings().set('rulers', []) panel.show() sublime.status_message("Dart: Errors found") editor_context.errors = all_errs
def run(self): """Runs forever checking for new linter results and displaying them to the user. """ while True: # Run at intervals. time.sleep(0.250) # We've got results for this buffer. Reset its version count so # the linting cycle can start again. # TODO(guillermooo): It's possible that we'll miss some edits (?). with g_edits_lock: DartLint.edits[self.view.buffer_id()] = 0 lines = self.get_data() if lines is None: continue # TODO(guillermooo): Compose a DartLintOutputPanel from a plain # OutputPanel to abstract all of this away. # Show errors in output panel and enable error navigation via F4. panel = OutputPanel('dart.analyzer') # Capture file name, rowcol and error message information. errors_pattern = r'^\w+\|\w+\|\w+\|(.+)\|(\d+)\|(\d+)\|\d+\|(.+)' panel.set('result_file_regex', errors_pattern) panel.write('\n'.join(lines)) panel.show() pattern = (r'^(?P<severity>\w+)\|(?P<type>\w+)\|(?P<code>\w+)\|' + r'(?P<file_name>.+)\|(?P<line>\d+)\|(?P<col>\d+)\|' + r'(?P<err_length>\d+)\|(?P<message>.+)') msg_pattern_machine = re.compile(pattern) # Collect data needed to generate error messages lint_data = [] lines_out = '' err_count = 0 culp_regions = {} for line in lines: line_out = '' line_data = {} line_groups = msg_pattern_machine.match(line) if line_groups is not None: if line_groups.group('file_name') != self.fileName: # output is for a different file continue line_out = '%s: %s on line %s, col %s: %s\n' % \ (line_groups.group('severity'), line_groups.group('code'), line_groups.group('line'), line_groups.group('col'), line_groups.group('message')) line_data['severity'] = line_groups.group('severity') line_data['col'] = line_groups.group('col') line_data['line'] = line_groups.group('line') line_data['msg'] = line_groups.group('message') line_data['code'] = line_groups.group('code') line_data['type'] = line_groups.group('type') line_data['err_length'] = line_groups.group('err_length') line_data['lint_out'] = line_out line_data['line_pt'] = self.view.text_point( int(line_data['line']) - 1, 0) line_data['point'] = self.view.text_point( int(line_data['line']) - 1, int(line_data['col'])) next_line = self.view.text_point(int(line_data['line']), 0) # Add a region (gutter mark and underline) if int(line_data['err_length']) > 0 and \ int(line_data['point']) + \ (int(line_data['err_length']) - 1) < next_line: # Set the error region line_data['culp_region'] = sublime.Region( int(line_data['point']) - 1, int(line_data['point']) + (int(line_data['err_length']) - 1)) else: # Set the line as the error region line_data['culp_region'] = self.view.line( line_data['line_pt']) # Add the region to the apropriate region collection if ('dartlint_' + line_data['severity']) not in \ culp_regions.keys(): culp_regions['dartlint_%s' % line_data['severity']] = [] culp_regions['dartlint_%s' % line_data['severity']].append( line_data['culp_region']) lines_out += line_out lint_data.append(line_data) err_count += 1 for reg_id in culp_regions.keys(): # set the scope name reg_list = culp_regions[reg_id] this_scope = 'dartlint.mark.warning' if reg_id.endswith('ERROR') is True: this_scope = 'dartlint.mark.error' if reg_id.endswith('INFO') is True: this_scope = 'dartlint.mark.info' # Seperate gutter and underline regions gutter_reg = [] for reg in reg_list: gutter_reg.append(self.view.line(reg.begin())) self.view.add_regions( reg_id + '_gutter', gutter_reg, # set this to this_scope for tinted gutter icons 'dartlint.mark.gutter', icon=GUTTER_Icon[reg_id], flags=SCOPES_Dartlint['dartlint.mark.gutter']['flags']) self.view.add_regions( reg_id, reg_list, this_scope, flags=SCOPES_Dartlint[this_scope]['flags']) # Set icon presidence? if lines_out is '': self.output = None print('No errors.') self.view.set_status('dartlint', 'Dartlint: No errors') else: # Sort list idx = 0 err_keys = [] for entry in lint_data: line_val = '{0:{fill}{align}16}'.format(entry['line'], fill='0', align='>') col_val = '{0:{fill}{align}16}'.format(entry['col'], fill='0', align='>') list_val = '%s-%s-%s' % (line_val, col_val, str(idx)) err_keys.append(list_val) idx += 1 new_err_list = [] err_keys.sort() for ek in err_keys: new_err_list.append(lint_data[int(ek.split('-')[2])]) self.output = new_err_list # Out to console print('\n' + lines_out)
def __call__(self, errors): '''Show errors in the ui. @errors An instance of `ErrorInfoCollection`. ''' view = get_active_view() # TODO(guillermooo): Use tokens to identify requests:file. if not self.compare_paths(errors.file, view.file_name()): _logger.debug('different view active - aborting') return panel = OutputPanel('dart.errors') analysis_errors = list(errors.errors) infos, warns, erros = self.group(analysis_errors) if len(infos + warns + erros) == 0: clear_ui() panel.hide() return info_regs = [self.error_to_region(view, item) for item in infos] warn_regs = [self.error_to_region(view, item) for item in warns] errs_regs = [self.error_to_region(view, item) for item in erros] _logger.debug('displaying errors to the user') self.add_regions(view, info_regs, warn_regs, errs_regs) all_sorted = sorted(infos + warns + erros, key=lambda x: x.location.offset) all_errs = (self.to_compact_text(item) for item in all_sorted) # TODO(guillermooo): abstract out the panel stuff into a DartErrorPanel class. panel = OutputPanel('dart.errors') # Tried to use .sublime-settings for this, but it won't work well. errors_pattern = r'^\w+\|\w+\|(.+)\|(\d+)\|(\d+)\|(.+)$' panel.set('result_file_regex', errors_pattern) all_errs = list(all_errs) # Overwrite any previous text in the panel. # We get errors sometimes when writing error lines here. panel.write('\n'.join(all_errs)) # TODO(guillermooo): remove this when .sublime-syntax has been fully # adopted. if sublime.version() >= '3084': panel.view.set_syntax_file('Packages/Dart/Support/Analyzer Output.sublime-syntax') else: panel.view.set_syntax_file('Packages/Dart/Support/Analyzer Output.tmLanguage') editor_context.errors = all_errs # To show the panel, use the Command Palette or the key binding. # panel.show() try: view.show(view.sel()[0]) except IndexError: pass sublime.status_message("Dart: Errors found")
def show_errors(errors): '''Show errors in the ui. @errors An instance of `ErrorInfoCollection`. ''' v = sublime.active_window().active_view() # TODO(guillermooo): Use tokens to identify requests:file. # todo (pp): notifications don't have id; process all if os.path.realpath(errors.file) != os.path.realpath(v.file_name()): _logger.debug('different view active - aborting') return analysis_errors = list(errors.errors) if analysis_errors == 0: clear_ui() return infos = [ae for ae in analysis_errors if (ae.severity == AnalysisErrorSeverity.INFO) and (ae.type != AnalysisErrorType.TODO)] warns = [ae for ae in analysis_errors if (ae.severity == AnalysisErrorSeverity.WARNING)] erros = [ae for ae in analysis_errors if (ae.severity == AnalysisErrorSeverity.ERROR)] def error_to_region(view, error): '''Converts location data to region data. ''' pass loc = error.location pt = view.text_point(loc.startLine - 1, loc.startColumn - 1) return sublime.Region(pt, pt + loc.length) info_regs = [error_to_region(v, item) for item in infos] warn_regs = [error_to_region(v, item) for item in warns] errs_regs = [error_to_region(v, item) for item in erros] _logger.debug('displaying errors to the user') v.add_regions(DAS_UI_REGIONS_INFOS, info_regs, scope=DAS_SCOPE_INFO, icon="Packages/Dart/gutter/dartlint-simple-info.png", flags=_flags) v.add_regions(DAS_UI_REGIONS_WARNINGS, warn_regs, scope=DAS_SCOPE_WARNING, icon="Packages/Dart/gutter/dartlint-simple-warning.png", flags=_flags) v.add_regions(DAS_UI_REGIONS_ERRORS, errs_regs, scope=DAS_SCOPE_ERROR, icon='Packages/Dart/gutter/dartlint-simple-error.png', flags=_flags) def to_compact_text(error): return ("{error.severity}|{error.type}|{loc.file}|" "{loc.startLine}|{loc.startColumn}|{error.message}").format( error=error, loc=error.location) info_patts = [to_compact_text(item) for item in infos] warn_patts = [to_compact_text(item) for item in warns] errs_patts = [to_compact_text(item) for item in erros] all_errs = set(errs_patts + warn_patts + info_patts) panel = OutputPanel('dart.analyzer') if not all_errs: editor_context.errors = [] panel.hide() return errors_pattern = r'^\w+\|\w+\|(.+)\|(\d+)\|(\d+)\|(.+)$' panel.set('result_file_regex', errors_pattern) # This will overwrite any previous text. panel.write('\n' + '\n'.join(all_errs)) panel.view.set_syntax_file('Packages/Dart/Support/Analyzer Output.sublime-syntax') panel.view.settings().set('rulers', []) panel.show() sublime.status_message("Dart: Errors found") editor_context.errors = all_errs