#include "support/textutils.h"
#include "support/types.h"
-#include "support/bind.h"
-
#include <algorithm>
#include <fstream>
#include <iomanip>
typedef map<docstring, Buffer::References> RefCache;
// A storehouse for the cloned buffers.
-std::list<CloneList *> cloned_buffers;
+typedef list<CloneList_ptr> CloneStore;
+CloneStore cloned_buffers;
} // namespace
/// need to regenerate .tex?
DepClean dep_clean;
- /// is save needed?
- mutable bool lyx_clean;
-
- /// is autosave needed?
- mutable bool bak_clean;
-
- /// is this an unnamed file (New...)?
- bool unnamed;
-
- /// is this an internal bufffer?
- bool internal_buffer;
-
- /// buffer is r/o
- bool read_only;
-
/// name of the file the buffer is associated with.
FileName filename;
- /** Set to true only when the file is fully loaded.
- * Used to prevent the premature generation of previews
- * and by the citation inset.
- */
- bool file_fully_loaded;
-
- /// original format of loaded file
- int file_format;
-
- /// if the file was originally loaded from an older format, do
- /// we need to back it up still?
- bool need_format_backup;
-
- /// Ignore the parent (e.g. when exporting a child standalone)?
- bool ignore_parent;
-
///
mutable TocBackend toc_backend;
/// map from the macro name to the position map,
/// which maps the macro definition position to the scope and the MacroData.
NamePositionScopeMacroMap macros;
- /// This seem to change the way Buffer::getMacro() works
- mutable bool macro_lock;
/// positions of child buffers in the buffer
typedef map<Buffer const * const, DocIterator> BufferPositionMap;
// file, and then to construct the Buffer's bibinfo from that.
/// A cache for bibliography info
mutable BiblioInfo bibinfo_;
- /// whether the bibinfo cache is valid
- mutable bool bibinfo_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.
FileName preview_file_;
string preview_format_;
- /// If there was an error when previewing, on the next preview we do
- /// a fresh compile (e.g. in case the user installed a package that
- /// was missing).
- bool require_fresh_start_;
/// Cache the references associated to a label and their positions
/// in the buffer.
///
PreviewLoader * preview_loader_;
+ /// If non zero, this buffer is a clone of existing buffer \p cloned_buffer_
+ /// This one is useful for preview detached in a thread.
+ Buffer const * cloned_buffer_;
+ ///
+ CloneList_ptr clone_list_;
+
+ ///
+ std::list<Buffer const *> include_list_;
+private:
+ /// So we can force access via the accessors.
+ mutable Buffer const * parent_buffer;
+
+ FileMonitorPtr file_monitor_;
+
+/// ints and bools are all listed last so as to avoid alignment issues
+public:
+ /// original format of loaded file
+ int file_format;
+
+ /// are we in the process of exporting this buffer?
+ mutable bool doing_export;
+
+ /// If there was an error when previewing, on the next preview we do
+ /// a fresh compile (e.g. in case the user installed a package that
+ /// was missing).
+ bool require_fresh_start_;
+
+ /// 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_;
+
+ /// is save needed?
+ mutable bool lyx_clean;
+
+ /// is autosave needed?
+ mutable bool bak_clean;
+
+ /// is this an unnamed file (New...)?
+ bool unnamed;
+
+ /// is this an internal bufffer?
+ bool internal_buffer;
+
+ /// buffer is r/o
+ bool read_only;
+
+ /** Set to true only when the file is fully loaded.
+ * Used to prevent the premature generation of previews
+ * and by the citation inset.
+ */
+ bool file_fully_loaded;
+
+ /// if the file was originally loaded from an older format, do
+ /// we need to back it up still?
+ bool need_format_backup;
+
+ /// Ignore the parent (e.g. when exporting a child standalone)?
+ bool ignore_parent;
+
+ /// This seem to change the way Buffer::getMacro() works
+ mutable bool macro_lock;
+
+ /// has been externally modified? Can be reset by the user.
+ mutable bool externally_modified_;
+
+ /// whether the bibinfo cache is valid
+ mutable bool bibinfo_cache_valid_;
+
+private:
+ int word_count_;
+ int char_count_;
+ int blank_count_;
+
+public:
/// This is here to force the test to be done whenever parent_buffer
/// is accessed.
Buffer const * parent() const
parent_buffer->invalidateBibinfoCache();
}
- /// If non zero, this buffer is a clone of existing buffer \p cloned_buffer_
- /// This one is useful for preview detached in a thread.
- Buffer const * cloned_buffer_;
- ///
- CloneList * clone_list_;
- /// are we in the process of exporting this buffer?
- mutable bool doing_export;
-
/// compute statistics
/// \p from initial position
/// \p to points to the end position
/// Notify or clear of external modification
void fileExternallyModified(bool exists);
- /// 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;
-
- int word_count_;
- int char_count_;
- int blank_count_;
-
- FileMonitorPtr file_monitor_;
};
Buffer::Impl::Impl(Buffer * owner, FileName const & file, bool readonly_,
Buffer const * cloned_buffer)
- : owner_(owner), lyx_clean(true), bak_clean(true), unnamed(false),
- internal_buffer(false), read_only(readonly_), filename(file),
- file_fully_loaded(false), file_format(LYX_FORMAT), need_format_backup(false),
- ignore_parent(false), toc_backend(owner), macro_lock(false),
- checksum_(0), wa_(nullptr), gui_(nullptr), undo_(*owner), bibinfo_cache_valid_(false),
- cite_labels_valid_(false), have_bibitems_(false), require_fresh_start_(false),
- inset(nullptr), preview_loader_(nullptr), cloned_buffer_(cloned_buffer),
- clone_list_(nullptr), doing_export(false),
- externally_modified_(false), parent_buffer(nullptr),
+ : owner_(owner), filename(file), toc_backend(owner), checksum_(0),
+ wa_(nullptr), gui_(nullptr), undo_(*owner), inset(nullptr),
+ preview_loader_(nullptr), cloned_buffer_(cloned_buffer),
+ clone_list_(nullptr), parent_buffer(nullptr), file_format(LYX_FORMAT),
+ doing_export(false), require_fresh_start_(false), cite_labels_valid_(false),
+ have_bibitems_(false), lyx_clean(true), bak_clean(true), unnamed(false),
+ internal_buffer(false), read_only(readonly_), file_fully_loaded(false),
+ need_format_backup(false), ignore_parent(false), macro_lock(false),
+ externally_modified_(false), bibinfo_cache_valid_(false),
word_count_(0), char_count_(0), blank_count_(0)
{
refreshFileMonitor();
// loop over children
for (auto const & p : d->children_positions) {
Buffer * child = const_cast<Buffer *>(p.first);
- if (d->clone_list_->erase(child))
- delete child;
+ if (d->clone_list_->erase(child))
+ delete child;
}
// if we're the master buffer, then we should get rid of the list
// of clones
// children still has a reference to this list. But we will try to
// continue, rather than shut down.
LATTEST(d->clone_list_->empty());
- list<CloneList *>::iterator it =
+ // The clone list itself is empty, but it's still referenced in our list
+ // of clones. So let's find it and remove it.
+ CloneStore::iterator it =
find(cloned_buffers.begin(), cloned_buffers.end(), d->clone_list_);
if (it == cloned_buffers.end()) {
// We will leak in this case, but it is safe to continue.
LATTEST(false);
- } else {
- delete(*it);
+ } else
cloned_buffers.erase(it);
- }
- delete d->clone_list_;
}
// FIXME Do we really need to do this right before we delete d?
// clear references to children in macro tables
Buffer * Buffer::cloneWithChildren() const
{
BufferMap bufmap;
- cloned_buffers.push_back(new CloneList);
- CloneList * clones = cloned_buffers.back();
+ cloned_buffers.emplace_back(new CloneList);
+ CloneList_ptr clones = cloned_buffers.back();
cloneWithChildren(bufmap, clones);
}
-void Buffer::cloneWithChildren(BufferMap & bufmap, CloneList * clones) const
+void Buffer::cloneWithChildren(BufferMap & bufmap, CloneList_ptr clones) const
{
// have we already been cloned?
if (bufmap.find(this) != bufmap.end())
Buffer * Buffer::cloneBufferOnly() const {
- cloned_buffers.push_back(new CloneList);
- CloneList * clones = cloned_buffers.back();
+ cloned_buffers.emplace_back(new CloneList);
+ CloneList_ptr clones = cloned_buffers.back();
Buffer * buffer_clone = new Buffer(fileName().absFileName(), false, this);
// The clone needs its own DocumentClass, since running updateBuffer() will
d->old_position = params().origin;
else
d->old_position = filePath();
+
+ if (!parent())
+ clearIncludeList();
+
bool const res = text().read(lex, errorList, d->inset);
d->old_position.clear();
// This is only set once per document (in master)
if (!runparams.is_child) {
runparams.use_polyglossia = features.usePolyglossia();
+ runparams.use_hyperref = features.isRequired("hyperref");
runparams.use_CJK = features.mustProvide("CJK");
}
LYXERR(Debug::LATEX, " Buffer validation done.");
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;
if (!features.runparams().is_child)
params().validate(features);
+ if (!parent())
+ clearIncludeList();
+
for (Paragraph const & p : paragraphs())
p.validate(features);
void Buffer::collectBibKeys(FileNameList & checkedFiles) const
{
+ if (!parent())
+ clearIncludeList();
+
for (InsetIterator it = inset_iterator_begin(inset()); it; ++it) {
it->collectBibKeys(it, checkedFiles);
if (it->lyxCode() == BIBITEM_CODE) {
// get buffer of external file
InsetInclude const & ins =
static_cast<InsetInclude const &>(*it);
- Buffer * child = ins.getChildBuffer();
+ Buffer * child = ins.loadIfNeeded();
if (!child)
continue;
child->dispatch(func, dr);
LASSERT(from, return);
LASSERT(to, return);
- for_each(par_iterator_begin(),
- par_iterator_end(),
- bind(&Paragraph::changeLanguage, _1, params(), from, to));
+ ParIterator it = par_iterator_begin();
+ ParIterator eit = par_iterator_end();
+ for (; it != eit; ++it)
+ it->changeLanguage(params(), from, to);
}
}
-docstring const Buffer::B_(string const & l10n) const
+docstring Buffer::B_(string const & l10n) const
{
return params().B_(l10n);
}
enc = params().bibFileEncoding(utf8input);
bool recorded = false;
- for (pair<docstring, string> pe : res) {
+ for (auto const & pe : res) {
if (pe.first == path) {
recorded = true;
break;
void Buffer::setParent(Buffer const * buffer)
{
- // Avoids recursive include.
- d->setParent(buffer == this ? nullptr : buffer);
+ // We need to do some work here to avoid recursive parent structures.
+ // This is the easy case.
+ if (buffer == this) {
+ LYXERR0("Ignoring attempt to set self as parent in\n" << fileName());
+ return;
+ }
+ // Now we check parents going upward, to make sure that IF we set the
+ // parent as requested, we would not generate a recursive include.
+ set<Buffer const *> sb;
+ Buffer const * b = buffer;
+ bool found_recursion = false;
+ while (b) {
+ if (sb.find(b) != sb.end()) {
+ found_recursion = true;
+ break;
+ }
+ sb.insert(b);
+ b = b->parent();
+ }
+
+ if (found_recursion) {
+ LYXERR0("Ignoring attempt to set parent of\n" <<
+ fileName() <<
+ "\nto " <<
+ buffer->fileName() <<
+ "\nbecause that would create a recursive inclusion.");
+ return;
+ }
+
+ // We should be safe now.
+ d->setParent(buffer);
updateMacros();
}
Buffer const * Buffer::masterBuffer() const
{
- // FIXME Should be make sure we are not in some kind
- // of recursive include? A -> B -> A will crash this.
Buffer const * const pbuf = d->parent();
if (!pbuf)
return this;
InsetInclude const & incinset =
static_cast<InsetInclude const &>(*insit.inset);
macro_lock = true;
- Buffer * child = incinset.getChildBuffer();
+ Buffer * child = incinset.loadIfNeeded();
macro_lock = false;
if (!child)
continue;
// get buffer of external file
InsetInclude const & ins =
static_cast<InsetInclude const &>(*it);
- Buffer * child = ins.getChildBuffer();
+ Buffer * child = ins.loadIfNeeded();
if (!child)
continue;
child->getUsedBranches(result, true);
InsetLabel const * Buffer::insetLabel(docstring const & label,
bool const active) const
{
- for (auto & rc : masterBuffer()->d->label_cache_) {
+ for (auto const & rc : masterBuffer()->d->label_cache_) {
if (rc.label == label && (rc.active || !active))
return rc.inset;
}
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
}
-
-namespace {
-
-class AutoSaveBuffer : public ForkedProcess {
-public:
- ///
- AutoSaveBuffer(Buffer const & buffer, FileName const & fname)
- : buffer_(buffer), fname_(fname) {}
- ///
- virtual shared_ptr<ForkedProcess> clone() const
- {
- return make_shared<AutoSaveBuffer>(*this);
- }
- ///
- int start()
- {
- command_ = to_utf8(bformat(_("Auto-saving %1$s"),
- from_utf8(fname_.absFileName())));
- return run(DontWait);
- }
-private:
- ///
- virtual int generateChild();
- ///
- Buffer const & buffer_;
- FileName fname_;
-};
-
-
-int AutoSaveBuffer::generateChild()
-{
-#if defined(__APPLE__)
- /* FIXME fork() is not usable for autosave on Mac OS X 10.6 (snow leopard)
- * We should use something else like threads.
- *
- * Since I do not know how to determine at run time what is the OS X
- * version, I just disable forking altogether for now (JMarc)
- */
- pid_t const pid = -1;
-#else
- // tmp_ret will be located (usually) in /tmp
- // will that be a problem?
- // Note that this calls ForkedCalls::fork(), so it's
- // ok cross-platform.
- pid_t const pid = fork();
- // If you want to debug the autosave
- // you should set pid to -1, and comment out the fork.
- if (pid != 0 && pid != -1)
- return pid;
-#endif
-
- // pid = -1 signifies that lyx was unable
- // to fork. But we will do the save
- // anyway.
- bool failed = false;
- TempFile tempfile("lyxautoXXXXXX.lyx");
- tempfile.setAutoRemove(false);
- FileName const tmp_ret = tempfile.name();
- if (!tmp_ret.empty()) {
- if (!buffer_.writeFile(tmp_ret))
- failed = true;
- else if (!tmp_ret.moveTo(fname_))
- failed = true;
- } else
- failed = true;
-
- if (failed) {
- // failed to write/rename tmp_ret so try writing direct
- if (!buffer_.writeFile(fname_)) {
- // It is dangerous to do this in the child,
- // but safe in the parent, so...
- if (pid == -1) // emit message signal.
- buffer_.message(_("Autosave failed!"));
- }
- }
-
- if (pid == 0) // we are the child so...
- _exit(0);
-
- return pid;
-}
-
-} // namespace
-
-
FileName Buffer::getEmergencyFileName() const
{
return FileName(d->filename.absFileName() + ".emergency");
string & result_file) const
{
bool const update_unincluded =
- params().maintain_unincluded_children
+ params().maintain_unincluded_children != BufferParams::CM_None
&& !params().getIncludedChildren().empty();
// (1) export with all included children (omit \includeonly)
string const ext = theFormats().extension(format);
FileName const tmp_result_file(changeExtension(filename, ext));
Converters::RetVal const retval =
- converters.convert(this, FileName(filename), tmp_result_file,
- FileName(absFileName()), backend_format, format, error_list);
+ converters.convert(this, FileName(filename), tmp_result_file,
+ FileName(absFileName()), backend_format, format,
+ error_list, Converters::none, includeall);
if (retval == Converters::KILLED)
return ExportCancel;
bool success = (retval == Converters::SUCCESS);
Buffer::ExportStatus Buffer::preview(string const & format) const
{
bool const update_unincluded =
- params().maintain_unincluded_children
+ params().maintain_unincluded_children != BufferParams::CM_None
&& !params().getIncludedChildren().empty();
return preview(format, update_unincluded);
}
// do the real work
ParIterator parit = cbuf.par_iterator_begin();
+ if (scope == UpdateMaster)
+ clearIncludeList();
updateBuffer(parit, utype);
// If this document has siblings, then update the TocBackend later. The
}
d->cite_labels_valid_ = true;
/// FIXME: Perf
+ clearIncludeList();
cbuf.tocBackend().update(true, utype);
if (scope == UpdateMaster)
cbuf.structureChanged();
}
-void Buffer::updateBuffer(ParIterator & parit, UpdateType utype) const
+void Buffer::updateBuffer(ParIterator & parit, UpdateType utype, bool const deleted) const
{
+ pushIncludedBuffer(this);
// LASSERT: Is it safe to continue here, or should we just return?
LASSERT(parit.pit() == 0, /**/);
// now the insets
for (auto const & insit : parit->insetList()) {
parit.pos() = insit.pos;
- insit.inset->updateBuffer(parit, utype);
+ insit.inset->updateBuffer(parit, utype, deleted || parit->isDeleted(insit.pos));
changed |= insit.inset->isChanged();
}
// set change indicator for the inset (or the cell that the iterator
// points to, if applicable).
parit.text()->inset().isChanged(changed);
+ popIncludedBuffer();
}
}
+void Buffer::pushIncludedBuffer(Buffer const * buf) const
+{
+ masterBuffer()->d->include_list_.push_back(buf);
+ if (lyxerr.debugging(Debug::FILES)) {
+ LYXERR0("Pushed. Stack now:");
+ if (masterBuffer()->d->include_list_.empty())
+ LYXERR0("EMPTY!");
+ else
+ for (auto const & b : masterBuffer()->d->include_list_)
+ LYXERR0(b->fileName());
+ }
+}
+
+
+void Buffer::popIncludedBuffer() const
+{
+ masterBuffer()->d->include_list_.pop_back();
+ if (lyxerr.debugging(Debug::FILES)) {
+ LYXERR0("Popped. Stack now:");
+ if (masterBuffer()->d->include_list_.empty())
+ LYXERR0("EMPTY!");
+ else
+ for (auto const & b : masterBuffer()->d->include_list_)
+ LYXERR0(b->fileName());
+ }
+}
+
+
+bool Buffer::isBufferIncluded(Buffer const * buf) const
+{
+ if (!buf)
+ return false;
+ if (lyxerr.debugging(Debug::FILES)) {
+ LYXERR0("Checking for " << buf->fileName() << ". Stack now:");
+ if (masterBuffer()->d->include_list_.empty())
+ LYXERR0("EMPTY!");
+ else
+ for (auto const & b : masterBuffer()->d->include_list_)
+ LYXERR0(b->fileName());
+ }
+ list<Buffer const *> const & blist = masterBuffer()->d->include_list_;
+ return find(blist.begin(), blist.end(), buf) != blist.end();
+}
+
+
+void Buffer::clearIncludeList() const
+{
+ LYXERR(Debug::FILES, "Clearing include list for " << fileName());
+ d->include_list_.clear();
+}
+
} // namespace lyx