commit bf99ece7366ad479458b3cecb067341a3320286a
Author: Pavel Sanda <[email protected]>
Date: Fri Jun 7 16:47:04 2019 +0200
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/[email protected]/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 c995f82..f1455dd 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 f1bd1f5..48ed0e7 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 8c22b1c..4dab241 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 0144e1c..74c875b 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<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;
diff --git a/src/LaTeX.h b/src/LaTeX.h
index a57cebc..ba088bb 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;
};