mirror of
https://git.lyx.org/repos/lyx.git
synced 2024-11-22 18:08:10 +00:00
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:
parent
adb779a692
commit
bf99ece736
@ -403,6 +403,12 @@ public:
|
|||||||
/// has been externally modified? Can be reset by the user.
|
/// has been externally modified? Can be reset by the user.
|
||||||
mutable bool externally_modified_;
|
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:
|
private:
|
||||||
/// So we can force access via the accessors.
|
/// So we can force access via the accessors.
|
||||||
mutable Buffer const * parent_buffer;
|
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;
|
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;
|
Buffer const * buf = 0;
|
||||||
Impl const * p = d;
|
Impl const * p = this;
|
||||||
if (err.child_name.empty())
|
if (err->child_name.empty())
|
||||||
tie(start, end) = p->texrow.getEntriesFromRow(errorRow);
|
tie(start, end) = p->texrow.getEntriesFromRow(errorRow);
|
||||||
else {
|
else {
|
||||||
// The error occurred in a child
|
// The error occurred in a child
|
||||||
for (Buffer const * child : getDescendents()) {
|
for (Buffer const * child : owner_->getDescendents()) {
|
||||||
string const child_name =
|
string const child_name =
|
||||||
DocFileName(changeExtension(child->absFileName(), "tex")).
|
DocFileName(changeExtension(child->absFileName(), "tex")).
|
||||||
mangledFileName();
|
mangledFileName();
|
||||||
if (err.child_name != child_name)
|
if (err->child_name != child_name)
|
||||||
continue;
|
continue;
|
||||||
tie(start, end) = child->d->texrow.getEntriesFromRow(errorRow);
|
tie(start, end) = child->d->texrow.getEntriesFromRow(errorRow);
|
||||||
if (!TexRow::isNone(start)) {
|
if (!TexRow::isNone(start)) {
|
||||||
buf = d->cloned_buffer_
|
buf = this->cloned_buffer_
|
||||||
? child->d->cloned_buffer_->d->owner_
|
? child->d->cloned_buffer_->d->owner_
|
||||||
: child->d->owner_;
|
: child->d->owner_;
|
||||||
p = child->d;
|
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));
|
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
|
void Buffer::updateBuffer(UpdateScope scope, UpdateType utype) const
|
||||||
{
|
{
|
||||||
LBUFERR(!text().paragraphs().empty());
|
LBUFERR(!text().paragraphs().empty());
|
||||||
|
@ -287,6 +287,8 @@ private:
|
|||||||
public:
|
public:
|
||||||
/// Fill in the ErrorList with the TeXErrors
|
/// Fill in the ErrorList with the TeXErrors
|
||||||
void bufferErrors(TeXErrors const &, ErrorList &) const;
|
void bufferErrors(TeXErrors const &, ErrorList &) const;
|
||||||
|
/// Fill in the Citation/Reference ErrorList from the TeXErrors
|
||||||
|
void bufferRefs(TeXErrors const &, ErrorList &) const;
|
||||||
|
|
||||||
enum OutputWhat {
|
enum OutputWhat {
|
||||||
FullSource,
|
FullSource,
|
||||||
|
@ -881,6 +881,14 @@ Converters::RetVal Converters::runLaTeX(Buffer const & buffer, string const & co
|
|||||||
if (result & LaTeX::ERRORS)
|
if (result & LaTeX::ERRORS)
|
||||||
buffer.bufferErrors(terr, errorList);
|
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()) {
|
if (!errorList.empty()) {
|
||||||
// We will show the LaTeX Errors GUI later which contains
|
// We will show the LaTeX Errors GUI later which contains
|
||||||
// specific error messages so it would be repetitive to give
|
// 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 =
|
int const ERROR_MASK =
|
||||||
LaTeX::NO_LOGFILE |
|
LaTeX::NO_LOGFILE |
|
||||||
LaTeX::ERRORS |
|
LaTeX::ERRORS |
|
||||||
|
LaTeX::UNDEF_CIT |
|
||||||
|
LaTeX::UNDEF_REF |
|
||||||
LaTeX::NO_OUTPUT;
|
LaTeX::NO_OUTPUT;
|
||||||
|
|
||||||
return (result & ERROR_MASK) == 0 ? SUCCESS : FAILURE;
|
return (result & ERROR_MASK) == 0 ? SUCCESS : FAILURE;
|
||||||
|
@ -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)
|
bool operator==(AuxInfo const & a, AuxInfo const & o)
|
||||||
{
|
{
|
||||||
return a.aux_file == o.aux_file
|
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 LaTeX::scanLogFile(TeXErrors & terr)
|
||||||
{
|
{
|
||||||
int last_line = -1;
|
int last_line = -1;
|
||||||
@ -706,6 +723,8 @@ int LaTeX::scanLogFile(TeXErrors & terr)
|
|||||||
stack <pair<string, int> > child;
|
stack <pair<string, int> > child;
|
||||||
children.clear();
|
children.clear();
|
||||||
|
|
||||||
|
terr.clearRefs();
|
||||||
|
|
||||||
string token;
|
string token;
|
||||||
while (getline(ifs, token)) {
|
while (getline(ifs, token)) {
|
||||||
// MikTeX sometimes inserts \0 in the log file. They can't be
|
// 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"))
|
if (contains(token, "file:line:error style messages enabled"))
|
||||||
fle_style = true;
|
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:") ||
|
if (prefixIs(token, "LaTeX Warning:") ||
|
||||||
prefixIs(token, "! pdfTeX warning")) {
|
prefixIs(token, "! pdfTeX warning")) {
|
||||||
// Here shall we handle different
|
// Here shall we handle different
|
||||||
@ -771,15 +796,28 @@ int LaTeX::scanLogFile(TeXErrors & terr)
|
|||||||
} else if (contains(token, "Etaremune labels have changed")) {
|
} else if (contains(token, "Etaremune labels have changed")) {
|
||||||
retval |= ERROR_RERUN;
|
retval |= ERROR_RERUN;
|
||||||
LYXERR(Debug::LATEX, "Force rerun.");
|
LYXERR(Debug::LATEX, "Force rerun.");
|
||||||
|
//"Citation `cit' on page X undefined on input line X."
|
||||||
} else if (contains(token, "Citation")
|
} else if (contains(token, "Citation")
|
||||||
&& contains(token, "on page")
|
//&& contains(token, "on input line") //often split to newline
|
||||||
&& contains(token, "undefined")) {
|
&& contains(token, "undefined")) {
|
||||||
retval |= UNDEF_CIT;
|
retval |= UNDEF_CIT;
|
||||||
} else if (contains(token, "Citation")
|
terr.insertRef(getLineNumber(token), from_ascii("Citation undefined"),
|
||||||
&& contains(token, "on input line")
|
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")) {
|
&& 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")) {
|
} else if (prefixIs(token, "Package")) {
|
||||||
// Package warnings
|
// Package warnings
|
||||||
retval |= PACKAGE_WARNING;
|
retval |= PACKAGE_WARNING;
|
||||||
@ -789,6 +827,9 @@ int LaTeX::scanLogFile(TeXErrors & terr)
|
|||||||
&& contains(token, "on page")
|
&& contains(token, "on page")
|
||||||
&& contains(token, "undefined")) {
|
&& contains(token, "undefined")) {
|
||||||
retval |= UNDEF_CIT;
|
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")) {
|
} else if (contains(token, "run BibTeX")) {
|
||||||
retval |= UNDEF_CIT;
|
retval |= UNDEF_CIT;
|
||||||
|
12
src/LaTeX.h
12
src/LaTeX.h
@ -60,14 +60,26 @@ public:
|
|||||||
///
|
///
|
||||||
Errors::const_iterator end() const { return errors.end(); }
|
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,
|
void insertError(int line, docstring const & error_desc,
|
||||||
docstring const & error_text,
|
docstring const & error_text,
|
||||||
std::string const & child_name = empty_string());
|
std::string const & child_name = empty_string());
|
||||||
///
|
///
|
||||||
void clearErrors() { errors.clear(); }
|
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:
|
private:
|
||||||
///
|
///
|
||||||
Errors errors;
|
Errors errors;
|
||||||
|
/// For missing Citation and references
|
||||||
|
Errors undef_ref;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user