]> git.lyx.org Git - features.git/commitdiff
* Lazy MathData to avoid unneeded interpretation of macro definitions
authorStefan Schimanski <sts@lyx.org>
Fri, 21 Dec 2007 20:42:46 +0000 (20:42 +0000)
committerStefan Schimanski <sts@lyx.org>
Fri, 21 Dec 2007 20:42:46 +0000 (20:42 +0000)
* DocIterator as MacroPosition
* Iterative search for macro in scope until a visible one is found.
This include the ability to resolve macro inside nested text insets.
* Speed up macro lookups by factor 2: only getMacro(name) call, no
further hasMacro(name) call before
* Both way child/master support
* Correct macro scope for multi-paragraph environments
* Correct macro scope for multi-depth-paragraphs
* Buffer::updateMacros made const
* Update macros when loaded (of master and child)
* Do not remove too many braces when unfolding a macro. This could
lead to a data loss because the relationship between arguments of
macros can be mixed up if nested macros are unfold at once.
* Reduce dependencies to MetricsInfo in MathMacro
* Update macros when needed. Normally it's done just before doing
metrics. But in cases without a brace around some constructs (like
\left(bla\right)) there is some help needed.

git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@22241 a592a061-630c-0410-9148-cb99ea01b6c8

43 files changed:
src/Buffer.cpp
src/Buffer.h
src/BufferView.cpp
src/DocIterator.cpp
src/DocIterator.h
src/LyXFunc.cpp
src/Paragraph.cpp
src/Paragraph.h
src/Text.cpp
src/Text.h
src/Text3.cpp
src/TextMetrics.cpp
src/buffer_funcs.cpp
src/insets/InsetBox.cpp
src/insets/InsetBox.h
src/insets/InsetBranch.cpp
src/insets/InsetBranch.h
src/insets/InsetCaption.cpp
src/insets/InsetCollapsable.cpp
src/insets/InsetCollapsable.h
src/insets/InsetFloat.h
src/insets/InsetFootlike.h
src/insets/InsetInclude.cpp
src/insets/InsetInclude.h
src/insets/InsetListings.h
src/insets/InsetNote.cpp
src/insets/InsetNote.h
src/insets/InsetTabular.cpp
src/insets/InsetTabular.h
src/insets/InsetText.h
src/insets/InsetTheorem.h
src/insets/InsetWrap.h
src/lyxfind.cpp
src/mathed/InsetMathNest.cpp
src/mathed/MacroTable.cpp
src/mathed/MacroTable.h
src/mathed/MathData.cpp
src/mathed/MathData.h
src/mathed/MathMacro.cpp
src/mathed/MathMacro.h
src/mathed/MathMacroTemplate.cpp
src/mathed/MathMacroTemplate.h
src/mathed/MathParser.cpp

index 9dc1b28000a0500a41bef70cbb269f9feef65536..7bba41a1cdf25b7015ef390f41e6604867d79410 100644 (file)
@@ -174,10 +174,19 @@ public:
        ///
        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;
@@ -223,8 +232,9 @@ static FileName createBufferTmpDir()
 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);
@@ -238,6 +248,8 @@ Buffer::Buffer(string const & file, bool readonly)
        : d(new Impl(*this, FileName(file), readonly)), gui_(0)
 {
        LYXERR(Debug::INFO, "Buffer::Buffer()");
+
+       d->inset.getText(0)->setMacrocontextPosition(par_iterator_begin());
 }
 
 
@@ -251,11 +263,13 @@ Buffer::~Buffer()
        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);
 
@@ -563,6 +577,8 @@ bool Buffer::readDocument(Lexer & lex)
                 text().paragraphs().end(),
                 bind(&Paragraph::setInsetOwner, _1, &inset()));
 
+       updateMacros();
+       updateMacroInstances();
        return res;
 }
 
@@ -1111,11 +1127,19 @@ void Buffer::writeLaTeXSource(odocstream & os,
        
        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;
        }
@@ -1126,9 +1150,15 @@ void Buffer::writeLaTeXSource(odocstream & os,
        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();
@@ -1624,6 +1654,7 @@ void Buffer::setParent(Buffer const * buffer)
 {
        // Avoids recursive include.
        d->parent_buffer = buffer == this ? 0 : buffer;
+       updateMacros();
 }
 
 
@@ -1642,110 +1673,394 @@ Buffer const * Buffer::masterBuffer() const
 }
 
 
