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
This commit is contained in:
Pavel Sanda 2019-06-07 16:47:04 +02:00
parent adb779a692
commit bf99ece736
5 changed files with 102 additions and 13 deletions

View 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());

View 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,

View 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;

View 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;

View 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;
};