def syntax_testing(self, stream, package): try: syntaxes = sublime.find_resources("*.sublime-syntax") if package != "__all__": syntaxes = [ s for s in syntaxes if s.startswith("Packages/%s/" % package) ] # remove UnitTesting syntax_tests syntaxes = [ s for s in syntaxes if not s.startswith("Packages/UnitTesting/") ] if not syntaxes: raise RuntimeError("No sublime-syntax files found in %s!" % package) total_errors = 0 total_failed_syntaxes = 0 for syntax in syntaxes: results = sublime_api.incompatible_syntax_patterns(syntax) for location, _, message in results: stream.write("%s:%d:%d: %s\n" % (syntax, location[0] + 1, location[0] + location[1], message)) if results: total_errors += len(results) total_failed_syntaxes += 1 if total_errors: stream.write( "FAILED: %d errors in %d of %d syntaxes\n" % (total_errors, total_failed_syntaxes, len(syntaxes))) else: stream.write("Success: %d syntaxes passed\n" % (len(syntaxes), )) stream.write("OK\n") except Exception as e: if not stream.closed: stream.write("ERROR: %s\n" % e) stream.write("\n") stream.write(DONE_MESSAGE) stream.close()
def syntax_testing(self, stream, package): try: syntaxes = sublime.find_resources("*.sublime-syntax") if package != "__all__": syntaxes = [s for s in syntaxes if s.startswith("Packages/%s/" % package)] # remove UnitTesting syntax_tests syntaxes = [s for s in syntaxes if not s.startswith("Packages/UnitTesting/")] if not syntaxes: raise RuntimeError("No sublime-syntax files found in %s!" % package) total_errors = 0 total_failed_syntaxes = 0 for syntax in syntaxes: results = sublime_api.incompatible_syntax_patterns(syntax) for location, _, message in results: stream.write("%s:%d:%d: %s\n" % (syntax, location[0] + 1, location[0] + location[1], message)) if results: total_errors += len(results) total_failed_syntaxes += 1 if total_errors: stream.write("FAILED: %d errors in %d of %d syntaxes\n" % ( total_errors, total_failed_syntaxes, len(syntaxes))) else: stream.write("Success: %d syntaxes passed\n" % (len(syntaxes),)) stream.write("OK\n") except Exception as e: if not stream.closed: stream.write("ERROR: %s\n" % e) stream.write("\n") stream.write(DONE_MESSAGE) stream.close()
def run(self, **kwargs): view = self.window.active_view() if not view or not view.file_name().endswith('.sublime-syntax'): sublime.error_message( 'Syntax compatibility tests can only be run when a ' '.sublime-syntax file is open') return if not hasattr(self, 'output_view'): # Try not to call get_output_panel until the regexes are assigned self.output_view = self.window.create_output_panel('exec') settings = self.output_view.settings() settings.set('result_file_regex', PACKAGES_FILE_REGEX) settings.set('result_base_dir', sublime.packages_path()) settings.set('word_wrap', True) settings.set('line_numbers', False) settings.set('gutter', False) settings.set('scroll_past_end', False) # Call create_output_panel a second time after assigning the above # settings, so that it'll be picked up as a result buffer self.window.create_output_panel('exec') relative_path = package_relative_path(view) if not relative_path: return show_panel_on_build(self.window) patterns = sublime_api.incompatible_syntax_patterns(relative_path) num = len(patterns) if num > 0: line_pattern = '{}:{}:{}: {}\n' for pattern in sorted(patterns, key=lambda p: p[0]): value_line, value_col = pattern[0] regex_line, regex_col = pattern[1] value_begin = view.text_point(value_line, value_col) next_char = view.substr(value_begin) while next_char == ' ': value_col += 1 value_begin = view.text_point(value_line, value_col) next_char = view.substr(value_begin) line = value_line + regex_line col = value_col + regex_col # Reconstruct the file cursor position by consuming YAML # string encoding # Quoted strings if next_char in {'\'', '"'}: col += 1 chunk_begin = value_begin + 1 chunk_end = chunk_begin + regex_col escaped_quotes = self.count_escapes(next_char, view, chunk_begin, chunk_end) while escaped_quotes: col += escaped_quotes chunk_begin = chunk_end chunk_end += escaped_quotes escaped_quotes = self.count_escapes(next_char, view, chunk_begin, chunk_end) # Block strings elif next_char == '|': line += 1 # Figure out the negative indent of the first line value_point = view.text_point(value_line + 1, 0) value_line = view.substr(view.line(value_point)) indent_size = len(value_line) - len(value_line.lstrip()) col = indent_size + regex_col # Folded strings elif next_char == '>': def get_line(view, line_num): point = view.text_point(line_num, 0) return view.substr(view.line(point)) line += 1 raw_line = get_line(view, line) stripped_line = raw_line.lstrip() stripped_len = len(stripped_line) indent_size = len(raw_line) - stripped_len consumed = 0 while consumed + stripped_len + 1 <= regex_col: consumed += stripped_len + 1 line += 1 raw_line = get_line(view, line) stripped_line = raw_line.lstrip() stripped_len = len(stripped_line) indent_size = len(raw_line) - stripped_len col = indent_size + (regex_col - consumed) append(self.output_view, line_pattern.format(relative_path, line + 1, col + 1, pattern[2])) message = 'FAILED: {} pattern{} in "{}" are incompatible with the new regex engine\n' s = 's' if num > 1 else '' params = (num, s, relative_path) else: message = 'Success: all patterns in "{}" are compatible with the new regex engine\n' params = (relative_path,) append(self.output_view, message.format(*params)) append(self.output_view, '[Finished]')