-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)
 {
@@ -2016,6 +2331,10 @@ void Buffer::resetChildDocuments(bool close_them) const
 
        if (use_gui && masterBuffer() == this)
                updateLabels(*this);
+
+       // clear references to children in macro tables
+       d->children_positions.clear();
+       d->position_to_children.clear();
 }
 
 
@@ -2037,6 +2356,8 @@ void Buffer::loadChildDocuments() const
 
        if (use_gui && masterBuffer() == this)
                updateLabels(*this);
+
+       updateMacros();
 }
 
 
@@ -2089,6 +2410,9 @@ bool Buffer::doExport(string const & format, bool put_in_tempdir,
        filename = changeExtension(filename,
                                   formats.extension(backend_format));
 
+       // fix macros
+       updateMacroInstances();
+
        // Plain text backend
        if (backend_format == "text")
                writePlaintextFile(*this, FileName(filename), runparams);
index 2c5a5a1c77251377f13990232462437383ed9e4c..9dd0fc38a4483bf323e26a65177af79c2781d0fa 100644 (file)
@@ -17,6 +17,7 @@
 #include "support/strfwd.h"
 #include "support/types.h"
 
+#include <set>
 #include <string>
 #include <vector>
 
@@ -25,6 +26,7 @@ namespace lyx {
 
 class BufferParams;
 class EmbeddedFiles;
+class DocIterator;
 class ErrorItem;
 class ErrorList;
 class FuncRequest;
@@ -359,16 +361,25 @@ public:
        //
        // 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.
@@ -442,7 +453,30 @@ public:
        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;
index 19b9c9c54f41d152f499193749d44e28673d6958..e6b288234662dbe1f4797b369a2907b6e103c3bd 100644 (file)
@@ -349,9 +349,7 @@ void BufferView::processUpdateFlags(Update::flags flags)
                << ", 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().
index 3d2c18d6347f18310b3ccce42e2e1c1ca56e40b7..51d21921a0990be19c59106f7823b209b5b3f9f9 100644 (file)
@@ -532,29 +532,6 @@ ostream & operator<<(ostream & os, DocIterator const & dit)
 }
 
 
-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)
index fc6125186c460b87f2b8c8e18b49ff31cf9fb528..6e730ea685018f6b85116c6adbdffdb07469b39e 100644 (file)
@@ -262,6 +262,32 @@ inline bool operator!=(DocIterator const & di1, DocIterator const & di2)
 }
 
 
+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.
index dfde956fb6230b25ee2549b0119bd02764c21153..61bc51dbc8d15f7f61895f5741500ceacee3bf25 100644 (file)
@@ -1790,7 +1790,7 @@ void LyXFunc::dispatch(FuncRequest const & cmd)
                        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());
index c21b7b349bd86dacb97053058132f04eb0400b86..61fe11117bf5f64e1b75cf7c0d05729cce1dbb6b 100644 (file)
@@ -183,10 +183,6 @@ public:
        ///
        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_;
 
@@ -232,7 +228,6 @@ Paragraph::Private::Private(Paragraph * owner)
        : owner_(owner), inset_owner_(0), begin_of_body_(0)
 {
        id_ = paragraph_id++;
-       macrocontext_position_ = 0;
        text_.reserve(100);
 }
 
@@ -2497,18 +2492,6 @@ int Paragraph::checkBiblio(bool track_changes)
 }
 
 
-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);
index 30c033b4875be3516646fe6dac79048e8be69c04..3b9d4be36ff5b75e090f16e532327177443e481d 100644 (file)
@@ -344,15 +344,6 @@ public:
        /// 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);
index 6b29809261a4d5be798c4bf31a1e39e065d9a9b6..12ed1b85d9ee12c8d6de915f50faaea6cb7af602 100644 (file)
@@ -1392,4 +1392,16 @@ void Text::charsTranspose(Cursor & cur)
 }
 
 
