From 96e12c8a274ea3e08648116f3fb052e3de005560 Mon Sep 17 00:00:00 2001 From: Matthew Sotoudeh Date: Mon, 13 Apr 2020 08:40:01 -0700 Subject: Initial code release --- patches/latexrun-force-colors | 28 ++++++++++++++++ patches/latexrun-pull-21 | 76 +++++++++++++++++++++++++++++++++++++++++++ patches/latexrun-pull-47 | 22 +++++++++++++ patches/latexrun-pull-61 | 18 ++++++++++ patches/latexrun-pull-62 | 31 ++++++++++++++++++ 5 files changed, 175 insertions(+) create mode 100644 patches/latexrun-force-colors create mode 100644 patches/latexrun-pull-21 create mode 100644 patches/latexrun-pull-47 create mode 100644 patches/latexrun-pull-61 create mode 100644 patches/latexrun-pull-62 (limited to 'patches') diff --git a/patches/latexrun-force-colors b/patches/latexrun-force-colors new file mode 100644 index 0000000..829a1fe --- /dev/null +++ b/patches/latexrun-force-colors @@ -0,0 +1,28 @@ +--- latexrun ++++ latexrun +@@ -416,7 +416,24 @@ class _Terminfo: + else: + s = self.__ensure(cap) + sys.stdout.buffer.write(s) +-terminfo = _Terminfo() ++ ++class _BatchColorTerminfo: ++ def has(self, *caps): ++ for cap in caps: ++ if cap not in ['bold', 'setaf', 'sgr0']: ++ return False ++ return True ++ ++ def send(self, *caps): ++ sys.stdout.flush() ++ for cap in caps: ++ if cap == 'bold': ++ sys.stdout.buffer.write(b'\x1b[1m') ++ elif isinstance(cap, tuple) and cap[0] == 'setaf': ++ sys.stdout.buffer.write(b'\x1b[%dm' % (cap[1] + 30)) ++ elif cap == 'sgr0': ++ sys.stdout.buffer.write(b'\x1b[m') ++terminfo = _BatchColorTerminfo() + + class Progress: + _enabled = None diff --git a/patches/latexrun-pull-21 b/patches/latexrun-pull-21 new file mode 100644 index 0000000..0b9c5b7 --- /dev/null +++ b/patches/latexrun-pull-21 @@ -0,0 +1,76 @@ +--- latexrun ++++ latexrun +@@ -85,7 +85,7 @@ def main(): + action=ArgParserWarnAction, dest='nowarns', default=set(['underfull']), + help='Enable/disable warning from CLASS, which can be any package name, ' + 'LaTeX warning class (e.g., font), bad box type ' +- '(underfull, overfull, loose, tight), or "all"') ++ '(underfull, overfull, loose, tight), strict parsing (strict-parse), or "all"') + arg_parser.add_argument( + '-O', metavar='DIR', dest='obj_dir', default='latex.out', + help='Directory for intermediate files and control database ' +@@ -236,6 +236,21 @@ def mkdir_p(path): + pass + else: raise + ++def nested_parenthesis_end(string, opening, closing, lax_checking=False): ++ """Return index where closing character corresponds to opening character""" ++ stack = [] ++ for i, c in enumerate(string): ++ if c in opening: ++ stack.append(c) ++ elif c in closing and stack: ++ start_ch = stack.pop() ++ if not lax_checking and opening.index(start_ch) != closing.index(c): ++ # Mismatch, e.g. expected ')', found '}' ++ return -1 ++ if not stack: ++ return i ++ return -1 ++ + class DB: + """A latexrun control database.""" + +@@ -1288,16 +1303,18 @@ class LaTeXFilter: + self.__file_stack.pop() + else: + self.__message('warning', None, +- "extra `)' in log; file names may be wrong ") ++ "extra `)' in log; file names may be wrong") + elif ch == '{': + # TeX uses this for various things we want to ignore, like + # file names and print_mark. Consume up to the '}' +- epos = self.__data.find('}', self.__pos) +- if epos != -1: +- self.__pos = epos + 1 +- else: ++ lax_checking = "strict-parse" in self.__suppress ++ epos = nested_parenthesis_end(self.__data[self.__pos-1:], '{[(', '}])', ++ lax_checking=lax_checking) ++ if epos == -1: + self.__message('warning', None, + "unbalanced `{' in log; file names may be wrong") ++ else: ++ self.__pos += epos + elif ch == '}': + self.__message('warning', None, + "extra `}' in log; file names may be wrong") +@@ -1439,14 +1456,15 @@ class LaTeXFilter: + return + # Back up to the end of the known message text + self.__pos = origpos + m.end() +- if self.__lookingat('\n'): ++ if self.__lookingatre(r'\s*\n'): + # We have a newline, so consume it and look for the + # offending text. + self.__pos += 1 + # If there is offending text, it will start with a font + # name, which will start with a \. +- if 'hbox' in msg and self.__lookingat('\\'): +- self.__consume_line(unwrap=True) ++ if 'hbox' in msg and self.__lookingatre(r'(\[\]\s?)*\s*\\'): ++ consumed = self.__consume_line(unwrap=True) ++ if self.TRACE: print('consuming `<{}>\''.format(consumed)) + msg = self.__simplify_message(msg) + ' (page {})'.format(self.__pageno) + cls = msg.split(None, 1)[0].lower() + self.__message('warning', lineno, msg, cls=cls) diff --git a/patches/latexrun-pull-47 b/patches/latexrun-pull-47 new file mode 100644 index 0000000..18afb05 --- /dev/null +++ b/patches/latexrun-pull-47 @@ -0,0 +1,22 @@ +--- latexrun ++++ latexrun +@@ -861,15 +861,16 @@ class LaTeX(Task): + pages of output. + """ + jobname = outname = None +- for m in re.finditer(r'^Transcript written on "?(.*)\.log"?\.$', stdout, ++ for m in re.finditer(r'^Transcript written on "?(.*?)\.log"?\.$', stdout, + re.MULTILINE | re.DOTALL): + jobname = m.group(1).replace('\n', '') + if jobname is None: + print(stdout, file=sys.stderr) + raise TaskError('failed to extract job name from latex log') +- for m in re.finditer(r'^Output written on "?(.*\.[^ ."]+)"? \([0-9]+ page', ++ for m in re.finditer(r'^Output written on "?(.*?\.[^ ."]+)"? \([0-9]+ (page)?', + stdout, re.MULTILINE | re.DOTALL): +- outname = m.group(1).replace('\n', '') ++ if m.group(2) == "page": ++ outname = m.group(1).replace('\n', '') + if outname is None and not \ + re.search(r'^No pages of output\.$|^! Emergency stop\.$' + r'|^! ==> Fatal error occurred, no output PDF file produced!$', diff --git a/patches/latexrun-pull-61 b/patches/latexrun-pull-61 new file mode 100644 index 0000000..a784377 --- /dev/null +++ b/patches/latexrun-pull-61 @@ -0,0 +1,18 @@ +--- latexrun ++++ latexrun +@@ -457,10 +457,12 @@ class Message(collections.namedtuple( + 'Message', 'typ filename lineno msg')): + def emit(self): + if self.filename: +- if self.filename.startswith('./'): +- finfo = self.filename[2:] +- else: ++ cwd = os.getcwd() ++ if (os.path.isabs(self.filename) and ++ os.path.commonpath([self.filename, cwd]) != cwd): + finfo = self.filename ++ else: ++ finfo = os.path.relpath(self.filename, cwd) + else: + finfo = '' + if self.lineno is not None: diff --git a/patches/latexrun-pull-62 b/patches/latexrun-pull-62 new file mode 100644 index 0000000..6c42f86 --- /dev/null +++ b/patches/latexrun-pull-62 @@ -0,0 +1,31 @@ +--- latexrun ++++ latexrun +@@ -965,7 +965,7 @@ class LaTeX(Task): + + def __clean_messages(self, msgs): + """Make some standard log messages more user-friendly.""" +- have_undefined_reference = False ++ has_errors = any(msg.typ == 'error' for msg in msgs) + for msg in msgs: + if msg.msg == '==> Fatal error occurred, no output PDF file produced!': + msg = msg._replace(typ='info', +@@ -973,10 +973,15 @@ class LaTeX(Task): + if msg.msg.startswith('[LaTeX] '): + # Strip unnecessary package name + msg = msg._replace(msg=msg.msg.split(' ', 1)[1]) +- if re.match(r'Reference .* undefined', msg.msg): +- have_undefined_reference = True +- if have_undefined_reference and \ +- re.match(r'There were undefined references', msg.msg): ++ if has_errors and re.match( ++ r'.*[Rr]erun to get .* right|.* on page .* undefined', msg.msg ++ ): ++ # Warnings on undefined references may occur in high ++ # numbers when documents fail to build. ++ continue ++ if re.match( ++ r"There were (multiply-defined labels|undefined references)", msg.msg ++ ): + # LaTeX prints this at the end so the user knows it's + # worthwhile looking back at the log. Since latexrun + # makes the earlier messages obvious, this is -- cgit v1.2.3