#!/usr/bin/env python from __future__ import absolute_import from __future__ import print_function import optparse import os import sys import subprocess from six.moves import filter from six.moves import map from six.moves import range try: import lister from typing import cast, Callable, Dict, Iterable, List except ImportError as e: print("ImportError: {}".format(e)) print("You need to run the Zulip linters inside a Zulip dev environment.") print("If you are using Vagrant, you can `vagrant ssh` to enter the Vagrant guest.") sys.exit(1) class Record(object): def __init__(self, func): # type: (Callable[[str], None]) -> None self.depth = 0 self.i = 0 self.line = 1 self.col = 1 self.matcher = func def validate(fn, check_indent=True): # type: (str, bool) -> None text = open(fn).read() def NoStartTag(end_tag): # type: (str) -> None raise Exception(''' No start tag fn: %s end tag: %s line %d, col %d ''' % (fn, end_tag, state.line, state.col)) def start_tag_matcher(s, start_tag): # type: (str, str) -> None start_line = state.line start_col = state.col state.depth += 1 old_matcher = state.matcher def f(end_tag): # type: (str) -> None problem = None if start_tag != end_tag: problem = 'Mismatched tag.' elif check_indent and state.line > start_line + 1 and state.col != start_col: problem = 'Bad indentation.' if problem: raise Exception(''' fn: %s %s start: %s line %d, col %d end tag: %s line %d, col %d ''' % (fn, problem, s, start_line, start_col, end_tag, state.line, state.col)) state.matcher = old_matcher state.depth -= 1 state.matcher = f state = Record(NoStartTag) def advance(n): # type: (int) -> None for _ in range(n): state.i += 1 if state.i >= 0 and text[state.i - 1] == '\n': state.line += 1 state.col = 1 else: state.col += 1 def looking_at(s): # type: (str) -> bool return text[state.i:state.i+len(s)] == s def looking_at_html_start(): # type: () -> bool return looking_at("<") and not looking_at(" bool return looking_at(" bool return looking_at("{{#") or looking_at("{{^") def looking_at_handlebars_end(): # type: () -> bool return looking_at("{{/") def looking_at_django_start(): # type: () -> bool return looking_at("{% ") and not looking_at("{% end") def looking_at_django_end(): # type: () -> bool return looking_at("{% end") def special_html_tag(s, tag): # type: (str, str) -> bool return (s.startswith('