+DocIterator Text::macrocontextPosition() const
+{
+       return macrocontext_position_;
+}
+
+
+void Text::setMacrocontextPosition(DocIterator const & pos)
+{
+       macrocontext_position_ = pos;
+}
+
+
 } // namespace lyx
index caf7b65d68fefc632a3b6ebd6136481f2f208625..0ebc1eeace21fc92b40aa046facff4d2075a9106 100644 (file)
@@ -14,6 +14,7 @@
 #ifndef TEXT_H
 #define TEXT_H
 
+#include "DocIterator.h"
 #include "ParagraphList.h"
 
 namespace lyx {
@@ -257,6 +258,14 @@ public:
        /// 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_;
@@ -287,6 +296,9 @@ private:
        /// \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
index 58aaddad90ba04b2058866223d93fce152cd3df2..b90c6c84823d4b39b22b5df30ce604b48b3dee8e 100644 (file)
@@ -1435,8 +1435,10 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
                        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;
index a90d0112fd66c2f469b0169aaa4d5df9cae88d40..e1cf6591e4b856a9108ca8360653fdfb0324564b 100644 (file)
@@ -375,35 +375,28 @@ bool TextMetrics::redoParagraph(pit_type const pit)
        // 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
@@ -412,6 +405,7 @@ bool TextMetrics::redoParagraph(pit_type const pit)
                        - 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);
index 05727eafc978b9f5db602b9ac148400ac74972ed..d38e3aaaa7f0a6c344fdde46e8aa14688e94e6aa 100644 (file)
@@ -410,6 +410,12 @@ void updateLabels(Buffer const & buf, ParIterator & parit)
 {
        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()) {
index 91619aa2ab0df0d94b8c65d0b7bc43abaf4f84f1..26360243994c089aff5dadbe6cc68c9ccd0e2a86 100644 (file)
@@ -239,6 +239,13 @@ bool InsetBox::getStatus(Cursor & cur, FuncRequest const & cmd,
 }
 
 
+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
 {
index 337c3bc34b20bb6c1888ccd28f894137723bc878..3ae2c6946e5a06aa892c0f43d697822d91013e2b 100644 (file)
@@ -86,6 +86,8 @@ public:
        ///
        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;
index b272647b83d08c3717a2ee7f9ea27bb449c63da2..0eed816e60643efde356f7693ca761b9a018f878 100644 (file)
@@ -270,6 +270,12 @@ void InsetBranch::validate(LaTeXFeatures & features) const
 }
 
 
+bool InsetBranch::isMacroScope(Buffer const & buf) const 
+{
+       // Its own scope if not selected by buffer
+       return !isBranchSelected(buf);
+}
+
 
 string const InsetBranchMailer::name_("branch");
 
index 754e788cb6f594aec5f89c63de0f8002cdd7ebf3..30305d8235740d18e8b2cb28bf64035b98209321 100644 (file)
@@ -80,8 +80,10 @@ public:
        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 &);
index f741289801e583f9a413af9aa51dfc119e7949b1..0c3be7102aab6459b00188504c15de7a9bf7a932 100644 (file)
@@ -184,6 +184,7 @@ bool InsetCaption::insetAllowed(InsetCode code) const
        case WRAP_CODE:
        case CAPTION_CODE:
        case NEWPAGE_CODE:
+       case MATHMACRO_CODE:
                return false;
        default:
                return InsetText::insetAllowed(code);
index c6ae5a5eee210c5918ee470c045a4f70716483f0..fe747bc9e8b840db7e264d4d4a53d3e0edc0372d 100644 (file)
@@ -782,6 +782,22 @@ InsetCollapsable::Decoration InsetCollapsable::decoration() const
 }
 
 
+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
 {
index 999315a50657ef4f49deb60d051a1fc82b035f04..1b6cbc3c45edf1ea30dba459d737ba19781795ef 100644 (file)
@@ -76,6 +76,8 @@ public:
        /// 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() {}
index ee2728a768d9f86ba2ff340357e84cd02447517a..228d3130d75c7977ce3518dfd22dd927aad76447 100644 (file)
@@ -59,6 +59,8 @@ public:
        ///
        InsetCode lyxCode() const { return FLOAT_CODE; }
        ///
+       bool isMacroScope(Buffer const & buf) const { return true; }
+       ///
        int latex(Buffer const &, odocstream &,
                  OutputParams const &) const;
        ///
index 32d0f6c84c94f5101d7df3d661996474f652a636..3c7cc09ee49a238e642c639a3feb37ff93875ac2 100644 (file)
@@ -33,6 +33,8 @@ public:
        ///
        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
index ec2e81c727a993e5ecc64b76d355706402c6123a..42a61077fc3ceedc305efcce125c3b979c5f2659 100644 (file)
@@ -268,9 +268,7 @@ docstring const InsetInclude::getScreenLabel(Buffer const & buf) const
 }
 
 
