///
mutable TocBackend toc_backend;
- /// macro table
- typedef map<unsigned int, MacroData, greater<int> > PositionToMacroMap;
- typedef map<docstring, PositionToMacroMap> NameToPositionMacroMap;
- NameToPositionMacroMap macros;
+ /// macro tables
+ typedef pair<DocIterator, MacroData> ScopeMacro;
+ typedef map<DocIterator, ScopeMacro> PositionScopeMacroMap;
+ typedef map<docstring, PositionScopeMacroMap> NamePositionScopeMacroMap;
+ NamePositionScopeMacroMap macros;
+ bool macro_lock;
+
+ /// positions of child buffers in the buffer
+ typedef map<Buffer const * const, DocIterator> BufferPositionMap;
+ typedef pair<DocIterator, Buffer const *> ScopeBuffer;
+ typedef map<DocIterator, ScopeBuffer> PositionScopeBufferMap;
+ BufferPositionMap children_positions;
+ PositionScopeBufferMap position_to_children;
/// Container for all sort of Buffer dependant errors.
map<string, ErrorList> errorLists;
Buffer::Impl::Impl(Buffer & parent, FileName const & file, bool readonly_)
: parent_buffer(0), lyx_clean(true), bak_clean(true), unnamed(false),
read_only(readonly_), filename(file), file_fully_loaded(false),
- inset(params), toc_backend(&parent), embedded_files(&parent),
- timestamp_(0), checksum_(0), wa_(0), undo_(parent)
+ inset(params), toc_backend(&parent), macro_lock(false),
+ embedded_files(&parent), timestamp_(0), checksum_(0), wa_(0),
+ undo_(parent)
{
temppath = createBufferTmpDir();
inset.setAutoBreakRows(true);
: d(new Impl(*this, FileName(file), readonly)), gui_(0)
{
LYXERR(Debug::INFO, "Buffer::Buffer()");
+
+ d->inset.getText(0)->setMacrocontextPosition(par_iterator_begin());
}
gui_ = 0;
Buffer const * master = masterBuffer();
- if (master != this && use_gui)
+ if (master != this && use_gui) {
// We are closing buf which was a child document so we
// must update the labels and section numbering of its master
// Buffer.
updateLabels(*master);
+ master->updateMacros();
+ }
resetChildDocuments(false);
text().paragraphs().end(),
bind(&Paragraph::setInsetOwner, _1, &inset()));
+ updateMacros();
+ updateMacroInstances();
return res;
}
LYXERR(Debug::INFO, "preamble finished, now the body.");
+ // fold macros if possible, still with parent buffer as the
+ // macros will be put in the prefix anyway.
+ updateMacros();
+ updateMacroInstances();
+
// if we are doing a real file with body, even if this is the
// child of some other buffer, let's cut the link here.
// This happens for example if only a child document is printed.
Buffer const * save_parent = 0;
if (output_preamble) {
+ // output the macros visible for this buffer
+ writeParentMacros(os);
+
save_parent = d->parent_buffer;
d->parent_buffer = 0;
}
latexParagraphs(*this, paragraphs(), os, d->texrow, runparams);
// Restore the parenthood if needed
- if (output_preamble)
+ if (output_preamble) {
d->parent_buffer = save_parent;
+ // restore macros with correct parent buffer (especially
+ // important for the redefinition flag which depends on the
+ // parent)
+ updateMacros();
+ }
+
// add this just in case after all the paragraphs
os << endl;
d->texrow.newline();
{
// Avoids recursive include.
d->parent_buffer = buffer == this ? 0 : buffer;
+ updateMacros();
}
}
-bool Buffer::hasMacro(docstring const & name, Paragraph const & par) const
+template<typename M>
+typename M::iterator greatest_below(M & m, typename M::key_type const & x)
{
- Impl::PositionToMacroMap::iterator it;
- it = d->macros[name].upper_bound(par.macrocontextPosition());
- if (it != d->macros[name].end())
- return true;
+ if (m.empty())
+ return m.end();
- // If there is a master buffer, query that
- Buffer const * master = masterBuffer();
- if (master && master != this)
- return master->hasMacro(name);
+ typename M::iterator it = m.lower_bound(x);
+ if (it == m.begin())
+ return m.end();
- return MacroTable::globalMacros().has(name);
+ it--;
+ return it;
}
-bool Buffer::hasMacro(docstring const & name) const
+MacroData const * Buffer::getBufferMacro(docstring const & name,
+ DocIterator const & pos) const
{
- if( !d->macros[name].empty() )
- return true;
+ LYXERR(Debug::DEBUG, "Searching for " << to_ascii(name) << " at " << pos);
- // If there is a master buffer, query that
- Buffer const * master = masterBuffer();
- if (master && master != this)
- return master->hasMacro(name);
+ // if paragraphs have no macro context set, pos will be empty
+ if (pos.empty())
+ return 0;
- return MacroTable::globalMacros().has(name);
+ // we haven't found anything yet
+ DocIterator bestPos = par_iterator_begin();
+ MacroData const * bestData = 0;
+
+ // find macro definitions for name
+ Impl::NamePositionScopeMacroMap::iterator nameIt
+ = d->macros.find(name);
+ if (nameIt != d->macros.end()) {
+ // find last definition in front of pos or at pos itself
+ Impl::PositionScopeMacroMap::const_iterator it
+ = greatest_below(nameIt->second, pos);
+ if (it != nameIt->second.end()) {
+ while (true) {
+ // scope ends behind pos?
+ if (pos < it->second.first) {
+ // Looks good, remember this. If there
+ // is no external macro behind this,
+ // we found the right one already.
+ bestPos = it->first;
+ bestData = &it->second.second;
+ break;
+ }
+
+ // try previous macro if there is one
+ if (it == nameIt->second.begin())
+ break;
+ it--;
+ }
+ }
+ }
+
+ // find macros in included files
+ Impl::PositionScopeBufferMap::const_iterator it
+ = greatest_below(d->position_to_children, pos);
+ if (it != d->position_to_children.end()) {
+ while (true) {
+ // do we know something better (i.e. later) already?
+ if (it->first < bestPos )
+ break;
+
+ // scope ends behind pos?
+ if (pos < it->second.first) {
+ // look for macro in external file
+ d->macro_lock = true;
+ MacroData const * data
+ = it->second.second->getMacro(name, false);
+ d->macro_lock = false;
+ if (data) {
+ bestPos = it->first;
+ bestData = data;
+ break;
+ }
+ }
+
+ // try previous file if there is one
+ if (it == d->position_to_children.begin())
+ break;
+ --it;
+ }
+ }
+
+ // return the best macro we have found
+ return bestData;
}
-MacroData const & Buffer::getMacro(docstring const & name,
- Paragraph const & par) const
+MacroData const * Buffer::getMacro(docstring const & name,
+ DocIterator const & pos, bool global) const
{
- Impl::PositionToMacroMap::iterator it;
- it = d->macros[name].upper_bound(par.macrocontextPosition());
- if( it != d->macros[name].end() )
- return it->second;
+ if (d->macro_lock)
+ return 0;
+
+ // query buffer macros
+ MacroData const * data = getBufferMacro(name, pos);
+ if (data != 0)
+ return data;
// If there is a master buffer, query that
- Buffer const * master = masterBuffer();
- if (master && master != this)
- return master->getMacro(name);
+ if (d->parent_buffer) {
+ d->macro_lock = true;
+ MacroData const * macro
+ = d->parent_buffer->getMacro(name, *this, false);
+ d->macro_lock = false;
+ if (macro)
+ return macro;
+ }
+
+ if (global) {
+ data = MacroTable::globalMacros().get(name);
+ if (data != 0)
+ return data;
+ }
- return MacroTable::globalMacros().get(name);
+ return 0;
}
-MacroData const & Buffer::getMacro(docstring const & name) const
+MacroData const * Buffer::getMacro(docstring const & name, bool global) const
{
- Impl::PositionToMacroMap::iterator it;
- it = d->macros[name].begin();
- if( it != d->macros[name].end() )
- return it->second;
+ // set scope end behind the last paragraph
+ DocIterator scope = par_iterator_begin();
+ scope.pit() = scope.lastpit() + 1;
- // If there is a master buffer, query that
- Buffer const * master = masterBuffer();
- if (master && master != this)
- return master->getMacro(name);
+ return getMacro(name, scope, global);
+}
- return MacroTable::globalMacros().get(name);
+
+MacroData const * Buffer::getMacro(docstring const & name, Buffer const & child, bool global) const
+{
+ // look where the child buffer is included first
+ Impl::BufferPositionMap::iterator it
+ = d->children_positions.find(&child);
+ if (it == d->children_positions.end())
+ return 0;
+
+ // check for macros at the inclusion position
+ return getMacro(name, it->second, global);
}
-void Buffer::updateMacros()
+void Buffer::updateEnvironmentMacros(DocIterator & it,
+ pit_type lastpit,
+ DocIterator & scope) const
{
- // start with empty table
- d->macros = Impl::NameToPositionMacroMap();
-
- // Iterate over buffer
- ParagraphList & pars = text().paragraphs();
- for (size_t i = 0, n = pars.size(); i != n; ++i) {
- // set position again
- pars[i].setMacrocontextPosition(i);
-
- //lyxerr << "searching main par " << i
- // << " for macro definitions" << endl;
- InsetList const & insets = pars[i].insetList();
- InsetList::const_iterator it = insets.begin();
+ Paragraph & par = it.paragraph();
+ depth_type depth
+ = par.params().depth();
+ Length const & leftIndent
+ = par.params().leftIndent();
+
+ // look for macros in each paragraph
+ while (it.pit() <= lastpit) {
+ Paragraph & par = it.paragraph();
+
+ // increased depth?
+ if ((par.params().depth() > depth
+ || par.params().leftIndent() != leftIndent)
+ && par.layout()->isEnvironment()) {
+ updateBlockMacros(it, scope);
+ continue;
+ }
+
+ // iterate over the insets of the current paragraph
+ InsetList const & insets = par.insetList();
+ InsetList::const_iterator iit = insets.begin();
InsetList::const_iterator end = insets.end();
- for ( ; it != end; ++it) {
- if (it->inset->lyxCode() != MATHMACRO_CODE)
+ for (; iit != end; ++iit) {
+ it.pos() = iit->pos;
+
+ // is it a nested text inset?
+ if (iit->inset->asInsetText()) {
+ // Inset needs its own scope?
+ InsetText const * itext
+ = iit->inset->asInsetText();
+ bool newScope = itext->isMacroScope(*this);
+
+ // scope which ends just behind die inset
+ DocIterator insetScope = it;
+ insetScope.pos()++;
+
+ // collect macros in inset
+ it.push_back(CursorSlice(*iit->inset));
+ updateInsetMacros(it, newScope ? insetScope : scope);
+ it.pop_back();
+ continue;
+ }
+
+ // is it an external file?
+ if (iit->inset->lyxCode() == INCLUDE_CODE) {
+ // get buffer of external file
+ InsetCommand const & inset
+ = static_cast<InsetCommand const &>(*iit->inset);
+ InsetCommandParams const & ip = inset.params();
+ d->macro_lock = true;
+ Buffer * child = loadIfNeeded(*this, ip);
+ d->macro_lock = false;
+ if (!child)
+ continue;
+
+ // register it, but only when it is
+ // included first in the buffer
+ if (d->children_positions.find(child)
+ == d->children_positions.end())
+ d->children_positions[child] = it;
+
+ // register child with its scope
+ d->position_to_children[it] = Impl::ScopeBuffer(scope, child);
+
+ continue;
+ }
+
+ if (iit->inset->lyxCode() != MATHMACRO_CODE)
continue;
// get macro data
- MathMacroTemplate const & macroTemplate
- = static_cast<MathMacroTemplate const &>(*it->inset);
+ MathMacroTemplate & macroTemplate
+ = static_cast<MathMacroTemplate &>(*iit->inset);
+ MacroContext mc(*this, it);
+ macroTemplate.updateToContext(mc);
// valid?
- if (macroTemplate.validMacro()) {
- MacroData macro = macroTemplate.asMacroData();
+ bool valid = macroTemplate.validMacro();
+ // FIXME: Should be fixNameAndCheckIfValid() in fact,
+ // then the BufferView's cursor will be invalid in
+ // some cases which leads to crashes.
+ if (!valid)
+ continue;
- // redefinition?
- // call hasMacro here instead of directly querying mc to
- // also take the master document into consideration
- macro.setRedefinition(hasMacro(macroTemplate.name()));
+ // register macro
+ d->macros[macroTemplate.name()][it]
+ = Impl::ScopeMacro(scope, MacroData(*this, it));
+ }
- // register macro (possibly overwrite the previous one of this paragraph)
- d->macros[macroTemplate.name()][i] = macro;
- }
+ // next paragraph
+ it.pit()++;
+ it.pos() = 0;
+ }
+}
+
+
+void Buffer::updateBlockMacros(DocIterator & it, DocIterator & scope) const
+{
+ Paragraph & par = it.paragraph();
+
+ // set scope for macros in this paragraph:
+ // * either the "old" outer scope
+ // * or the scope ending after the environment
+ if (par.layout()->isEnvironment()) {
+ // find end of environment block,
+ DocIterator envEnd = it;
+ pit_type n = it.lastpit() + 1;
+ depth_type depth = par.params().depth();
+ Length const & length = par.params().leftIndent();
+ // looping through the paragraph, basically until
+ // the layout changes or the depth gets smaller.
+ // (the logic of output_latex.cpp's TeXEnvironment)
+ do {
+ envEnd.pit()++;
+ if (envEnd.pit() == n)
+ break;
+ } while (par.layout() == envEnd.paragraph().layout()
+ || depth < envEnd.paragraph().params().depth()
+ || length != envEnd.paragraph().params().leftIndent());
+
+ // collect macros from environment block
+ updateEnvironmentMacros(it, envEnd.pit() - 1, envEnd);
+ } else {
+ // collect macros from paragraph
+ updateEnvironmentMacros(it, it.pit(), scope);
+ }
+}
+
+
+void Buffer::updateInsetMacros(DocIterator & it, DocIterator & scope) const
+{
+ // look for macros in each paragraph
+ pit_type n = it.lastpit() + 1;
+ while (it.pit() < n)
+ updateBlockMacros(it, scope);
+}
+
+
+void Buffer::updateMacros() const
+{
+ if (d->macro_lock)
+ return;
+
+ LYXERR(Debug::DEBUG, "updateMacro of " << d->filename.onlyFileName());
+
+ // start with empty table
+ d->macros.clear();
+ d->children_positions.clear();
+ d->position_to_children.clear();
+
+ // Iterate over buffer, starting with first paragraph
+ // The scope must be bigger than any lookup DocIterator
+ // later. For the global lookup, lastpit+1 is used, hence
+ // we use lastpit+2 here.
+ DocIterator it = par_iterator_begin();
+ DocIterator outerScope = it;
+ outerScope.pit() = outerScope.lastpit() + 2;
+ updateInsetMacros(it, outerScope);
+}
+
+
+void Buffer::updateMacroInstances() const
+{
+ LYXERR(Debug::DEBUG, "updateMacroInstances for " << d->filename.onlyFileName());
+ ParIterator it = par_iterator_begin();
+ ParIterator end = par_iterator_end();
+ for (; it != end; it.forwardPos()) {
+ // look for MathData cells in InsetMathNest insets
+ Inset * inset = it.nextInset();
+ if (!inset)
+ continue;
+
+ InsetMath * minset = inset->asInsetMath();
+ if (!minset)
+ continue;
+
+ // update macro in all cells of the InsetMathNest
+ DocIterator::idx_type n = minset->nargs();
+ MacroContext mc = MacroContext(*this, it);
+ for (DocIterator::idx_type i = 0; i < n; ++i) {
+ MathData & data = minset->cell(i);
+ data.updateMacros(0, mc);
}
}
}
+void Buffer::listMacroNames(MacroNameSet & macros) const
+{
+ if (d->macro_lock)
+ return;
+
+ d->macro_lock = true;
+
+ // loop over macro names
+ Impl::NamePositionScopeMacroMap::iterator nameIt
+ = d->macros.begin();
+ Impl::NamePositionScopeMacroMap::iterator nameEnd
+ = d->macros.end();
+ for (; nameIt != nameEnd; ++nameIt)
+ macros.insert(nameIt->first);
+
+ // loop over children
+ Impl::BufferPositionMap::iterator it
+ = d->children_positions.begin();
+ Impl::BufferPositionMap::iterator end
+ = d->children_positions.end();
+ for (; it != end; ++it)
+ it->first->listMacroNames(macros);
+
+ // call parent
+ if (d->parent_buffer)
+ d->parent_buffer->listMacroNames(macros);
+
+ d->macro_lock = false;
+}
+
+
+void Buffer::writeParentMacros(odocstream & os) const
+{
+ if (!d->parent_buffer)
+ return;
+
+ // collect macro names
+ MacroNameSet names;
+ d->parent_buffer->listMacroNames(names);
+
+ // resolve and output them
+ MacroNameSet::iterator it = names.begin();
+ MacroNameSet::iterator end = names.end();
+ for (; it != end; ++it) {
+ // defined?
+ MacroData const * data =
+ d->parent_buffer->getMacro(*it, *this, false);
+ if (data)
+ data->write(os, true);
+ }
+}
+
+
void Buffer::changeRefsIfUnique(docstring const & from, docstring const & to,
InsetCode code)
{
if (use_gui && masterBuffer() == this)
updateLabels(*this);
+
+ // clear references to children in macro tables
+ d->children_positions.clear();
+ d->position_to_children.clear();
}
if (use_gui && masterBuffer() == this)
updateLabels(*this);
+
+ updateMacros();
}
filename = changeExtension(filename,
formats.extension(backend_format));
+ // fix macros
+ updateMacroInstances();
+
// Plain text backend
if (backend_format == "text")
writePlaintextFile(*this, FileName(filename), runparams);
#include "support/strfwd.h"
#include "support/types.h"
+#include <set>
#include <string>
#include <vector>
class BufferParams;
class EmbeddedFiles;
+class DocIterator;
class ErrorItem;
class ErrorList;
class FuncRequest;
//
// Macro handling
//
- /// Collect macros in paragraphs
- void updateMacros();
- /// Look for macro defined before par (or in the master buffer)
- bool hasMacro(docstring const & name, Paragraph const & par) const;
- /// Look for macro defined anywhere in the buffer (or in the master buffer)
- bool hasMacro(docstring const & name) const;
- /// Return macro defined before par (or in the master buffer)
- MacroData const & getMacro(docstring const & name, Paragraph const & par) const;
+ /// Collect macro definitions in paragraphs
+ void updateMacros() const;
+ /// Iterate through the whole buffer and try to resolve macros
+ void updateMacroInstances() const;
+
+ typedef std::set<docstring> MacroNameSet;
+ /// List macro names of this buffer. the parent and the children
+ void listMacroNames(MacroNameSet & macros) const;
+ /// Write out all macros somewhere defined in the parent,
+ /// its parents and its children, which are visible at the beginning
+ /// of this buffer
+ void writeParentMacros(odocstream & os) const;
+
+ /// Return macro defined before pos (or in the master buffer)
+ MacroData const * getMacro(docstring const & name, DocIterator const & pos, bool global = true) const;
/// Return macro defined anywhere in the buffer (or in the master buffer)
- MacroData const & getMacro(docstring const & name) const;
+ MacroData const * getMacro(docstring const & name, bool global = true) const;
+ /// Return macro defined before the inclusion of the child
+ MacroData const * getMacro(docstring const & name, Buffer const & child, bool global = true) const;
/// Replace the inset contents for insets which InsetCode is equal
/// to the passed \p inset_code.
std::vector<Format const *> exportableFormats(bool only_viewable) const;
private:
- ///
+ /// search for macro in local (buffer) table or in children
+ MacroData const * getBufferMacro(docstring const & name,
+ DocIterator const & pos) const;
+ /** Update macro table in the whole text inset
+ \param it at the start of the text inset)
+ */
+ void updateInsetMacros(DocIterator & it,
+ DocIterator & scope) const;
+ /** Update macro table for paragraphs until \c lastpit
+ \param it in some text inset
+ \param lastpit last processed paragraph
+ */
+ void updateEnvironmentMacros(DocIterator & it,
+ pit_type lastpit,
+ DocIterator & scope) const;
+ /** Update macro table for one paragraph block with
+ same layout and depth, until \c lastpit
+ \param it in some text inset
+ \param lastpit last processed paragraph
+ */
+ void updateBlockMacros(DocIterator & it,
+ DocIterator & scope) const;
+
+ ///
bool readFileHelper(support::FileName const & s);
///
std::vector<std::string> backends() const;
<< ", singlepar = " << (flags & Update::SinglePar)
<< "] buffer: " << &buffer_);
- // Update macro store
- if (!(cursor().inMathed() && cursor().inMacroMode()))
- buffer_.updateMacros();
+ buffer_.updateMacros();
// Now do the first drawing step if needed. This consists on updating
// the CoordCache in updateMetrics().
}
-bool operator<(DocIterator const & p, DocIterator const & q)
-{
- size_t depth = min(p.depth(), q.depth());
- for (size_t i = 0 ; i < depth ; ++i) {
- if (p[i] != q[i])
- return p[i] < q[i];
- }
- return p.depth() < q.depth();
-}
-
-
-bool operator>(DocIterator const & p, DocIterator const & q)
-{
- return q < p;
-}
-
-
-bool operator<=(DocIterator const & p, DocIterator const & q)
-{
- return !(q < p);
-}
-
-
///////////////////////////////////////////////////////
StableDocIterator::StableDocIterator(DocIterator const & dit)
}
+inline
+bool operator<(DocIterator const & p, DocIterator const & q)
+{
+ size_t depth = std::min(p.depth(), q.depth());
+ for (size_t i = 0 ; i < depth ; ++i) {
+ if (p[i] != q[i])
+ return p[i] < q[i];
+ }
+ return p.depth() < q.depth();
+}
+
+
+inline
+bool operator>(DocIterator const & p, DocIterator const & q)
+{
+ return q < p;
+}
+
+
+inline
+bool operator<=(DocIterator const & p, DocIterator const & q)
+{
+ return !(q < p);
+}
+
+
// The difference to a ('non stable') DocIterator is the removed
// (overwritten by 0...) part of the CursorSlice data items. So this thing
// is suitable for external storage, but not for iteration as such.
if (flag.enabled()
&& !lyxaction.funcHasFlag(action, LyXAction::NoBuffer)
&& !lyxaction.funcHasFlag(action, LyXAction::ReadOnly))
- lyx_view_->buffer()->markDirty();
+ lyx_view_->buffer()->markDirty();
//Do we have a selection?
theSelection().haveSelection(view()->cursor().selection());
///
ParagraphParameters params_;
- /// position of the paragraph in the buffer. Only macros from
- /// paragraphs strictly smaller are visible in this paragraph
- unsigned int macrocontext_position_;
-
/// for recording and looking up changes
Changes changes_;
: owner_(owner), inset_owner_(0), begin_of_body_(0)
{
id_ = paragraph_id++;
- macrocontext_position_ = 0;
text_.reserve(100);
}
}
-unsigned int Paragraph::macrocontextPosition() const
-{
- return d->macrocontext_position_;
-}
-
-
-void Paragraph::setMacrocontextPosition(unsigned int pos)
-{
- d->macrocontext_position_ = pos;
-}
-
-
void Paragraph::checkAuthors(AuthorList const & authorList)
{
d->changes_.checkAuthors(authorList);
/// was previously past that position. Return 0 otherwise.
int checkBiblio(bool track_changes);
- /// To resolve macros properly the paragraphs are numbered.
- /// Every macro definition is stored with its paragraph number
- /// as well. Only those macros with a smaller number become
- /// visible in a paragraph (plus those in the same paragraph, but
- /// in an earlier inset.
- unsigned int macrocontextPosition() const;
- ///
- void setMacrocontextPosition(unsigned int pos);
-
/// For each author, set 'used' to true if there is a change
/// by this author in the paragraph.
void checkAuthors(AuthorList const & authorList);
}
+DocIterator Text::macrocontextPosition() const
+{
+ return macrocontext_position_;
+}
+
+
+void Text::setMacrocontextPosition(DocIterator const & pos)
+{
+ macrocontext_position_ = pos;
+}
+
+
} // namespace lyx
#ifndef TEXT_H
#define TEXT_H
+#include "DocIterator.h"
#include "ParagraphList.h"
namespace lyx {
/// from \first to \last paragraph
void deleteEmptyParagraphMechanism(pit_type first, pit_type last, bool trackChanges);
+ /// To resolve macros properly the texts get their DocIterator.
+ /// Every macro definition is stored with its DocIterator
+ /// as well. Only those macros with a smaller iterator become
+ /// visible in a paragraph.
+ DocIterator macrocontextPosition() const;
+ ///
+ void setMacrocontextPosition(DocIterator const & pos);
+
public:
///
ParagraphList pars_;
/// \param asParagraphs whether to paste as paragraphs or as lines
void pasteString(Cursor & cur, docstring const & str,
bool asParagraphs);
+
+ /// position of the text in the buffer.
+ DocIterator macrocontext_position_;
};
} // namespace lyx
string const s1 = token(s, ' ', 1);
int const nargs = s1.empty() ? 0 : convert<int>(s1);
string const s2 = token(s, ' ', 2);
- string const type = s2.empty() ? "newcommand" : s2;
- cur.insert(new MathMacroTemplate(from_utf8(token(s, ' ', 0)), nargs, false, from_utf8(type)));
+ MacroType type = MacroTypeNewcommand;
+ if (s2 == "def")
+ type = MacroTypeDef;
+ cur.insert(new MathMacroTemplate(from_utf8(token(s, ' ', 0)), nargs, false, type));
//cur.nextInset()->edit(cur, true);
}
break;
// so better to calculate that once here.
int const right_margin = rightMargin(pm);
+ // iterator pointing to paragraph to resolve macros
+ DocIterator parPos = text_->macrocontextPosition();
+ if (!parPos.empty())
+ parPos.pit() = pit;
+
// redo insets
// FIXME: We should always use getFont(), see documentation of
// noFontChange() in Inset.h.
Font const bufferfont = buffer.params().getFont();
- MacroContext mc(buffer, par);
InsetList::const_iterator ii = par.insetList().begin();
InsetList::const_iterator iend = par.insetList().end();
for (; ii != iend; ++ii) {
- // the macro must come here, _before_ the metric call, because
- // the macro should see itself to detect recursions. To find out
- // whether the macro definition is a redefinition it will look
- // at the MacroData::redefinition_. So it doesn't get confused
- // by the already existing macro definition of itself in the
- // macro context.
- if (ii->inset->lyxCode() == MATHMACRO_CODE) {
- // get macro data
- MathMacroTemplate const & macroTemplate
- = static_cast<MathMacroTemplate const &>(*ii->inset);
-
- // valid?
- if (macroTemplate.validMacro()) {
- MacroData macro = macroTemplate.asMacroData();
-
- // redefinition?
- macro.setRedefinition(mc.has(macroTemplate.name()));
-
- // register macro (possibly overwrite the previous one of this paragraph)
- mc.insert(macroTemplate.name(), macro);
- }
+ // position already initialized?
+ if (!parPos.empty()) {
+ parPos.pos() = ii->pos;
+
+ // A macro template would normally not be visible
+ // by itself. But the tex macro semantics allow
+ // recursion, so we artifically take the context
+ // after the macro template to simulate this.
+ if (ii->inset->lyxCode() == MATHMACRO_CODE)
+ parPos.pos()++;
}
// do the metric calculation
- right_margin;
Font const & font = ii->inset->noFontChange() ?
bufferfont : getDisplayFont(pit, ii->pos);
+ MacroContext mc(buffer, parPos);
MetricsInfo mi(bv_, font.fontInfo(), w, mc);
ii->inset->metrics(mi, dim);
Dimension const old_dim = pm.insetDimension(ii->inset);
{
BOOST_ASSERT(parit.pit() == 0);
+ // set the position of the text in the buffer to be able
+ // to resolve macros in it. This has nothing to do with
+ // labels, but by putting it here we avoid implementing
+ // a whole bunch of traversal routines just for this call.
+ parit.text()->setMacrocontextPosition(parit);
+
depth_type maxdepth = 0;
pit_type const lastpit = parit.lastpit();
for ( ; parit.pit() <= lastpit ; ++parit.pit()) {
}
+bool InsetBox::isMacroScope(Buffer const & buf) const
+{
+ BoxType btype = boxtranslator().find(params_.type);
+ return btype != Frameless || params_.inner_box;
+}
+
+
int InsetBox::latex(Buffer const & buf, odocstream & os,
OutputParams const & runparams) const
{
///
bool noFontChange() const { return true; }
///
+ bool isMacroScope(Buffer const & buf) const;
+ ///
int latex(Buffer const &, odocstream &, OutputParams const &) const;
///
int plaintext(Buffer const &, odocstream &, OutputParams const &) const;
}
+bool InsetBranch::isMacroScope(Buffer const & buf) const
+{
+ // Its own scope if not selected by buffer
+ return !isBranchSelected(buf);
+}
+
string const InsetBranchMailer::name_("branch");
bool isBranchSelected(Buffer const & buffer) const;
///
bool getStatus(Cursor &, FuncRequest const &, FuncStatus &) const;
- //
+ ///
virtual void updateLabels(Buffer const &, ParIterator const &);
+ ///
+ bool isMacroScope(Buffer const & buf) const;
protected:
///
InsetBranch(InsetBranch const &);
case WRAP_CODE:
case CAPTION_CODE:
case NEWPAGE_CODE:
+ case MATHMACRO_CODE:
return false;
default:
return InsetText::insetAllowed(code);
}
+bool InsetCollapsable::isMacroScope(Buffer const & buf) const
+{
+ // layout_ == 0 leads to no latex output, so ignore
+ // the macros outside
+ if (!layout_)
+ return true;
+
+ // see InsetCollapsable::latex(...) below. In these case
+ // an environment is opened there
+ if (!layout_->latexname.empty())
+ return true;
+
+ return false;
+}
+
+
int InsetCollapsable::latex(Buffer const & buf, odocstream & os,
OutputParams const & runparams) const
{
/// can we go further down on mouse click?
bool descendable() const;
///
+ bool isMacroScope(Buffer const & buf) const;
+ ///
void setLabel(docstring const & l);
///
virtual void setButtonLabel() {}
///
InsetCode lyxCode() const { return FLOAT_CODE; }
///
+ bool isMacroScope(Buffer const & buf) const { return true; }
+ ///
int latex(Buffer const &, odocstream &,
OutputParams const &) const;
///
///
void write(Buffer const & buf, std::ostream & os) const;
///
+ bool isMacroScope(Buffer const & buf) const { return true; }
+ ///
bool insetAllowed(InsetCode) const;
/** returns true if, when outputing LaTeX, font changes should
be closed before generating this inset. This is needed for
}
-namespace {
-
-/// return the child buffer if the file is a LyX doc and is loaded
+ /// return the child buffer if the file is a LyX doc and is loaded
Buffer * getChildBuffer(Buffer const & buffer, InsetCommandParams const & params)
{
if (isVerbatim(params) || isListings(params))
return childBuffer;
}
-} // namespace anon
-
-
/// return true if the file is or got loaded.
Buffer * loadIfNeeded(Buffer const & parent, InsetCommandParams const & params)
{
mutable docstring listings_label_;
};
-
+/// return the child buffer if the file is a LyX doc and is loaded
+Buffer * getChildBuffer(Buffer const & buffer, InsetCommandParams const & params);
+
/// return loaded Buffer or zero if the file loading did not proceed.
Buffer * loadIfNeeded(Buffer const & parent, InsetCommandParams const & params);
///
virtual docstring const editMessage() const;
///
+ bool isMacroScope(Buffer const & buf) const { return true; }
+ ///
int latex(Buffer const &, odocstream &, OutputParams const &) const;
///
void validate(LaTeXFeatures &) const;
}
+bool InsetNote::isMacroScope(Buffer const & buf) const
+{
+ // LyX note has no latex output
+ if (params_.type == InsetNoteParams::Note)
+ return true;
+
+ return InsetCollapsable::isMacroScope(buf);
+}
+
+
int InsetNote::latex(Buffer const & buf, odocstream & os,
OutputParams const & runparams_in) const
{
/// show the note dialog
bool showInsetDialog(BufferView * bv) const;
///
+ bool isMacroScope(Buffer const & buf) const;
+ ///
int latex(Buffer const &, odocstream &, OutputParams const &) const;
///
int plaintext(Buffer const &, odocstream &, OutputParams const &) const;
}
+bool InsetTabular::insetAllowed(InsetCode code) const
+{
+ if (code == MATHMACRO_CODE)
+ return false;
+
+ return true;
+}
+
+
void InsetTabular::write(Buffer const & buf, ostream & os) const
{
os << "Tabular" << endl;
///
EDITABLE editable() const { return HIGHLY_EDITABLE; }
///
- bool insetAllowed(InsetCode) const { return true; }
+ bool insetAllowed(InsetCode code) const;
///
bool allowSpellCheck() const { return true; }
///
/// should paragraph indendation be ommitted in any case?
bool neverIndent(Buffer const &) const;
///
+ virtual bool isMacroScope(Buffer const & buf) const { return true; }
+ ///
virtual bool allowMultiPar() const { return true; }
///
InsetText(InsetText const &);
///
void draw(PainterInfo & pi, int x, int y) const;
///
+ bool isMacroScope(Buffer const & buf) const { return true; }
+ ///
int latex(Buffer const &, odocstream &,
OutputParams const &) const;
///
///
InsetCode lyxCode() const { return WRAP_CODE; }
///
+ bool isMacroScope(Buffer const & buf) const { return true; }
+ ///
int latex(Buffer const &, odocstream &,
OutputParams const &) const;
///
cap::replaceSelectionWithString(cur, replacestr, fw);
bv->buffer().markDirty();
find(bv, searchstr, cs, mw, fw, false);
+ bv->buffer().updateMacros();
bv->processUpdateFlags(Update::Force | Update::FitCursor);
return 1;
bool fold = cmd.action == LFUN_MATH_MACRO_FOLD;
bool found = findMacroToFoldUnfold(it, fold);
if (found) {
- cur.recordUndo();
+ MathMacro * macro = it.nextInset()->asInsetMath()->asMacro();
+ cur.recordUndoInset();
if (fold)
- it.nextInset()->asInsetMath()->asMacro()->fold(cur);
+ macro->fold(cur);
else
- it.nextInset()->asInsetMath()->asMacro()->unfold(cur);
- }\v
+ macro->unfold(cur);
+ }
break;
}
#include <config.h>
+#include "InsetMathSqrt.h"
#include "MacroTable.h"
#include "MathMacroTemplate.h"
#include "MathMacroArgument.h"
+#include "MathStream.h"
#include "MathSupport.h"
-#include "InsetMathSqrt.h"
-
#include "InsetMathNest.h"
+
#include "Buffer.h"
+#include "InsetList.h"
+#include "Text.h"
#include "support/debug.h"
#include "DocIterator.h"
/////////////////////////////////////////////////////////////////////
MacroData::MacroData()
- : numargs_(0), lockCount_(0), redefinition_(false)
+ : queried_(true), numargs_(0), optionals_(0), lockCount_(0),
+ redefinition_(false), type_(MacroTypeNewcommand)
{}
-
-MacroData::MacroData(docstring const & definition,
- vector<docstring> const & defaults,
- int numargs, int optionals, docstring const & display,
- string const & requires)
- : definition_(definition), numargs_(numargs), display_(display),
- requires_(requires), lockCount_(0), redefinition_(false),
- optionals_(optionals), defaults_(defaults)
+
+
+MacroData::MacroData(Buffer const & buf, DocIterator const & pos)
+ : buffer_(&buf), pos_(pos), queried_(false), numargs_(0),
+ optionals_(0), lockCount_(0), redefinition_(false),
+ type_(MacroTypeNewcommand)
{
- defaults_.resize(optionals);
}
+
+
+MacroData::MacroData(MathMacroTemplate const & macro)
+ : queried_(false), numargs_(0), optionals_(0), lockCount_(0),
+ redefinition_(false), type_(MacroTypeNewcommand)
+{
+ queryData(macro);
+}
void MacroData::expand(vector<MathData> const & args, MathData & to) const
{
+ updateData();
+
InsetMathSqrt inset; // Hack. Any inset with a cell would do.
// FIXME UNICODE
asArray(display_.empty() ? definition_ : display_, inset.cell(0));
size_t MacroData::optionals() const
{
+ updateData();
return optionals_;
}
vector<docstring> const & MacroData::defaults() const
{
+ updateData();
return defaults_;
}
}
+void MacroData::queryData(MathMacroTemplate const & macro) const
+{
+ if (queried_)
+ return;
+
+ queried_ = true;
+ definition_ = macro.definition();
+ numargs_ = macro.numArgs();
+ display_ = macro.displayDefinition();
+ redefinition_ = macro.redefinition();
+ type_ = macro.type();
+ optionals_ = macro.numOptionals();
+ macro.getDefaults(defaults_);
+}
+
+
+void MacroData::updateData() const
+{
+ if (queried_)
+ return;
+
+ BOOST_ASSERT(buffer_ != 0);
+
+ // Try to fix position DocIterator. Should not do anything in theory.
+ pos_.fixIfBroken();
+
+ // find macro template
+ Inset * inset = pos_.nextInset();
+ if (inset == 0 || inset->lyxCode() != MATHMACRO_CODE) {
+ lyxerr << "BUG: No macro template found by MacroData" << endl;
+ return;
+ }
+
+ // query the data from the macro template
+ queryData(static_cast<MathMacroTemplate const &>(*inset));
+}
+
+
+void MacroData::write(odocstream & os, bool overwriteRedefinition) const
+{
+ updateData();
+
+ // find macro template
+ Inset * inset = pos_.nextInset();
+ if (inset == 0 || inset->lyxCode() != MATHMACRO_CODE) {
+ lyxerr << "BUG: No macro template found by MacroData" << endl;
+ return;
+ }
+
+ // output template
+ MathMacroTemplate const & tmpl
+ = static_cast<MathMacroTemplate const &>(*inset);
+ WriteStream wi(os, false, true);
+ tmpl.write(wi, overwriteRedefinition);
+}
+
+
/////////////////////////////////////////////////////////////////////
//
// The global table of macros
}
-bool MacroTable::has(docstring const & name) const
-{
- return find(name) != end();
-}
-
-
-MacroData const & MacroTable::get(docstring const & name) const
+MacroData const * MacroTable::get(docstring const & name) const
{
const_iterator it = find(name);
- BOOST_ASSERT(it != end());
- return it->second;
+ return it == end() ? 0 : &it->second;
}
{
//lyxerr << "MacroTable::insert, def: " << to_utf8(def) << endl;
MathMacroTemplate mac(def);
- MacroData data = mac.asMacroData();
+ MacroData data(mac);
data.requires() = requires;
insert(mac.name(), data);
}
}
-MacroContext::MacroContext(Buffer const & buf, Paragraph const & par)
- : buf_(buf), par_(par)
-{
-}
-
-
-bool MacroContext::has(docstring const & name) const
-{
- // check if it's a local macro
- if (macros_.has(name))
- return true;
-
- // otherwise ask the buffer
- return buf_.hasMacro(name, par_);
-}
-
+/////////////////////////////////////////////////////////////////////
+//
+// MacroContext
+//
+/////////////////////////////////////////////////////////////////////
-MacroData const & MacroContext::get(docstring const & name) const
+MacroContext::MacroContext(Buffer const & buf, DocIterator const & pos)
+ : buf_(buf), pos_(pos)
{
- // check if it's a local macro
- if (macros_.has(name))
- return macros_.get(name);
-
- // ask the buffer for its macros
- return buf_.getMacro(name, par_);
}
-void MacroContext::insert(docstring const & name, MacroData const & data)
+MacroData const * MacroContext::get(docstring const & name) const
{
- macros_.insert(name, data);
+ return buf_.getMacro(name, pos_);
}
-
} // namespace lyx
#ifndef MATH_MACROTABLE_H
#define MATH_MACROTABLE_H
+#include "DocIterator.h"
+
#include "support/docstring.h"
+#include "support/types.h"
#include <map>
#include <vector>
class Buffer;
class MathData;
+class MathMacroTemplate;
class Paragraph;
+
+enum MacroType {
+ MacroTypeNewcommand,
+ MacroTypeDef
+};
+
+
///
class MacroData {
public:
- ///
+ /// Constructor to make STL containers happy
MacroData();
+ /// Create lazy MacroData which only queries the macro template when needed
+ MacroData(Buffer const & buf, DocIterator const & pos);
+ /// Create non-lazy MacroData which directly queries the macro template
+ MacroData(MathMacroTemplate const & macro);
+
///
- MacroData(docstring const & definition,
- std::vector<docstring> const & defaults, int nargs, int optionals,
- docstring const & display, std::string const & requires);
- ///
- docstring const & definition() const { return definition_; }
+ docstring const & definition() const { updateData(); return definition_; }
///
- docstring const & display() const { return display_; }
+ docstring const & display() const { updateData(); return display_; }
/// arity including optional arguments (if there is any)
- size_t numargs() const { return numargs_; }
+ size_t numargs() const { updateData(); return numargs_; }
/// replace #1,#2,... by given MathAtom 0,1,.., _including_ the possible
/// optional argument
void expand(std::vector<MathData> const & from, MathData & to) const;
///
std::vector<docstring> const & defaults() const;
///
- std::string requires() const { return requires_; }
+ std::string const & requires() const { updateData(); return requires_; }
///
- std::string & requires() { return requires_; }
+ std::string & requires() { updateData(); return requires_; }
/// lock while being drawn to avoid recursions
int lock() const { return ++lockCount_; }
bool redefinition() const { return redefinition_; }
///
void setRedefinition(bool redefined) { redefinition_ = redefined; }
+
+ ///
+ MacroType type() const { return type_; }
+ ///
+ MacroType & type() { return type_; }
+ /// output as TeX macro, only works for lazy MacroData!!!
+ void write(odocstream & os, bool overwriteRedefinition) const;
+
///
bool operator==(MacroData const & x) const {
+ updateData();
+ x.updateData();
return definition_ == x.definition_
&& numargs_ == x.numargs_
&& display_ == x.display_
private:
///
- docstring definition_;
+ void queryData(MathMacroTemplate const & macro) const;
///
- size_t numargs_;
+ void updateData() const;
///
- docstring display_;
+ Buffer const * buffer_;
+ /// The position of the definition in the buffer.
+ /// There is no guarantee it stays valid if the buffer
+ /// changes. But it (normally) exists only until the
+ /// next Buffer::updateMacros call where new MacroData
+ /// objects are created for each macro definition.
+ /// In the worst case, it is invalidated and the MacroData
+ /// returns its defaults values and the user sees unfolded
+ /// macros.
+ mutable DocIterator pos_;
///
- std::string requires_;
+ mutable bool queried_;
///
- mutable int lockCount_;
+ mutable docstring definition_;
+ ///
+ mutable size_t numargs_;
+ ///
+ mutable docstring display_;
+ ///
+ mutable std::string requires_;
+ ///
+ mutable size_t optionals_;
+ ///
+ mutable std::vector<docstring> defaults_;
///
- bool redefinition_;
+ mutable int lockCount_;
///
- size_t optionals_;
+ mutable bool redefinition_;
///
- std::vector<docstring> defaults_;
+ mutable MacroType type_;
};
-
+
/// A lookup table of macro definitions.
/**
* This contains a table of "global" macros that are always accessible,
void insert(docstring const & definition, std::string const &);
/// Insert pre-digested macro definition
void insert(docstring const & name, MacroData const & data);
- /// Do we have a macro by that name?
- bool has(docstring const & name) const;
///
- MacroData const & get(docstring const & name) const;
+ MacroData const * get(docstring const & name) const;
///
void dump();
**/
class MacroContext {
public:
- /// construct context for insets in par (not including the ones
- /// defined in par itself)
- MacroContext(Buffer const & buf, Paragraph const & par);
+ /// construct context for the insets at pos
+ MacroContext(Buffer const & buf, DocIterator const & pos);
- /// Look for macro
- bool has(docstring const & name) const;
/// Lookup macro
- MacroData const & get(docstring const & name) const;
-
- /// Insert pre-digested macro definition
- void insert(docstring const & name, MacroData const & data);
+ MacroData const * get(docstring const & name) const;
private:
- /// context local macros
- MacroTable macros_;
///
Buffer const & buf_;
///
- Paragraph const & par_;
+ DocIterator const & pos_;
};
} // namespace lyx
return;
}
- const_cast<MathData*>(this)->updateMacros(mi);
+ Cursor & cur = mi.base.bv->cursor();
+ const_cast<MathData*>(this)->updateMacros(&cur, mi.macrocontext);
dim.asc = 0;
dim.wid = 0;
}
-void MathData::updateMacros(MetricsInfo & mi)
+void MathData::updateMacros(Cursor * cur, MacroContext const & mc)
{
- Cursor & cur = mi.base.bv->cursor();
-
// go over the array and look for macros
for (size_t i = 0; i < size(); ++i) {
MathMacro * macroInset = operator[](i).nucleus()->asMacro();
continue;
// get macro
- macroInset->updateMacro(mi);
+ macroInset->updateMacro(mc);
size_t macroNumArgs = 0;
size_t macroOptionals = 0;
MacroData const * macro = macroInset->macro();
// store old and compute new display mode
MathMacro::DisplayMode newDisplayMode;
MathMacro::DisplayMode oldDisplayMode = macroInset->displayMode();
- newDisplayMode = macroInset->computeDisplayMode(mi);
+ newDisplayMode = macroInset->computeDisplayMode();
// arity changed or other reason to detach?
if (oldDisplayMode == MathMacro::DISPLAY_NORMAL
&& (macroInset->arity() != macroNumArgs
|| macroInset->optionals() != macroOptionals
|| newDisplayMode == MathMacro::DISPLAY_UNFOLDED)) {
+
detachMacroParameters(cur, i);
}
if (newDisplayMode != MathMacro::DISPLAY_UNFOLDED
&& oldDisplayMode == MathMacro::DISPLAY_UNFOLDED) {
// put cursor in front of macro
- int macroSlice = cur.find(macroInset);
- if (macroSlice != -1)
- cur.cutOff(macroSlice - 1);
+ if (cur) {
+ int macroSlice = cur->find(macroInset);
+ if (macroSlice != -1)
+ cur->cutOff(macroSlice - 1);
+ }
}
// update the display mode
attachMacroParameters(cur, i, macroNumArgs, macroOptionals,
fromInitToNormalMode, interactive);
- // FIXME: proper anchor handling, this removes the selection
- cur.updateInsets(&cur.bottom().inset());
- cur.clearSelection();
+ if (cur) {
+ // FIXME: proper anchor handling, this removes the selection
+ cur->updateInsets(&cur->bottom().inset());
+ cur->clearSelection();
+ }
}
// Give macro the chance to adapt to new situation.
if (inset->asScriptInset())
inset = inset->asScriptInset()->nuc()[0].nucleus();
BOOST_ASSERT(inset->asMacro());
- inset->asMacro()->updateRepresentation(mi);
+ inset->asMacro()->updateRepresentation(cur);
}
}
-void MathData::detachMacroParameters(Cursor & cur, const size_type macroPos)
+void MathData::detachMacroParameters(Cursor * cur, const size_type macroPos)
{
MathMacro * macroInset = operator[](macroPos).nucleus()->asMacro();
// detach all arguments
vector<MathData> detachedArgs;
if (macroPos + 1 == size())
- // strip arguments if we are at the MathData end
- macroInset->detachArguments(detachedArgs, true);
+ // strip arguments if we are at the MathData end
+ macroInset->detachArguments(detachedArgs, true);
else
- macroInset->detachArguments(detachedArgs, false);
+ macroInset->detachArguments(detachedArgs, false);
// find cursor slice
- int curMacroSlice = cur.find(macroInset);
+ int curMacroSlice = -1;
+ if (cur)
+ curMacroSlice = cur->find(macroInset);
idx_type curMacroIdx = -1;
pos_type curMacroPos = -1;
vector<CursorSlice> argSlices;
if (curMacroSlice != -1) {
- curMacroPos = cur[curMacroSlice].pos();
- curMacroIdx = cur[curMacroSlice].idx();
- cur.cutOff(curMacroSlice, argSlices);
- cur.pop_back();
+ curMacroPos = (*cur)[curMacroSlice].pos();
+ curMacroIdx = (*cur)[curMacroSlice].idx();
+ cur->cutOff(curMacroSlice, argSlices);
+ cur->pop_back();
}
// only [] after the last non-empty argument can be dropped later
size_t lastNonEmptyOptional = 0;
for (size_t l = 0; l < detachedArgs.size() && l < macroInset->optionals(); ++l) {
- if (!detachedArgs[l].empty())
- lastNonEmptyOptional = l;
+ if (!detachedArgs[l].empty())
+ lastNonEmptyOptional = l;
}
// optional arguments to be put back?
// then we can drop empty optional parameters
if (detachedArgs[j].empty() && canDropEmptyOptional) {
if (curMacroIdx == j)
- cur[curMacroSlice - 1].pos() = macroPos + 1;
+ (*cur)[curMacroSlice - 1].pos() = macroPos + 1;
continue;
}
// cursor in optional argument of macro?
if (curMacroIdx == j) {
if (brace) {
- cur.append(0, curMacroPos);
- cur[curMacroSlice - 1].pos() = macroPos + 2;
+ cur->append(0, curMacroPos);
+ (*cur)[curMacroSlice - 1].pos() = macroPos + 2;
} else
- cur[curMacroSlice - 1].pos() = macroPos + 2 + curMacroPos;
- cur.append(argSlices);
- } else if (cur[curMacroSlice - 1].pos() >= int(p))
+ (*cur)[curMacroSlice - 1].pos() = macroPos + 2 + curMacroPos;
+ cur->append(argSlices);
+ } else if ((*cur)[curMacroSlice - 1].pos() >= int(p))
// cursor right of macro
- cur[curMacroSlice - 1].pos() += optarg.size();
+ (*cur)[curMacroSlice - 1].pos() += optarg.size();
}
// put them back into the MathData
for (; j < detachedArgs.size(); ++j, ++p) {
MathData const & arg = detachedArgs[j];
- if (arg.size() == 1 && !arg[0]->asScriptInset()) // && arg[0]->asCharInset())
+ if (arg.size() == 1
+ && !arg[0]->asScriptInset()
+ && !(arg[0]->asMacro() && arg[0]->asMacro()->arity() > 0))
insert(p, arg[0]);
else
insert(p, MathAtom(new InsetMathBrace(arg)));
-
+
// cursor in macro?
if (curMacroSlice == -1)
continue;
// cursor in j-th argument of macro?
if (curMacroIdx == j) {
if (operator[](p).nucleus()->asBraceInset()) {
- cur[curMacroSlice - 1].pos() = p;
- cur.append(0, curMacroPos);
- cur.append(argSlices);
+ (*cur)[curMacroSlice - 1].pos() = p;
+ cur->append(0, curMacroPos);
+ cur->append(argSlices);
} else {
- cur[curMacroSlice - 1].pos() = p; // + macroPos;
- cur.append(argSlices);
+ (*cur)[curMacroSlice - 1].pos() = p; // + macroPos;
+ cur->append(argSlices);
}
- } else if (cur[curMacroSlice - 1].pos() >= int(p))
- ++cur[curMacroSlice - 1].pos();
+ } else if ((*cur)[curMacroSlice - 1].pos() >= int(p))
+ ++(*cur)[curMacroSlice - 1].pos();
}
- // FIXME: proper anchor handling, this removes the selection
- cur.clearSelection();
- cur.updateInsets(&cur.bottom().inset());
+ if (cur) {
+ // FIXME: proper anchor handling, this removes the selection
+ cur->clearSelection();
+ cur->updateInsets(&cur->bottom().inset());
+ }
}
-void MathData::attachMacroParameters(Cursor & cur,
+void MathData::attachMacroParameters(Cursor * cur,
const size_type macroPos, const size_type macroNumArgs,
const int macroOptionals, const bool fromInitToNormalMode,
const bool interactiveInit)
MathAtom scriptToPutAround;
// find cursor slice again of this MathData
- int thisSlice = cur.find(*this);
+ int thisSlice = -1;
+ if (cur)
+ thisSlice = cur->find(*this);
int thisPos = -1;
if (thisSlice != -1)
- thisPos = cur[thisSlice].pos();
+ thisPos = (*cur)[thisSlice].pos();
// find arguments behind the macro
if (!interactiveInit) {
operator[](macroPos) = scriptToPutAround;
// go into the script inset nucleus
- if (thisPos == int(macroPos))
- cur.append(0, 0);
+ if (cur && thisPos == int(macroPos))
+ cur->append(0, 0);
// get pointer to "deep" copied macro inset
InsetMathScript * scriptInset
// fix cursor if right of p
if (thisPos >= int(p))
- cur[thisSlice].pos() -= p - (macroPos + 1);
+ (*cur)[thisSlice].pos() -= p - (macroPos + 1);
// was the macro inset just inserted interactively and was now folded
// and the cursor is just behind?
- if (cur[thisSlice].pos() == int(macroPos + 1)
- && interactiveInit
- && fromInitToNormalMode
- && macroInset->arity() > 0
- && thisSlice + 1 == int(cur.depth())) {
+ if ((*cur)[thisSlice].pos() == int(macroPos + 1)
+ && interactiveInit
+ && fromInitToNormalMode
+ && macroInset->arity() > 0
+ && thisSlice + 1 == int(cur->depth())) {
// then enter it if the cursor was just behind
- cur[thisSlice].pos() = macroPos;
- cur.push_back(CursorSlice(*macroInset));
- macroInset->idxFirst(cur);
+ (*cur)[thisSlice].pos() = macroPos;
+ cur->push_back(CursorSlice(*macroInset));
+ macroInset->idxFirst(*cur);
}
}
-void MathData::collectOptionalParameters(Cursor & cur,
+void MathData::collectOptionalParameters(Cursor * cur,
const size_type numOptionalParams, vector<MathData> & params,
size_t & pos, const pos_type macroPos, const int thisPos, const int thisSlice)
{
// is a [] block following which could be an optional parameter?
if (operator[](pos)->getChar() != '[')
break;
-
+
// found possible optional argument, look for "]"
size_t right = pos + 1;
for (; right < size(); ++right) {
// place cursor in optional argument of macro
if (thisSlice != -1
- && thisPos >= int(pos) && thisPos <= int(right)) {
+ && thisPos >= int(pos) && thisPos <= int(right)) {
int paramPos = max(0, thisPos - int(pos) - 1);
vector<CursorSlice> x;
- cur.cutOff(thisSlice, x);
- cur[thisSlice].pos() = macroPos;
+ cur->cutOff(thisSlice, x);
+ (*cur)[thisSlice].pos() = macroPos;
if (brace) {
paramPos = x[0].pos();
x.erase(x.begin());
}
- cur.append(0, paramPos);
- cur.append(x);
+ cur->append(0, paramPos);
+ cur->append(x);
}
pos = right + 1;
}
// fill up empty optional parameters
- while (params.size() < numOptionalParams) {
- params.push_back(MathData());
- }
+ while (params.size() < numOptionalParams)
+ params.push_back(MathData());
}
-void MathData::collectParameters(Cursor & cur,
+void MathData::collectParameters(Cursor * cur,
const size_type numParams, vector<MathData> & params,
size_t & pos, MathAtom & scriptToPutAround,
const pos_type macroPos, const int thisPos, const int thisSlice)
// fix cursor
vector<CursorSlice> argSlices;
int argPos = 0;
- if (thisSlice != -1 && thisPos == int(pos)) {
- cur.cutOff(thisSlice, argSlices);
- }
+ if (thisSlice != -1 && thisPos == int(pos))
+ cur->cutOff(thisSlice, argSlices);
// which kind of parameter is it? In {}? With index x^n?
InsetMathBrace const * brace = cell->asBraceInset();
// this should only happen after loading, so make cursor handling simple
if (thisPos >= int(macroPos) && thisPos <= int(macroPos + numParams)) {
argSlices.clear();
- cur.append(0, 0);
+ if (cur)
+ cur->append(0, 0);
}
} else {
// the simplest case: plain inset
// put cursor in argument again
if (thisSlice != - 1 && thisPos == int(pos)) {
- cur.append(params.size() - 1, argPos);
- cur.append(argSlices);
- cur[thisSlice].pos() = macroPos;
+ cur->append(params.size() - 1, argPos);
+ cur->append(argSlices);
+ (*cur)[thisSlice].pos() = macroPos;
}
++pos;
class BufferView;
class Cursor;
+class DocIterator;
class LaTeXFeatures;
class ReplaceData;
+class MacroContext;
class MathMacro;
class MetricsInfo;
class PainterInfo;
///
void swap(MathData & ar) { base_type::swap(ar); }
+ /// attach/detach arguments to macros, updating the cur to
+ /// stay visually at the same position (cur==0 is allowed)
+ void updateMacros(Cursor * cur, MacroContext const & mc);
+
protected:
/// cached values for super/subscript placement
mutable int minasc_;
/// is this an exact match at this position?
bool find1(MathData const & ar, size_type pos) const;
- /// attach/detach arguments to macros
- void updateMacros(MetricsInfo & mi);
///
- void detachMacroParameters(Cursor & cur, const size_type macroPos);
+ void detachMacroParameters(Cursor * cur, const size_type macroPos);
///
- void attachMacroParameters(Cursor & cur, const size_type macroPos,
+ void attachMacroParameters(Cursor * cur, const size_type macroPos,
const size_type macroNumArgs, const int macroOptionals,
const bool fromInitToNormalMode, const bool interactiveInit);
///
- void collectOptionalParameters(Cursor & cur,
+ void collectOptionalParameters(Cursor * cur,
const size_type numOptionalParams, std::vector<MathData> & params,
size_t & pos, const pos_type macroPos, const int thisPos, const int thisSlice);
///
- void collectParameters(Cursor & cur,
+ void collectParameters(Cursor * cur,
const size_type numParams, std::vector<MathData> & params,
size_t & pos, MathAtom & scriptToPutAround,
const pos_type macroPos, const int thisPos, const int thisSlice);
}
-void MathMacro::updateMacro(MetricsInfo & mi)
+void MathMacro::updateMacro(MacroContext const & mc)
{
- if (validName() && mi.macrocontext.has(name())) {
- macro_ = &mi.macrocontext.get(name());
- if (macroBackup_ != *macro_) {
+ if (validName()) {
+ macro_ = mc.get(name());
+ if (macro_ && macroBackup_ != *macro_) {
macroBackup_ = *macro_;
needsUpdate_ = true;
}
}
-void MathMacro::updateRepresentation(MetricsInfo & mi)
+void MathMacro::updateRepresentation(Cursor const * bvCur)
{
// index of child where the cursor is (or -1 if none is edited)
- int curIdx = cursorIdx(mi.base.bv->cursor());
+ int curIdx = -1;
+ if (bvCur)
+ curIdx = cursorIdx(*bvCur);
previousCurIdx_ = curIdx;
// known macro?
- if (macro_) {
- requires_ = macro_->requires();
-
- if (displayMode_ == DISPLAY_NORMAL) {
- // set edit mode to draw box around if needed
- bool prevEditing = editing_;
- editing_ = editMode(mi.base.bv->cursor());
-
- // editMode changed and we have to switch default value and hole of optional?
- if (optionals_ > 0 && nargs() > 0 &&
- prevEditing != editing_)
- needsUpdate_ = true;
-
- // macro changed?
- if (needsUpdate_) {
- needsUpdate_ = false;
-
- // get default values of macro
- vector<docstring> const & defaults = macro_->defaults();
-
- // create MathMacroArgumentValue objects pointing to the cells of the macro
- vector<MathData> values(nargs());
- for (size_t i = 0; i < nargs(); ++i) {
- if (!cell(i).empty() || i >= defaults.size() ||
- defaults[i].empty() || curIdx == (int)i)
- values[i].insert(0, MathAtom(new ArgumentProxy(*this, i)));
- else
- values[i].insert(0, MathAtom(new ArgumentProxy(*this, i, defaults[i])));
- }
-
- // expanding macro with the values
- macro_->expand(values, expanded_.cell(0));
- }
+ if (macro_ == 0)
+ return;
+
+ // update requires
+ requires_ = macro_->requires();
+
+ // non-normal mode? We are done!
+ if (displayMode_ != DISPLAY_NORMAL)
+ return;
+
+ // set edit mode to draw box around if needed
+ bool prevEditing = editing_;
+ editing_ = false;
+ if (bvCur)
+ editing_ = editMode(*bvCur);
+
+ // editMode changed and we have to switch default value and hole of optional?
+ if (optionals_ > 0 && nargs() > 0 &&
+ prevEditing != editing_)
+ needsUpdate_ = true;
+
+ // macro changed?
+ if (needsUpdate_) {
+ needsUpdate_ = false;
+
+ // get default values of macro
+ vector<docstring> const & defaults = macro_->defaults();
+
+ // create MathMacroArgumentValue objects pointing to the cells of the macro
+ vector<MathData> values(nargs());
+ for (size_t i = 0; i < nargs(); ++i) {
+ ArgumentProxy * proxy;
+ if (!cell(i).empty()
+ || i >= defaults.size()
+ || defaults[i].empty()
+ || curIdx == (int)i)
+ proxy = new ArgumentProxy(*this, i);
+ else
+ proxy = new ArgumentProxy(*this, i, defaults[i]);
+ values[i].insert(0, MathAtom(proxy));
}
- }
+
+ // expanding macro with the values
+ macro_->expand(values, expanded_.cell(0));
+ }
}
}
-MathMacro::DisplayMode MathMacro::computeDisplayMode(MetricsInfo const &) const
+MathMacro::DisplayMode MathMacro::computeDisplayMode() const
{
if (nextFoldMode_ == true && macro_ && !macro_->locked())
return DISPLAY_NORMAL;
void MathMacro::write(WriteStream & os) const
{
- if (displayMode_ == DISPLAY_NORMAL) {
- BOOST_ASSERT(macro_);
-
- os << "\\" << name();
- bool first = true;
- idx_type i = 0;
-
- // Use macroBackup_ instead of macro_ here, because
- // this is outside the metrics/draw calls, hence the macro_
- // variable can point to a MacroData which was freed already.
- vector<docstring> const & defaults = macroBackup_.defaults();
-
- // Optional argument
- if (os.latex()) {
- if (i < optionals_) {
- // the first real optional, the others are non-optional in latex
- if (!cell(i).empty()) {
- first = false;
- os << "[" << cell(0) << "]";
- }
-
- ++i;
- }
- } else {
- // In lyx mode print all in any case
- for (; i < cells_.size() && i < optionals_; ++i) {
+ // non-normal mode
+ if (displayMode_ != DISPLAY_NORMAL) {
+ os << "\\" << name() << " ";
+ os.pendingSpace(true);
+ return;
+ }
+
+ // normal mode
+ BOOST_ASSERT(macro_);
+
+ os << "\\" << name();
+ bool first = true;
+ idx_type i = 0;
+
+ // Use macroBackup_ instead of macro_ here, because
+ // this is outside the metrics/draw calls, hence the macro_
+ // variable can point to a MacroData which was freed already.
+ vector<docstring> const & defaults = macroBackup_.defaults();
+
+ // Optional argument
+ if (os.latex()) {
+ // Print first optional in LaTeX semantics
+ if (i < optionals_) {
+ // the first real optional, the others are non-optional in latex
+ if (!cell(i).empty()) {
first = false;
- os << "[" << cell(i) << "]";
+ os << "[" << cell(0) << "]";
}
+
+ ++i;
}
-
- for (; i < cells_.size(); ++i) {
- if (cell(i).empty() && i < optionals_) {
- os << "{" << defaults[i] << "}";
- } else if (cell(i).size() == 1 && cell(i)[0].nucleus()->asCharInset()) {
- if (first)
- os << " ";
- os << cell(i);
- } else
- os << "{" << cell(i) << "}";
+ } else {
+ // In lyx mode print all in any case
+ for (; i < cells_.size() && i < optionals_; ++i) {
first = false;
+ os << "[" << cell(i) << "]";
}
- if (first)
- os.pendingSpace(true);
- } else {
- os << "\\" << name() << " ";
- os.pendingSpace(true);
}
+
+ // Print remaining macros
+ // (also the further optional parameters in LaTeX mode!)
+ for (; i < cells_.size(); ++i) {
+ if (cell(i).empty() && i < optionals_)
+ os << "{" << defaults[i] << "}";
+ else if (cell(i).size() == 1
+ && cell(i)[0].nucleus()->asCharInset()) {
+ if (first)
+ os << " ";
+ os << cell(i);
+ } else
+ os << "{" << cell(i) << "}";
+ first = false;
+ }
+
+ // add space if there was no argument
+ if (first)
+ os.pendingSpace(true);
}
/// update the display mode (should only be called after detaching arguments)
void setDisplayMode(DisplayMode mode);
/// compute the next display mode
- DisplayMode computeDisplayMode(MetricsInfo const & mi) const;
+ DisplayMode computeDisplayMode() const;
/// update macro definition
- void updateMacro(MetricsInfo & mi);
+ void updateMacro(MacroContext const & mc);
/// check if macro definition changed, argument changed etc. and adapt
- void updateRepresentation(MetricsInfo & mi);
+ void updateRepresentation(Cursor const * bvCur);
/// empty macro, put arguments into args, possibly strip arity-attachedArgsNum_ empty ones.
/// Includes the optional arguments.
void detachArguments(std::vector<MathData> & args, bool strip);
MathMacroTemplate::MathMacroTemplate()
- : InsetMathNest(3), numargs_(0), optionals_(0), type_(from_ascii("newcommand"))
+ : InsetMathNest(3), numargs_(0), optionals_(0),
+ type_(MacroTypeNewcommand)
{
initMath();
}
MathMacroTemplate::MathMacroTemplate(docstring const & name, int numargs,
- int optionals, docstring const & type,
+ int optionals, MacroType type,
vector<MathData> const & optionalValues,
MathData const & def, MathData const & display)
-: InsetMathNest(optionals + 3), numargs_(numargs),
- optionals_(optionals), optionalValues_(optionalValues), type_(type)
+ : InsetMathNest(optionals + 3), numargs_(numargs),
+ optionals_(optionals), optionalValues_(optionalValues),
+ type_(type)
{
initMath();
}
+void MathMacroTemplate::updateToContext(MacroContext const & mc) const
+{
+ redefinition_ = mc.get(name()) != 0;
+}
+
+
void MathMacroTemplate::metrics(MetricsInfo & mi, Dimension & dim) const
{
FontSetChanger dummy1(mi.base, from_ascii("mathnormal"));
// valid macro?
MacroData const * macro = 0;
- if (validName() && mi.macrocontext.has(name())) {
- macro = &mi.macrocontext.get(name());
- if (type_ == from_ascii("newcommand") || type_ == from_ascii("renewcommand")) {
- // use the MacroData::redefinition_ information instead of MacroContext::has
- // because the macro is known here already anyway to detect recursive definitions
- type_ = macro->redefinition() ? from_ascii("renewcommand") : from_ascii("newcommand");
- }
+ if (validName()) {
+ macro = mi.macrocontext.get(name());
+
+ // updateToContext() - avoids another lookup
+ redefinition_ = macro != 0;
}
// create label "{#1}{#2}:="
int real_des = dim.des + dim0.ascent() / 2;
dim.asc = max(real_asc, real_des) + dim0.ascent() / 2 + 5;
dim.des = max(real_asc, real_des) - dim0.ascent() / 2 + 5;
-
+
setDimCache(mi, dim);
}
int num = numargs_ + 1;
if (arg.size() != 0)
num = convert<int>(arg);
- bool on = (num >= optionals_ && numargs_ < 9 && num <= numargs_ + 1);
+ bool on = (num >= optionals_
+ && numargs_ < 9 && num <= numargs_ + 1);
flag.enabled(on);
break;
}
}
case LFUN_MATH_MACRO_MAKE_OPTIONAL:
- flag.enabled(numargs_ > 0 && optionals_ < numargs_ && type_ != from_ascii("def"));
+ flag.enabled(numargs_ > 0
+ && optionals_ < numargs_
+ && type_ != MacroTypeDef);
break;
case LFUN_MATH_MACRO_MAKE_NONOPTIONAL:
- flag.enabled(optionals_ > 0 && type_ != from_ascii("def"));
+ flag.enabled(optionals_ > 0
+ && type_ != MacroTypeDef);
break;
case LFUN_MATH_MACRO_ADD_OPTIONAL_PARAM:
break;
case LFUN_MATH_MACRO_ADD_GREEDY_OPTIONAL_PARAM:
- flag.enabled(numargs_ == 0 && type_ != from_ascii("def"));
+ flag.enabled(numargs_ == 0
+ && type_ != MacroTypeDef);
break;
default:
void MathMacroTemplate::write(WriteStream & os) const
{
- if (type_ == "def") {
+ write(os, false);
+}
+
+
+void MathMacroTemplate::write(WriteStream & os, bool overwriteRedefinition) const
+{
+ if (type_ == MacroTypeDef) {
os << "\\def\\" << name().c_str();
for (int i = 1; i <= numargs_; ++i)
os << '#' << i;
} else {
// newcommand or renewcommand
- os << "\\" << type_.c_str() << "{\\" << name().c_str() << '}';
+ if (redefinition_ && !overwriteRedefinition)
+ os << "\\renewcommand";
+ else
+ os << "\\newcommand";
+ os << "{\\" << name().c_str() << '}';
if (numargs_ > 0)
os << '[' << numargs_ << ']';
}
-MacroData MathMacroTemplate::asMacroData() const
+bool MathMacroTemplate::fixNameAndCheckIfValid()
+{
+ // check all the characters/insets in the name cell
+ size_t i = 0;
+ MathData & data = cell(0);
+ while (i < data.size()) {
+ InsetMathChar const * cinset = data[i]->asCharInset();
+ if (cinset) {
+ // valid character in [a-zA-Z]?
+ char_type c = cinset->getChar();
+ if ((c >= 'a' && c <= 'z')
+ || (c >= 'A' && c <= 'Z')) {
+ ++i;
+ continue;
+ }
+ }
+
+ // throw cell away
+ data.erase(i);
+ }
+
+ // now it should be valid if anything in the name survived
+ return data.size() > 0;
+}
+
+
+void MathMacroTemplate::getDefaults(vector<docstring> & defaults) const
{
- vector<docstring> defaults(numargs_);
+ defaults.resize(numargs_);
for (int i = 0; i < optionals_; ++i)
- defaults[i] = asString(cell(optIdx(i)));
- return MacroData(asString(cell(defIdx())), defaults,
- numargs_, optionals_, asString(cell(displayIdx())), string());
+ defaults[i] = asString(cell(optIdx(i)));
+}
+
+
+docstring MathMacroTemplate::definition() const
+{
+ return asString(cell(defIdx()));
+}
+
+
+docstring MathMacroTemplate::displayDefinition() const
+{
+ return asString(cell(displayIdx()));
}
+size_t MathMacroTemplate::numArgs() const
+{
+ return numargs_;
+}
+
+
+size_t MathMacroTemplate::numOptionals() const
+{
+ return optionals_;
+}
+
} // namespace lyx
MathMacroTemplate();
///
MathMacroTemplate(docstring const & name, int nargs, int optional,
- docstring const & type,
+ MacroType type,
std::vector<MathData> const & optionalValues = std::vector<MathData>(),
MathData const & def = MathData(),
MathData const & display = MathData());
void write(Buffer const &, std::ostream & os) const;
///
void write(WriteStream & os) const;
+ /// Output LaTeX code, but assume that the macro is not definied yet
+ /// if overwriteRedefinition is true
+ void write(WriteStream & os, bool overwriteRedefinition) const;
///
int plaintext(Buffer const &, odocstream &,
OutputParams const &) const;
+ ///
+ bool noFontChange() const { return true; }
///
docstring name() const;
+ ///
+ void getDefaults(std::vector<docstring> & defaults) const;
+ ///
+ docstring definition() const;
+ ///
+ docstring displayDefinition() const;
+ ///
+ size_t numArgs() const;
+ ///
+ size_t numOptionals() const;
+ ///
+ bool redefinition() const { return redefinition_; }
+ ///
+ MacroType type() const { return type_; }
+
/// check name and possible other formal properties
bool validMacro() const;
///
bool validName() const;
- ///
- MacroData asMacroData() const;
+ /// Remove everything from the name which makes it invalid
+ /// and return true iff it is valid.
+ bool fixNameAndCheckIfValid();
+
+ /// decide whether its a redefinition
+ void updateToContext(MacroContext const & mc) const;
+
///
void draw(PainterInfo & pi, int x, int y) const;
///
mutable int numargs_;
///
int optionals_;
- /// keeps the old optional default value when an optional argument is disabled
+ /// keeps the old optional default value when an
+ /// optional argument is disabled
std::vector<MathData> optionalValues_;
- /// newcommand or renewcommand or def
- mutable docstring type_;
+
+ /// (re)newcommand or def
+ mutable MacroType type_;
+ /// defined before already?
+ mutable bool redefinition_;
};
t.cs() == "newcommand" ||
t.cs() == "renewcommand")
{
- docstring const type = t.cs();
+ MacroType type = MacroTypeNewcommand;
+ if (t.cs() == "def")
+ type = MacroTypeDef;
docstring name;
int nargs = 0;
int optionals = 0;
vector<MathData> optionalValues;
- if (t.cs() == "def") {
+ if (type == MacroTypeDef) {
// get name
name = getToken().cs();
nargs /= 2;
//lyxerr << "read \\def parameter list '" << pars << "'" << endl;
- } else { // t.cs() == "newcommand" || t.cs() == "renewcommand"
+ } else {
if (getToken().cat() != catBegin) {
error("'{' in \\newcommand expected (1) ");
return;