From bf99ece7366ad479458b3cecb067341a3320286a Mon Sep 17 00:00:00 2001 From: Pavel Sanda Date: Fri, 7 Jun 2019 16:47:04 +0200 Subject: [PATCH] Start reporting missing citations and broken references in LaTeX build. Now we report these in the same way as LaTeX errors (but let the user to see the result anyway). It remains to be shown much is this disturbing to users. Generally, ignoring these is not a good idea, because they are harder to manually spot in longer documents. The details of reported error varies because log linebreaks at 90 induced by pdflatex make log harder to parse. The committed code is more robust than previous, in which some missing cits/refs with long keys would go unnoticed. Tested on bibtex and natbib. https://www.mail-archive.com/lyx-devel@lists.lyx.org/msg208912.html --- src/Buffer.cpp | 42 +++++++++++++++++++++++++++++++--------- src/Buffer.h | 2 ++ src/Converter.cpp | 10 ++++++++++ src/LaTeX.cpp | 49 +++++++++++++++++++++++++++++++++++++++++++---- src/LaTeX.h | 12 ++++++++++++ 5 files changed, 102 insertions(+), 13 deletions(-) diff --git a/src/Buffer.cpp b/src/Buffer.cpp index c995f825f5..f1455ddd90 100644 --- a/src/Buffer.cpp +++ b/src/Buffer.cpp @@ -403,6 +403,12 @@ public: /// has been externally modified? Can be reset by the user. mutable bool externally_modified_; + ///Binding LaTeX lines with buffer positions. + //Common routine for LaTeX and Reference errors listing. + void traverseErrors(TeXErrors::Errors::const_iterator err, + TeXErrors::Errors::const_iterator end, + ErrorList & errorList) const; + private: /// So we can force access via the accessors. mutable Buffer const * parent_buffer; @@ -4897,26 +4903,26 @@ Buffer::ReadStatus Buffer::loadThisLyXFile(FileName const & fn) } -void Buffer::bufferErrors(TeXErrors const & terr, ErrorList & errorList) const +void Buffer::Impl::traverseErrors(TeXErrors::Errors::const_iterator err, TeXErrors::Errors::const_iterator end, ErrorList & errorList) const { - for (auto const & err : terr) { + for (; err != end; ++err) { TexRow::TextEntry start = TexRow::text_none, end = TexRow::text_none; - int errorRow = err.error_in_line; + int errorRow = err->error_in_line; Buffer const * buf = 0; - Impl const * p = d; - if (err.child_name.empty()) + Impl const * p = this; + if (err->child_name.empty()) tie(start, end) = p->texrow.getEntriesFromRow(errorRow); else { // The error occurred in a child - for (Buffer const * child : getDescendents()) { + for (Buffer const * child : owner_->getDescendents()) { string const child_name = DocFileName(changeExtension(child->absFileName(), "tex")). mangledFileName(); - if (err.child_name != child_name) + if (err->child_name != child_name) continue; tie(start, end) = child->d->texrow.getEntriesFromRow(errorRow); if (!TexRow::isNone(start)) { - buf = d->cloned_buffer_ + buf = this->cloned_buffer_ ? child->d->cloned_buffer_->d->owner_ : child->d->owner_; p = child->d; @@ -4924,12 +4930,30 @@ void Buffer::bufferErrors(TeXErrors const & terr, ErrorList & errorList) const } } } - errorList.push_back(ErrorItem(err.error_desc, err.error_text, + errorList.push_back(ErrorItem(err->error_desc, err->error_text, start, end, buf)); } } +void Buffer::bufferErrors(TeXErrors const & terr, ErrorList & errorList) const +{ + TeXErrors::Errors::const_iterator err = terr.begin(); + TeXErrors::Errors::const_iterator end = terr.end(); + + d->traverseErrors(err, end, errorList); +} + + +void Buffer::bufferRefs(TeXErrors const & terr, ErrorList & errorList) const +{ + TeXErrors::Errors::const_iterator err = terr.begin_ref(); + TeXErrors::Errors::const_iterator end = terr.end_ref(); + + d->traverseErrors(err, end, errorList); +} + + void Buffer::updateBuffer(UpdateScope scope, UpdateType utype) const { LBUFERR(!text().paragraphs().empty()); diff --git a/src/Buffer.h b/src/Buffer.h index f1bd1f5e79..48ed0e7c36 100644 --- a/src/Buffer.h +++ b/src/Buffer.h @@ -287,6 +287,8 @@ private: public: /// Fill in the ErrorList with the TeXErrors void bufferErrors(TeXErrors const &, ErrorList &) const; + /// Fill in the Citation/Reference ErrorList from the TeXErrors + void bufferRefs(TeXErrors const &, ErrorList &) const; enum OutputWhat { FullSource, diff --git a/src/Converter.cpp b/src/Converter.cpp index 8c22b1c42a..4dab2413db 100644 --- a/src/Converter.cpp +++ b/src/Converter.cpp @@ -881,6 +881,14 @@ Converters::RetVal Converters::runLaTeX(Buffer const & buffer, string const & co if (result & LaTeX::ERRORS) buffer.bufferErrors(terr, errorList); + if ((result & LaTeX::UNDEF_CIT) || (result & LaTeX::UNDEF_REF)) { + buffer.bufferRefs(terr, errorList); + if (errorList.empty()) + errorList.push_back(ErrorItem(_("Undefined reference"), + _("Undefined reference or citation was found during the build, please check the Log."), + &buffer)); + } + if (!errorList.empty()) { // We will show the LaTeX Errors GUI later which contains // specific error messages so it would be repetitive to give @@ -909,6 +917,8 @@ Converters::RetVal Converters::runLaTeX(Buffer const & buffer, string const & co int const ERROR_MASK = LaTeX::NO_LOGFILE | LaTeX::ERRORS | + LaTeX::UNDEF_CIT | + LaTeX::UNDEF_REF | LaTeX::NO_OUTPUT; return (result & ERROR_MASK) == 0 ? SUCCESS : FAILURE; diff --git a/src/LaTeX.cpp b/src/LaTeX.cpp index 0144e1cf16..74c875b840 100644 --- a/src/LaTeX.cpp +++ b/src/LaTeX.cpp @@ -73,6 +73,15 @@ void TeXErrors::insertError(int line, docstring const & error_desc, } +void TeXErrors::insertRef(int line, docstring const & error_desc, + docstring const & error_text, + string const & child_name) +{ + Error newerr(line, error_desc, error_text, child_name); + undef_ref.push_back(newerr); +} + + bool operator==(AuxInfo const & a, AuxInfo const & o) { return a.aux_file == o.aux_file @@ -682,6 +691,14 @@ bool LaTeX::runBibTeX(vector const & bibtex_info, } +//helper func for scanLogFile; gets line number X from strings "... on input line X ..." +//returns 0 if none is found +int getLineNumber(const string &token){ + string l = support::token(token, ' ', tokenPos(token,' ',"line") + 1); + return l.empty() ? 0 : convert(l); +} + + int LaTeX::scanLogFile(TeXErrors & terr) { int last_line = -1; @@ -706,6 +723,8 @@ int LaTeX::scanLogFile(TeXErrors & terr) stack > child; children.clear(); + terr.clearRefs(); + string token; while (getline(ifs, token)) { // MikTeX sometimes inserts \0 in the log file. They can't be @@ -752,6 +771,12 @@ int LaTeX::scanLogFile(TeXErrors & terr) if (contains(token, "file:line:error style messages enabled")) fle_style = true; + //Handles both "LaTeX Warning:" & "Package natbib Warning:" + //Various handlers for missing citations below won't catch the problem if citation + //key is long (>~25chars), because pdflatex splits output at line length 80. + if (contains(token, "There were undefined citations.")) + retval |= UNDEF_CIT; + if (prefixIs(token, "LaTeX Warning:") || prefixIs(token, "! pdfTeX warning")) { // Here shall we handle different @@ -771,15 +796,28 @@ int LaTeX::scanLogFile(TeXErrors & terr) } else if (contains(token, "Etaremune labels have changed")) { retval |= ERROR_RERUN; LYXERR(Debug::LATEX, "Force rerun."); + //"Citation `cit' on page X undefined on input line X." } else if (contains(token, "Citation") - && contains(token, "on page") + //&& contains(token, "on input line") //often split to newline && contains(token, "undefined")) { retval |= UNDEF_CIT; - } else if (contains(token, "Citation") - && contains(token, "on input line") + terr.insertRef(getLineNumber(token), from_ascii("Citation undefined"), + from_utf8(token), child_name); + //"Reference `X' on page Y undefined on input line Z." + } else if (contains(token, "Reference") + //&& contains(token, "on input line")) //often split to new line && contains(token, "undefined")) { - retval |= UNDEF_CIT; + retval |= UNDEF_REF; + terr.insertRef(getLineNumber(token), from_ascii("Reference undefined"), + from_utf8(token), child_name); + + //If label is too long pdlaftex log line splitting will make the above fail + //so we catch at least this generic statement occuring for both CIT & REF. + } else if (contains(token, "There were undefined references.")) { + if (!(retval & UNDEF_CIT)) //if not handled already + retval |= UNDEF_REF; } + } else if (prefixIs(token, "Package")) { // Package warnings retval |= PACKAGE_WARNING; @@ -789,6 +827,9 @@ int LaTeX::scanLogFile(TeXErrors & terr) && contains(token, "on page") && contains(token, "undefined")) { retval |= UNDEF_CIT; + //Unf only keys up to ~6 chars will make it due to line splits + terr.insertRef(getLineNumber(token), from_ascii("Citation undefined"), + from_utf8(token), child_name); } } else if (contains(token, "run BibTeX")) { retval |= UNDEF_CIT; diff --git a/src/LaTeX.h b/src/LaTeX.h index a57cebc83b..ba088bb5cc 100644 --- a/src/LaTeX.h +++ b/src/LaTeX.h @@ -60,14 +60,26 @@ public: /// Errors::const_iterator end() const { return errors.end(); } /// + Errors::const_iterator begin_ref() const { return undef_ref.begin(); } + /// + Errors::const_iterator end_ref() const { return undef_ref.end(); } + /// void insertError(int line, docstring const & error_desc, docstring const & error_text, std::string const & child_name = empty_string()); /// void clearErrors() { errors.clear(); } + /// + void insertRef(int line, docstring const & error_desc, + docstring const & error_text, + std::string const & child_name = empty_string()); + /// + void clearRefs() { undef_ref.clear(); } private: /// Errors errors; + /// For missing Citation and references + Errors undef_ref; }; -- 2.39.2