-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))
@@ -289,9 +287,6 @@ Buffer * getChildBuffer(Buffer const & buffer, InsetCommandParams const & params
                return childBuffer;
 }
 
-} // namespace anon
-
-
 /// return true if the file is or got loaded.
 Buffer * loadIfNeeded(Buffer const & parent, InsetCommandParams const & params)
 {
index f495d1d9ed8eb612c07754a253e3fae9c58c54e4..cddb631908be8564d271508ad3e75996779b0030 100644 (file)
@@ -131,7 +131,9 @@ private:
        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);
 
index d2b63e7110cc687bc49f7dff896d06a190e80c3b..f5cf33ce886957188e5f1c6594c9e9e0f356002a 100644 (file)
@@ -45,6 +45,8 @@ public:
        ///
        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;
index 9aa037dba3187f6e12337fd100e24e8fc2e37f03..94463a64decc44880a0602962359b269fca6cb39 100644 (file)
@@ -218,6 +218,16 @@ void InsetNote::updateLabels(Buffer const & buf, ParIterator const & it)
 }
 
 
+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
 {
index 11fe68f7832a8ea841e3ec26b4f6ca0d9934524b..1d51ee74d18488186acdb830d5b09136b627b40a 100644 (file)
@@ -62,6 +62,8 @@ public:
        /// 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;
index fb0dc7b48295ecc86f1ac98fbebeaecaa0ff4877..b8f8b5562a178b1a736a5cb821e8c7505773b99a 100644 (file)
@@ -2897,6 +2897,15 @@ void InsetTabular::buffer(Buffer const * b)
 }
 
 
+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;
index 8950077de263bd4dc71f0e2d4b9ed13f02ef4795..c3a64cea937ed98d515b0ace9b1935789f57507d 100644 (file)
@@ -679,7 +679,7 @@ public:
        ///
        EDITABLE editable() const { return HIGHLY_EDITABLE; }
        ///
-       bool insetAllowed(InsetCode) const { return true; }
+       bool insetAllowed(InsetCode code) const;
        ///
        bool allowSpellCheck() const { return true; }
        ///
index f6bb2112a971abfdf28dc9040437b4662efe9ac0..40efd2b7aaeeb632d84b96a9fe1439d25c090298 100644 (file)
@@ -130,6 +130,8 @@ public:
        /// 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 &);
index bf20efbd2db3c85f49b9c0ec9e06157cfa88732b..d22c3451c01de0900b1e9d487a3a154a4bd9f38e 100644 (file)
@@ -35,6 +35,8 @@ public:
        ///
        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;
        ///
index 01a9d7645fa0f2df6299e14a2f3b79c4e0a271fb..4202469f05420d9087133420044ad2ab88789986 100644 (file)
@@ -57,6 +57,8 @@ public:
        ///
        InsetCode lyxCode() const { return WRAP_CODE; }
        ///
+       bool isMacroScope(Buffer const & buf) const { return true; }
+       ///
        int latex(Buffer const &, odocstream &,
                  OutputParams const &) const;
        ///
index 34ecff40a15f207f63412901c2ccf7273587b46f..8406c77193f9a632be8acc58fa326c953aedac44 100644 (file)
@@ -208,6 +208,7 @@ int replace(BufferView * bv, docstring const & searchstr,
        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;
index 0844b4d8a9ad8c3bc6c9794925c651653fbc64b5..00d4e2ac6e0c7759f4e48fd7cd99221dc907f65d 100644 (file)
@@ -994,12 +994,13 @@ void InsetMathNest::doDispatch(Cursor & cur, FuncRequest & cmd)
                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;
        }
 
