]> git.lyx.org Git - features.git/commitdiff
Start reporting missing citations and broken references in LaTeX build.
authorPavel Sanda <sanda@lyx.org>
Fri, 7 Jun 2019 14:47:04 +0000 (16:47 +0200)
committerPavel Sanda <sanda@lyx.org>
Fri, 7 Jun 2019 14:47:04 +0000 (16:47 +0200)
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
src/Buffer.h
src/Converter.cpp
src/LaTeX.cpp
src/LaTeX.h

index c995f825f5e60cf3d5fe15ebae3cede97779b80a..f1455ddd909e090c83e72561f5256bf70b1a80b8 100644 (file)
@@ -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());
index f1bd1f5e795ee7d85513c1217fda43971d8f875a..48ed0e7c36be7779af78124402ec07fd122e31d9 100644 (file)
@@ -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,
index 8c22b1c42aed4f412fdffe8b0f2f6655ec2b6574..4dab2413dbd408495af5fd3d3f6bab7f6556750c 100644 (file)
@@ -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;
index 0144e1cf166cdab2e6890d6ca82652724accf989..74c875b84057312538a89fab5e53cc3348b0a756 100644 (file)
@@ -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<AuxInfo> 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<int>(l);
+}
+
+
 int LaTeX::scanLogFile(TeXErrors & terr)
 {
        int last_line = -1;
@@ -706,6 +723,8 @@ int LaTeX::scanLogFile(TeXErrors & terr)
        stack <pair<string, int> > 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;
index a57cebc83bcc2ff5ac874e37f17efb361d1bbac2..ba088bb5cceed9afa352e26049a677a4558e3215 100644 (file)
@@ -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;
 };