int const LYX_FORMAT = LYX_FORMAT_LYX;
typedef map<string, bool> DepClean;
-typedef map<docstring, pair<InsetLabel const *, Buffer::References> > RefCache;
+
+// Information about labels and their associated refs
+struct LabelInfo {
+ /// label string
+ docstring label;
+ /// label inset
+ InsetLabel const * inset;
+ /// associated references cache
+ Buffer::References references;
+ /// whether this label is active (i.e., not deleted)
+ bool active;
+};
+
+typedef vector<LabelInfo> LabelCache;
+
+typedef map<docstring, Buffer::References> RefCache;
} // namespace
/// A cache for the bibfiles (including bibfiles of loaded child
/// documents), needed for appropriate update of natbib labels.
- mutable support::FileNamePairList bibfiles_cache_;
+ mutable docstring_list bibfiles_cache_;
// FIXME The caching mechanism could be improved. At present, we have a
// cache for each Buffer, that caches all the bibliography info for that
mutable BiblioInfo bibinfo_;
/// whether the bibinfo cache is valid
mutable bool bibinfo_cache_valid_;
+ /// whether the bibfile cache is valid
+ mutable bool bibfile_cache_valid_;
/// Cache of timestamps of .bib files
map<FileName, time_t> bibfile_status_;
/// Indicates whether the bibinfo has changed since the last time
/// we ran updateBuffer(), i.e., whether citation labels may need
/// to be updated.
mutable bool cite_labels_valid_;
+ /// Do we have a bibliography environment?
+ mutable bool have_bibitems_;
/// These two hold the file name and format, written to by
/// Buffer::preview and read from by LFUN_BUFFER_VIEW_CACHE.
/// was missing).
bool preview_error_;
+ /// Cache the references associated to a label and their positions
+ /// in the buffer.
mutable RefCache ref_cache_;
+ /// Cache the label insets and their activity status.
+ mutable LabelCache label_cache_;
/// our Text that should be wrapped in an InsetText
InsetText * inset;
if (!cloned_buffer_ && parent_buffer && pb)
LYXERR0("Warning: a buffer should not have two parents!");
parent_buffer = pb;
- if (!cloned_buffer_ && parent_buffer)
+ if (!cloned_buffer_ && parent_buffer) {
parent_buffer->invalidateBibinfoCache();
+ }
}
/// If non zero, this buffer is a clone of existing buffer \p cloned_buffer_
file_fully_loaded(false), file_format(LYX_FORMAT), need_format_backup(false),
ignore_parent(false), toc_backend(owner), macro_lock(false),
checksum_(0), wa_(0), gui_(0), undo_(*owner), bibinfo_cache_valid_(false),
- cite_labels_valid_(false), preview_error_(false),
- inset(0), preview_loader_(0), cloned_buffer_(cloned_buffer),
+ bibfile_cache_valid_(false), cite_labels_valid_(false), have_bibitems_(false),
+ preview_error_(false), inset(0), preview_loader_(0), cloned_buffer_(cloned_buffer),
clone_list_(0), doing_export(false),
tracked_changes_present_(0), externally_modified_(false), parent_buffer(0),
word_count_(0), char_count_(0), blank_count_(0)
bibfiles_cache_ = cloned_buffer_->d->bibfiles_cache_;
bibinfo_ = cloned_buffer_->d->bibinfo_;
bibinfo_cache_valid_ = cloned_buffer_->d->bibinfo_cache_valid_;
+ bibfile_cache_valid_ = cloned_buffer_->d->bibfile_cache_valid_;
bibfile_status_ = cloned_buffer_->d->bibfile_status_;
cite_labels_valid_ = cloned_buffer_->d->cite_labels_valid_;
+ have_bibitems_ = cloned_buffer_->d->have_bibitems_;
unnamed = cloned_buffer_->d->unnamed;
internal_buffer = cloned_buffer_->d->internal_buffer;
layout_position = cloned_buffer_->d->layout_position;
}
-Buffer * Buffer::cloneFromMaster() const
+Buffer * Buffer::cloneWithChildren() const
{
BufferMap bufmap;
cloned_buffers.push_back(new CloneList);
CloneList * clones = cloned_buffers.back();
- masterBuffer()->cloneWithChildren(bufmap, clones);
+ cloneWithChildren(bufmap, clones);
// make sure we got cloned
BufferMap::const_iterator bit = bufmap.find(this);
if (branch_suffix.empty())
return filename;
- string const name = filename.onlyFileNameWithoutExt()
- + to_utf8(branch_suffix);
+ string const name = addExtension(filename.onlyFileNameWithoutExt()
+ + to_utf8(branch_suffix), filename.extension());
FileName res(filename.onlyPath().absFileName() + "/" + name);
- res.changeExtension(filename.extension());
return res;
}
<< absFileName()
<< ") does not include "
"this document. Ignoring the master assignment.");
+ // If the master has just been created, un-hide it (#11162)
+ if (!master->fileName().exists())
+ lyx::dispatch(FuncRequest(LFUN_BUFFER_SWITCH,
+ master->absFileName()));
}
}
}
FileName savefile(tempfile->name());
LYXERR(Debug::FILES, "Saving to " << savefile.absFileName());
+ if (!savefile.clonePermissions(fileName()))
+ LYXERR0("Failed to clone the permission from " << fileName().absFileName() << " to " << savefile.absFileName());
+
if (!writeFile(savefile))
return false;
LaTeXFeatures features(*this, params(), runparams);
validate(features);
// This is only set once per document (in master)
- if (!runparams.is_child)
+ if (!runparams.is_child) {
runparams.use_polyglossia = features.usePolyglossia();
+ runparams.use_hyperref = features.isRequired("hyperref");
+ }
LYXERR(Debug::LATEX, " Buffer validation done.");
bool const output_preamble =
"file path name."),
inputpath, uncodable_glyphs));
} else {
- string docdir =
- support::latex_path(original_path);
+ string docdir = os::latex_path(original_path);
if (contains(docdir, '#')) {
docdir = subst(docdir, "#", "\\#");
os << "\\catcode`\\#=11"
os << "\\catcode`\\%=11"
"\\def\\%{%}\\catcode`\\%=14\n";
}
+ if (contains(docdir, '~'))
+ docdir = subst(docdir, "~", "\\string~");
+ bool const nonascii = !isAscii(from_utf8(docdir));
+ // LaTeX 2019/10/01 handles non-ascii path without detokenize
+ bool const utfpathlatex = features.isAvailable("LaTeX-2019/10/01");
+ bool const detokenize = !utfpathlatex && nonascii;
+ bool const quote = contains(docdir, ' ');
+ if (utfpathlatex && nonascii)
+ os << "\\UseRawInputEncoding\n";
os << "\\makeatletter\n"
- << "\\def\\input@path{{"
- << docdir << "}}\n"
+ << "\\def\\input@path{{";
+ if (detokenize)
+ os << "\\detokenize{";
+ if (quote)
+ os << "\"";
+ os << docdir;
+ if (quote)
+ os << "\"";
+ if (detokenize)
+ os << "}";
+ os << "}}\n"
<< "\\makeatother\n";
}
}
// Biblatex bibliographies are loaded here
if (params().useBiblatex()) {
vector<docstring> const bibfiles =
- prepareBibFilePaths(runparams, getBibfiles(), true);
+ prepareBibFilePaths(runparams, getBibfiles(), true);
for (docstring const & file: bibfiles)
os << "\\addbibresource{" << file << "}\n";
}
if (!styles.empty())
os << "\n<!-- Text Class Preamble -->\n" << styles << '\n';
- styles = features.getPreambleSnippets().str;
- if (!styles.empty())
- os << "\n<!-- Preamble Snippets -->\n" << styles << '\n';
-
// we will collect CSS information in a stream, and then output it
// either here, as part of the header, or else in a separate file.
odocstringstream css;
}
+// This is not in master, so it has been removed at 2126d5a3, on
+// 14 December 2018, so as to test whether it's needed. If there
+// aren't any complaints, it can be fully removed.
+#if 0
+void Buffer::updateBibfilesCache(UpdateScope scope) const
+{
+ // FIXME This is probably unnecssary, given where we call this.
+ // If this is a child document, use the parent's cache instead.
+ if (parent() && scope != UpdateChildOnly) {
+ masterBuffer()->updateBibfilesCache();
+ return;
+ }
+
+ docstring_list old_cache = d->bibfiles_cache_;
+ d->bibfiles_cache_.clear();
+ for (InsetIterator it = inset_iterator_begin(inset()); it; ++it) {
+ if (it->lyxCode() == BIBTEX_CODE) {
+ InsetBibtex const & inset = static_cast<InsetBibtex const &>(*it);
+ docstring_list const bibfiles = inset.getBibFiles();
+ d->bibfiles_cache_.insert(d->bibfiles_cache_.end(),
+ bibfiles.begin(),
+ bibfiles.end());
+ } else if (it->lyxCode() == INCLUDE_CODE) {
+ InsetInclude & inset = static_cast<InsetInclude &>(*it);
+ Buffer const * const incbuf = inset.getChildBuffer();
+ if (!incbuf)
+ continue;
+ docstring_list const & bibfiles =
+ incbuf->getBibfiles(UpdateChildOnly);
+ if (!bibfiles.empty()) {
+ d->bibfiles_cache_.insert(d->bibfiles_cache_.end(),
+ bibfiles.begin(),
+ bibfiles.end());
+ }
+ }
+ }
+ d->bibfile_cache_valid_ = true;
+ d->cite_labels_valid_ = false;
+ if (d->bibfiles_cache_ != old_cache)
+ d->bibinfo_cache_valid_ = false;
+}
+#endif
+
+
void Buffer::invalidateBibinfoCache() const
{
d->bibinfo_cache_valid_ = false;
}
-FileNamePairList const & Buffer::getBibfiles(UpdateScope scope) const
+docstring_list const & Buffer::getBibfiles(UpdateScope scope) const
{
// FIXME This is probably unnecessary, given where we call this.
- // If this is a child document, use the master instead.
+ // If this is a child document, use the master's cache instead.
Buffer const * const pbuf = masterBuffer();
if (pbuf != this && scope != UpdateChildOnly)
return pbuf->getBibfiles();
+
return d->bibfiles_cache_;
}
}
-void Buffer::registerBibfiles(FileNamePairList const & bf) const {
+void Buffer::registerBibfiles(const docstring_list & bf) const
+{
+ // We register the bib files in the master buffer,
+ // if there is one, but also in every single buffer,
+ // in case a child is compiled alone.
Buffer const * const tmp = masterBuffer();
if (tmp != this)
tmp->registerBibfiles(bf);
for (auto const & p : bf) {
- FileNamePairList::const_iterator tmp =
+ docstring_list::const_iterator temp =
find(d->bibfiles_cache_.begin(), d->bibfiles_cache_.end(), p);
- if (tmp == d->bibfiles_cache_.end())
+ if (temp == d->bibfiles_cache_.end())
d->bibfiles_cache_.push_back(p);
}
}
+static map<docstring, FileName> bibfileCache;
+
+FileName Buffer::getBibfilePath(docstring const & bibid) const
+{
+ map<docstring, FileName>::const_iterator it =
+ bibfileCache.find(bibid);
+ if (it != bibfileCache.end()) {
+ // i.e., return bibfileCache[bibid];
+ return it->second;
+ }
+
+ LYXERR(Debug::FILES, "Reading file location for " << bibid);
+ string const texfile = changeExtension(to_utf8(bibid), "bib");
+ // we need to check first if this file exists where it's said to be.
+ // there's a weird bug that occurs otherwise: if the file is in the
+ // Buffer's directory but has the same name as some file that would be
+ // found by kpsewhich, then we find the latter, not the former.
+ FileName const local_file = makeAbsPath(texfile, filePath());
+ FileName file = local_file;
+ if (!file.exists()) {
+ // there's no need now to check whether the file can be found
+ // locally
+ file = findtexfile(texfile, "bib", true);
+ if (file.empty())
+ file = local_file;
+ }
+ LYXERR(Debug::FILES, "Found at: " << file);
+
+ bibfileCache[bibid] = file;
+ return bibfileCache[bibid];
+}
+
+
void Buffer::checkIfBibInfoCacheIsValid() const
{
// use the master's cache
return;
}
- // if we already know the cache is invalid, no need to check
- // the timestamps
+ // If we already know the cache is invalid, stop here.
+ // This is important in the case when the bibliography
+ // environment (rather than Bib[la]TeX) is used.
+ // In that case, the timestamp check below gives no
+ // sensible result. Rather than that, the cache will
+ // be invalidated explicitly via invalidateBibInfoCache()
+ // by the Bibitem inset.
+ // Same applies for bib encoding changes, which trigger
+ // invalidateBibInfoCache() by InsetBibtex.
if (!d->bibinfo_cache_valid_)
return;
+ if (d->have_bibitems_) {
+ // We have a bibliography environment.
+ // Invalidate the bibinfo cache unconditionally.
+ // Cite labels will get invalidated by the inset if needed.
+ d->bibinfo_cache_valid_ = false;
+ return;
+ }
+
+ // OK. This is with Bib(la)tex. We'll assume the cache
+ // is valid and change this if we find changes in the bibs.
+ d->bibinfo_cache_valid_ = true;
+ d->cite_labels_valid_ = true;
// compare the cached timestamps with the actual ones.
- FileNamePairList const & bibfiles_cache = getBibfiles();
- FileNamePairList::const_iterator ei = bibfiles_cache.begin();
- FileNamePairList::const_iterator en = bibfiles_cache.end();
- for (; ei != en; ++ ei) {
- FileName const fn = ei->second;
+ docstring_list const & bibfiles_cache = getBibfiles();
+ for (auto const & bf : bibfiles_cache) {
+ FileName const fn = getBibfilePath(bf);
time_t lastw = fn.lastModified();
time_t prevw = d->bibfile_status_[fn];
if (lastw != prevw) {
}
+void Buffer::clearBibFileCache() const
+{
+ bibfileCache.clear();
+}
+
+
void Buffer::reloadBibInfoCache() const
{
+ if (isInternal())
+ return;
+
// use the master's cache
Buffer const * const tmp = masterBuffer();
if (tmp != this) {
checkIfBibInfoCacheIsValid();
if (d->bibinfo_cache_valid_)
return;
+ LYXERR(Debug::FILES, "Bibinfo cache was invalid.");
+ // re-read file locations when this info changes
+ // FIXME Is this sufficient? Or should we also force that
+ // in some other cases? If so, then it is easy enough to
+ // add the following line in some other places.
+ clearBibFileCache();
d->bibinfo_.clear();
FileNameList checkedFiles;
+ d->have_bibitems_ = false;
collectBibKeys(checkedFiles);
d->bibinfo_cache_valid_ = true;
}
void Buffer::collectBibKeys(FileNameList & checkedFiles) const
{
- for (InsetIterator it = inset_iterator_begin(inset()); it; ++it)
+ for (InsetIterator it = inset_iterator_begin(inset()); it; ++it) {
it->collectBibKeys(it, checkedFiles);
+ if (it->lyxCode() == BIBITEM_CODE) {
+ if (parent() != 0)
+ parent()->d->have_bibitems_ = true;
+ else
+ d->have_bibitems_ = true;
+ }
+ }
}
void Buffer::addBiblioInfo(BiblioInfo const & bin) const
{
+ // We add the biblio info to the parent buffer,
+ // if there is one, but also to this buffer, in case
+ // it is compiled alone.
BiblioInfo & bi = d->bibinfo_;
bi.mergeBiblioInfo(bin);
- if (parent() != 0) {
- BiblioInfo & masterbi = parent()->d->bibinfo_;
- masterbi.mergeBiblioInfo(bin);
- }
+ if (parent())
+ parent()->addBiblioInfo(bin);
}
for ( ; cur ; cur.forwardPar())
cur.paragraph().anonymize();
dr.forceBufferUpdate();
+ dr.screenUpdate(Update::Force);
break;
}
vector<docstring> const Buffer::prepareBibFilePaths(OutputParams const & runparams,
- FileNamePairList const bibfilelist,
+ docstring_list const & bibfilelist,
bool const add_extension) const
{
// If we are processing the LaTeX file in a temp directory then
// check for spaces in paths
bool found_space = false;
- FileNamePairList::const_iterator it = bibfilelist.begin();
- FileNamePairList::const_iterator en = bibfilelist.end();
- for (; it != en; ++it) {
- string utf8input = to_utf8(it->first);
+ for (auto const & bit : bibfilelist) {
+ string utf8input = to_utf8(bit);
string database =
prepareFileNameForLaTeX(utf8input, ".bib", runparams.nice);
FileName try_in_file =
// If the file has not been found, try with the real file name
// (it might come from a child in a sub-directory)
if (!not_from_texmf) {
- try_in_file = it->second;
+ try_in_file = getBibfilePath(bit);
if (try_in_file.isReadableFile()) {
// Check if the file is in texmf
FileName kpsefile(findtexfile(changeExtension(utf8input, "bib"), "bib", true));
RefCache::iterator it = d->ref_cache_.find(label);
if (it != d->ref_cache_.end())
- return it->second.second;
+ return it->second;
- static InsetLabel const * dummy_il = 0;
static References const dummy_refs = References();
it = d->ref_cache_.insert(
- make_pair(label, make_pair(dummy_il, dummy_refs))).first;
- return it->second.second;
+ make_pair(label, dummy_refs)).first;
+ return it->second;
}
}
-void Buffer::setInsetLabel(docstring const & label, InsetLabel const * il)
+void Buffer::setInsetLabel(docstring const & label, InsetLabel const * il,
+ bool const active)
{
- masterBuffer()->d->ref_cache_[label].first = il;
+ LabelInfo linfo;
+ linfo.label = label;
+ linfo.inset = il;
+ linfo.active = active;
+ masterBuffer()->d->label_cache_.push_back(linfo);
}
-InsetLabel const * Buffer::insetLabel(docstring const & label) const
+InsetLabel const * Buffer::insetLabel(docstring const & label,
+ bool const active) const
{
- return masterBuffer()->d->ref_cache_[label].first;
+ for (auto & rc : masterBuffer()->d->label_cache_) {
+ if (rc.label == label && (rc.active || !active))
+ return rc.inset;
+ }
+ return nullptr;
+}
+
+
+bool Buffer::activeLabel(docstring const & label) const
+{
+ if (!insetLabel(label, true))
+ return false;
+
+ return true;
}
void Buffer::clearReferenceCache() const
{
- if (!d->parent())
+ if (!d->parent()) {
d->ref_cache_.clear();
+ d->label_cache_.clear();
+ }
}
LaTeXFeatures features(*this, params(), runparams);
validate(features);
runparams.use_polyglossia = features.usePolyglossia();
+ runparams.use_hyperref = features.isRequired("hyperref");
// latex or literate
otexstream ots(os);
// output above
// file (not for previewing).
Alert::error(_("Couldn't export file"), bformat(
_("No information for exporting the format %1$s."),
- theFormats().prettyName(format)));
+ translateIfPossible(theFormats().prettyName(format))));
}
return ExportNoPathToFormat;
}
bformat(_("The directory path to the document\n%1$s\n"
"contains spaces, but your TeX installation does "
"not allow them. You should save the file to a directory "
- "whose name does not contain spaces."), from_ascii(filePath())));
+ "whose name does not contain spaces."), from_utf8(filePath())));
return ExportTexPathHasSpaces;
} else {
runparams.nice = false;
} else {
message(bformat(_("Document exported as %1$s "
"to file `%2$s'"),
- theFormats().prettyName(format),
+ translateIfPossible(theFormats().prettyName(format)),
makeDisplayPath(result_file)));
}
} else {
// This must be a dummy converter like fax (bug 1888)
message(bformat(_("Document exported as %1$s"),
- theFormats().prettyName(format)));
+ translateIfPossible(theFormats().prettyName(format))));
}
return success ? ExportSuccess : ExportConverterError;
_("&Remove"), _("&Keep"));
if (del_emerg == 0)
emergencyFile.removeFile();
+ else {
+ // See bug #11464
+ FileName newname;
+ string const ename = emergencyFile.absFileName();
+ bool noname = true;
+ // Surely we can find one in 100 tries?
+ for (int i = 1; i < 100; ++i) {
+ newname.set(ename + to_string(i) + ".lyx");
+ if (!newname.exists()) {
+ noname = false;
+ break;
+ }
+ }
+ if (!noname) {
+ // renameTo returns true on success. So inverting that
+ // will give us true if we fail.
+ noname = !emergencyFile.renameTo(newname);
+ }
+ if (noname) {
+ Alert::warning(_("Can't rename emergency file!"),
+ _("LyX was unable to rename the emergency file. "
+ "You should do so manually. Otherwise, you will be"
+ "asked about it again the next time you try to load"
+ "this file, and may over-write your own work."));
+ }
+ }
return ReadOriginal;
}
Buffer const * const master = masterBuffer();
DocumentClass const & textclass = master->params().documentClass();
- FileNamePairList old_bibfiles;
- // do this only if we are the top-level Buffer
- if (master == this) {
+ docstring_list old_bibfiles;
+ // Do this only if we are the top-level Buffer. We also need to account
+ // for the case of a previewed child with ignored parent here.
+ if (master == this && !d->ignore_parent) {
textclass.counters().reset(from_ascii("bibitem"));
reloadBibInfoCache();
// we will re-read this cache as we go through, but we need
ParIterator parit = cbuf.par_iterator_begin();
updateBuffer(parit, utype);
- // If this document has siblings, then update the TocBackend later. The
- // reason is to ensure that later siblings are up to date when e.g. the
- // broken or not status of references is computed. The update is called
- // in InsetInclude::addToToc.
if (master != this)
+ // If this document has siblings, then update the TocBackend later. The
+ // reason is to ensure that later siblings are up to date when e.g. the
+ // broken or not status of references is computed. The update is called
+ // in InsetInclude::addToToc.
return;
// if the bibfiles changed, the cache of bibinfo is invalid
- FileNamePairList new_bibfiles = d->bibfiles_cache_;
+ docstring_list new_bibfiles = d->bibfiles_cache_;
// this is a trick to determine whether the two vectors have
// the same elements.
sort(new_bibfiles.begin(), new_bibfiles.end());