index 7dea5f93413dec02a11f085e437594d96c8b30a5..406e901c49d0f3503f9606e379540b81c866e9e4 100644 (file)
 
 #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"
@@ -38,24 +41,32 @@ namespace lyx {
 /////////////////////////////////////////////////////////////////////
 
 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));
@@ -81,12 +92,14 @@ void MacroData::expand(vector<MathData> const & args, MathData & to) const
 
 size_t MacroData::optionals() const
 {
+       updateData();
        return optionals_;
 }
 
 
 vector<docstring> const &  MacroData::defaults() const
 {
+       updateData();
        return defaults_;
 }
 
@@ -98,6 +111,63 @@ void MacroData::unlock() const
 }
 
 
+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
@@ -111,17 +181,10 @@ MacroTable & MacroTable::globalMacros()
 }
 
 
-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;
 }
 
 
@@ -136,7 +199,7 @@ void MacroTable::insert(docstring const & def, string const & requires)
 {
        //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);
 }
@@ -154,38 +217,21 @@ void MacroTable::dump()
 }
 
 
-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
index 709fd3b61fcc35a80f09ae7b0994d97b2286bf78..5fa88cee2b2e43eb996126af9d803f8b4aabe3fc 100644 (file)
 #ifndef MATH_MACROTABLE_H
 #define MATH_MACROTABLE_H
 
+#include "DocIterator.h"
+
 #include "support/docstring.h"
+#include "support/types.h"
 
 #include <map>
 #include <vector>
@@ -22,23 +25,32 @@ namespace lyx {
 
 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;
@@ -47,9 +59,9 @@ public:
        ///
        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_; }
@@ -62,9 +74,19 @@ public:
        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_
@@ -77,24 +99,43 @@ public:
 
 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,
@@ -109,10 +150,8 @@ public:
        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();
 
@@ -131,25 +170,17 @@ public:
  **/
 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
index ff0cff0340ee169fdbd63c71beb8e16519ae5722..1d40598dff076c3b7bb120b3712d16cf80cb413f 100644 (file)
@@ -247,7 +247,8 @@ void MathData::metrics(MetricsInfo & mi, Dimension & dim) const
                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;
@@ -322,10 +323,8 @@ void MathData::drawT(TextPainter & pain, int x, int y) const
 }
 
 
-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();
@@ -333,7 +332,7 @@ void MathData::updateMacros(MetricsInfo & mi)
                        continue;
                
                // get macro
-               macroInset->updateMacro(mi);
+               macroInset->updateMacro(mc);
                size_t macroNumArgs = 0;
                size_t macroOptionals = 0;
                MacroData const * macro = macroInset->macro();
@@ -345,13 +344,14 @@ void MathData::updateMacros(MetricsInfo & mi)
                // 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);
                }
 
@@ -362,9 +362,11 @@ void MathData::updateMacros(MetricsInfo & mi)
                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
@@ -389,9 +391,11 @@ void MathData::updateMacros(MetricsInfo & mi)
                        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.
@@ -401,40 +405,42 @@ void MathData::updateMacros(MetricsInfo & mi)
                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?
@@ -447,7 +453,7 @@ void MathData::detachMacroParameters(Cursor & cur, const size_type macroPos)
                // 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;
                }
                
@@ -484,24 +490,26 @@ void MathData::detachMacroParameters(Cursor & cur, const size_type macroPos)
                // 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;
@@ -509,24 +517,26 @@ void MathData::detachMacroParameters(Cursor & cur, const size_type macroPos)
                // 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)
@@ -540,10 +550,12 @@ void MathData::attachMacroParameters(Cursor & cur,
        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) {
@@ -564,8 +576,8 @@ void MathData::attachMacroParameters(Cursor & cur,
                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 
@@ -582,24 +594,24 @@ void MathData::attachMacroParameters(Cursor & cur,
 
        // 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)
 {
@@ -608,7 +620,7 @@ void MathData::collectOptionalParameters(Cursor & cur,
                // 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) {
@@ -636,29 +648,28 @@ void MathData::collectOptionalParameters(Cursor & cur,
                
                // 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) 
@@ -670,9 +681,8 @@ void MathData::collectParameters(Cursor & cur,
                // 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();
@@ -701,7 +711,8 @@ void MathData::collectParameters(Cursor & cur,
                        // 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
@@ -712,9 +723,9 @@ void MathData::collectParameters(Cursor & cur,
                
                // 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;
index 24f08c545b6e72736862ea0542ca86fb40bf0fa1..1ddf623685218ff3e0dc5b0a849d12b34fdc9b4b 100644 (file)
@@ -27,8 +27,10 @@ namespace lyx {
 
 class BufferView;
 class Cursor;
+class DocIterator;
 class LaTeXFeatures;
 class ReplaceData;
+class MacroContext;
 class MathMacro;
 class MetricsInfo;
 class PainterInfo;
@@ -154,6 +156,10 @@ public:
        ///
        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_;
@@ -166,20 +172,18 @@ private:
        /// 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);
index 27904af557c76a0ffbd4990fe5aa3a02eddaf2ff..5e8b4784597d68820e241cb07d04483658c3e3f5 100644 (file)
@@ -213,11 +213,11 @@ int MathMacro::kerning() const {
 }
 
 
-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;
                }
@@ -227,48 +227,60 @@ void MathMacro::updateMacro(MetricsInfo & mi)
 }
 
 
-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));
+       }               
 }
 
 
@@ -359,7 +371,7 @@ void MathMacro::setDisplayMode(MathMacro::DisplayMode mode)
 }
 
 
-MathMacro::DisplayMode MathMacro::computeDisplayMode(MetricsInfo const &) const
+MathMacro::DisplayMode MathMacro::computeDisplayMode() const
 {
        if (nextFoldMode_ == true && macro_ && !macro_->locked())
                return DISPLAY_NORMAL;
@@ -531,54 +543,63 @@ bool MathMacro::folded() const
 
 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);
 }
 
 
index 0209ac6a36394c112e7d1e74c635f430e2125192..bab83a44939b0d8650f0101af3110349776d8872 100644 (file)
@@ -127,11 +127,11 @@ protected:
        /// 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);
index fb0b15d659d01813b93609c066132d0bd264b939..77cf8350dd87f347225acd6818d769e9bef42b68 100644 (file)
@@ -90,18 +90,20 @@ void InsetMathWrapper::draw(PainterInfo & pi, int x, int y) const
 
 
 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();
 
@@ -150,6 +152,12 @@ docstring MathMacroTemplate::name() const
 }
 
 
+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"));
@@ -157,13 +165,11 @@ void MathMacroTemplate::metrics(MetricsInfo & mi, Dimension & dim) const
 
        // 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}:="
@@ -220,7 +226,7 @@ void MathMacroTemplate::metrics(MetricsInfo & mi, Dimension & dim) const
        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);
 }
 
@@ -598,7 +604,8 @@ bool MathMacroTemplate::getStatus(Cursor & /*cur*/, FuncRequest const & cmd,
                        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;
                }
@@ -616,11 +623,14 @@ bool MathMacroTemplate::getStatus(Cursor & /*cur*/, FuncRequest const & cmd,
                }
 
                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:
@@ -632,7 +642,8 @@ bool MathMacroTemplate::getStatus(Cursor & /*cur*/, FuncRequest const & cmd,
                        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:
@@ -668,13 +679,23 @@ void MathMacroTemplate::write(Buffer const &, ostream & os) const
 
 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_ << ']';
                
@@ -754,14 +775,61 @@ bool MathMacroTemplate::validMacro() const
 }
 
 
-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
index b90c8a96cb0c01d4f283118c3befef7fa0c14d8c..c14bfcb3ebade4af9ebeb024efc737c91634ced1 100644 (file)
@@ -29,7 +29,7 @@ public:
        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());
@@ -43,18 +43,43 @@ public:
        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;
        ///
@@ -100,10 +125,14 @@ private:
        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_;
 };
 
 
index d2c4d762edfd0bed9e35f47e86a55b22abe2b3d3..ebeebd36527ef844efbf18aea9ef986382feaa1b 100644 (file)
@@ -896,12 +896,14 @@ void Parser::parse1(InsetMathGrid & grid, unsigned flags,
                        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();
 
@@ -914,7 +916,7 @@ void Parser::parse1(InsetMathGrid & grid, unsigned flags,
                                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;