X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Fmathed%2FMathData.cpp;h=8fcf7466e34c825cfbe07f7badab5ca09befa1ee;hb=55a3dd7b346d29a52ba305a4558e9e380ef50f47;hp=ff0cff0340ee169fdbd63c71beb8e16519ae5722;hpb=9abb7db46800e554f57e865a3e768602ffd9d6f1;p=lyx.git diff --git a/src/mathed/MathData.cpp b/src/mathed/MathData.cpp index ff0cff0340..8fcf7466e3 100644 --- a/src/mathed/MathData.cpp +++ b/src/mathed/MathData.cpp @@ -3,7 +3,7 @@ * This file is part of LyX, the document processor. * Licence details can be found in the file COPYING. * - * \author André Pönitz + * \author André Pönitz * \author Stefan Schimanski * * Full author contact details are available in file CREDITS. @@ -27,36 +27,40 @@ #include "BufferView.h" #include "CoordCache.h" #include "Cursor.h" -#include "support/debug.h" +#include "mathed/InsetMathUnknown.h" + +#include "support/debug.h" #include "support/docstream.h" #include "frontends/FontMetrics.h" #include "frontends/Painter.h" -#include +#include "support/lassert.h" #include +#include + using namespace std; namespace lyx { -MathData::MathData(const_iterator from, const_iterator to) - : base_type(from, to) +MathData::MathData(Buffer * buf, const_iterator from, const_iterator to) + : base_type(from, to), buffer_(buf) {} MathAtom & MathData::operator[](pos_type pos) { - BOOST_ASSERT(pos < size()); + LASSERT(pos < size(), /**/); return base_type::operator[](pos); } MathAtom const & MathData::operator[](pos_type pos) const { - BOOST_ASSERT(pos < size()); + LASSERT(pos < size(), /**/); return base_type::operator[](pos); } @@ -69,7 +73,7 @@ void MathData::insert(size_type pos, MathAtom const & t) void MathData::insert(size_type pos, MathData const & ar) { - BOOST_ASSERT(pos <= size()); + LASSERT(pos <= size(), /**/); base_type::insert(begin() + pos, ar.begin(), ar.end()); } @@ -247,19 +251,36 @@ void MathData::metrics(MetricsInfo & mi, Dimension & dim) const return; } - const_cast(this)->updateMacros(mi); + Cursor & cur = mi.base.bv->cursor(); + const_cast(this)->updateMacros(&cur, mi.macrocontext, InternalUpdate); + + DocIterator const & inlineCompletionPos = mi.base.bv->inlineCompletionPos(); + MathData const * inlineCompletionData = 0; + if (inlineCompletionPos.inMathed()) + inlineCompletionData = &inlineCompletionPos.cell(); dim.asc = 0; dim.wid = 0; Dimension d; - atom_dims_.clear(); - for (size_t i = 0, n = size(); i != n; ++i) { + CoordCacheBase & coords = mi.base.bv->coordCache().insets(); + for (pos_type i = 0, n = size(); i != n; ++i) { MathAtom const & at = operator[](i); at->metrics(mi, d); - atom_dims_.push_back(d); + coords.add(at.nucleus(), d); dim += d; if (i == n - 1) - kerning_ = at->kerning(); + kerning_ = at->kerning(mi.base.bv); + + // HACK to draw completion suggestion inline + if (inlineCompletionData != this + || size_t(inlineCompletionPos.pos()) != i + 1) + continue; + + docstring const & completion = mi.base.bv->inlineCompletion(); + if (completion.length() == 0) + continue; + + dim.wid += mathed_string_width(mi.base.font, completion); } // Cache the dimension. mi.base.bv->coordCache().arrays().add(this, dim); @@ -286,12 +307,46 @@ void MathData::draw(PainterInfo & pi, int x, int y) const || x >= bv. workWidth()) return; + DocIterator const & inlineCompletionPos = bv.inlineCompletionPos(); + MathData const * inlineCompletionData = 0; + if (inlineCompletionPos.inMathed()) + inlineCompletionData = &inlineCompletionPos.cell(); + + CoordCacheBase & coords = pi.base.bv->coordCache().insets(); for (size_t i = 0, n = size(); i != n; ++i) { MathAtom const & at = operator[](i); - bv.coordCache().insets().add(at.nucleus(), x, y); + coords.add(at.nucleus(), x, y); at->drawSelection(pi, x, y); at->draw(pi, x, y); - x += atom_dims_[i].wid; + x += coords.dim(at.nucleus()).wid; + + // Is the inline completion here? + if (inlineCompletionData != this + || size_t(inlineCompletionPos.pos()) != i + 1) + continue; + docstring const & completion = bv.inlineCompletion(); + if (completion.length() == 0) + continue; + FontInfo f = pi.base.font; + + // draw the unique and the non-unique completion part + // Note: this is not time-critical as it is + // only done once per screen. + size_t uniqueTo = bv.inlineCompletionUniqueChars(); + docstring s1 = completion.substr(0, uniqueTo); + docstring s2 = completion.substr(uniqueTo); + + if (s1.size() > 0) { + f.setColor(Color_inlinecompletion); + pi.pain.text(x, y, s1, f); + x += mathed_string_width(f, s1); + } + + if (s2.size() > 0) { + f.setColor(Color_nonunique_inlinecompletion); + pi.pain.text(x, y, s2, f); + x += mathed_string_width(f, s2); + } } } @@ -322,18 +377,38 @@ void MathData::drawT(TextPainter & pain, int x, int y) const } -void MathData::updateMacros(MetricsInfo & mi) +void MathData::updateBuffer(ParIterator const & it, UpdateType utype) { - Cursor & cur = mi.base.bv->cursor(); + // pass down + for (size_t i = 0, n = size(); i != n; ++i) { + MathAtom & at = operator[](i); + at.nucleus()->updateBuffer(it, utype); + } +} + + +void MathData::updateMacros(Cursor * cur, MacroContext const & mc, + UpdateType utype) +{ + // If we are editing a macro, we cannot update it immediately, + // otherwise wrong undo steps will be recorded (bug 6208). + InsetMath const * inmath = cur ? cur->inset().asInsetMath() : 0; + MathMacro const * inmacro = inmath ? inmath->asMacro() : 0; + docstring const edited_name = inmacro ? inmacro->name() : docstring(); // go over the array and look for macros for (size_t i = 0; i < size(); ++i) { MathMacro * macroInset = operator[](i).nucleus()->asMacro(); - if (!macroInset) + if (!macroInset || macroInset->name_.empty() + || macroInset->name_[0] == '^' + || macroInset->name_[0] == '_' + || (macroInset->name() == edited_name + && macroInset->displayMode() == + MathMacro::DISPLAY_UNFOLDED)) continue; - + // get macro - macroInset->updateMacro(mi); + macroInset->updateMacro(mc); size_t macroNumArgs = 0; size_t macroOptionals = 0; MacroData const * macro = macroInset->macro(); @@ -345,14 +420,18 @@ 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)) { + && (macroInset->arity() != macroNumArgs + || macroInset->optionals() != macroOptionals + || newDisplayMode == MathMacro::DISPLAY_UNFOLDED)) { + detachMacroParameters(cur, i); + // FIXME: proper anchor handling, this removes the selection + if (cur) + cur->clearSelection(); } // the macro could have been copied while resizing this @@ -360,25 +439,28 @@ void MathData::updateMacros(MetricsInfo & mi) // Cursor in \label? if (newDisplayMode != MathMacro::DISPLAY_UNFOLDED - && oldDisplayMode == 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 + size_t appetite = macroInset->appetite(); macroInset->setDisplayMode(newDisplayMode); // arity changed? if (newDisplayMode == MathMacro::DISPLAY_NORMAL - && (macroInset->arity() != macroNumArgs - || macroInset->optionals() != macroOptionals)) { + && (macroInset->arity() != macroNumArgs + || macroInset->optionals() != macroOptionals)) { // is it a virgin macro which was never attached to parameters? bool fromInitToNormalMode = (oldDisplayMode == MathMacro::DISPLAY_INIT - || oldDisplayMode == MathMacro::DISPLAY_INTERACTIVE_INIT) - && newDisplayMode == MathMacro::DISPLAY_NORMAL; + || oldDisplayMode == MathMacro::DISPLAY_INTERACTIVE_INIT) + && newDisplayMode == MathMacro::DISPLAY_NORMAL; // if the macro was entered interactively (i.e. not by paste or during // loading), it should not be greedy, but the cursor should @@ -387,11 +469,13 @@ void MathData::updateMacros(MetricsInfo & mi) // attach parameters attachMacroParameters(cur, i, macroNumArgs, macroOptionals, - fromInitToNormalMode, interactive); + fromInitToNormalMode, interactive, appetite); - // 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. @@ -400,41 +484,43 @@ void MathData::updateMacros(MetricsInfo & mi) InsetMath * inset = operator[](i).nucleus(); if (inset->asScriptInset()) inset = inset->asScriptInset()->nuc()[0].nucleus(); - BOOST_ASSERT(inset->asMacro()); - inset->asMacro()->updateRepresentation(mi); + LASSERT(inset->asMacro(), /**/); + inset->asMacro()->updateRepresentation(cur, mc, utype); } } -void MathData::detachMacroParameters(Cursor & cur, const size_type macroPos) +void MathData::detachMacroParameters(DocIterator * cur, const size_type macroPos) { MathMacro * macroInset = operator[](macroPos).nucleus()->asMacro(); // detach all arguments vector 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 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 +533,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; } @@ -461,7 +547,7 @@ void MathData::detachMacroParameters(Cursor & cur, const size_type macroPos) for (size_t q = 0; q < arg.size(); ++q) { if (arg[q]->getChar() == ']') { // put brace - brace = new InsetMathBrace(); + brace = new InsetMathBrace(buffer_); break; } } @@ -484,24 +570,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,27 +597,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) + 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) + const bool interactiveInit, const size_t appetite) { MathMacro * macroInset = operator[](macroPos).nucleus()->asMacro(); @@ -538,41 +625,50 @@ void MathData::attachMacroParameters(Cursor & cur, size_t p = macroPos + 1; vector detachedArgs; 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) { collectOptionalParameters(cur, macroOptionals, detachedArgs, p, - macroPos, thisPos, thisSlice); - collectParameters(cur, macroNumArgs, detachedArgs, p, scriptToPutAround, macroPos, thisPos, thisSlice); } - + collectParameters(cur, macroNumArgs, detachedArgs, p, + scriptToPutAround, macroPos, thisPos, thisSlice, appetite); + // attach arguments back to macro inset macroInset->attachArguments(detachedArgs, macroNumArgs, macroOptionals); - + // found tail script? E.g. \foo{a}b^x if (scriptToPutAround.nucleus()) { + InsetMathScript * scriptInset = + scriptToPutAround.nucleus()->asScriptInset(); + // In the math parser we remove empty braces in the base + // of a script inset, but we have to restore them here. + if (scriptInset->nuc().empty()) { + MathData ar; + scriptInset->nuc().push_back( + MathAtom(new InsetMathBrace(ar))); + } // put macro into a script inset - scriptToPutAround.nucleus()->asScriptInset()->nuc()[0] - = operator[](macroPos); + scriptInset->nuc()[0] = operator[](macroPos); 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 - = operator[](macroPos).nucleus()->asScriptInset(); + scriptInset = operator[](macroPos).nucleus()->asScriptInset(); macroInset = scriptInset->nuc()[0].nucleus()->asMacro(); } - + // remove them from the MathData erase(begin() + macroPos + 1, begin() + p); @@ -582,39 +678,57 @@ 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 & params, - size_t & pos, const pos_type macroPos, const int thisPos, const int thisSlice) + size_t & pos, MathAtom & scriptToPutAround, + const pos_type macroPos, const int thisPos, const int thisSlice) { + Buffer * buf = cur ? cur->buffer() : 0; // insert optional arguments? - while (params.size() < numOptionalParams && pos < size()) { + while (params.size() < numOptionalParams + && pos < size() + && !scriptToPutAround.nucleus()) { // 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) { - if (operator[](right)->getChar() == ']') + MathAtom & cell = operator[](right); + + if (cell->getChar() == ']') // found right end break; + + // maybe "]" with a script around? + InsetMathScript * script = cell.nucleus()->asScriptInset(); + if (!script) + continue; + if (script->nuc().size() != 1) + continue; + if (script->nuc()[0]->getChar() == ']') { + // script will be put around the macro later + scriptToPutAround = cell; + break; + } } // found? @@ -624,7 +738,7 @@ void MathData::collectOptionalParameters(Cursor & cur, } // add everything between [ and ] as optional argument - MathData optarg(begin() + pos + 1, begin() + right); + MathData optarg(buf, begin() + pos + 1, begin() + right); // a brace? bool brace = false; @@ -636,43 +750,47 @@ 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 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 & params, size_t & pos, MathAtom & scriptToPutAround, - const pos_type macroPos, const int thisPos, const int thisSlice) + const pos_type macroPos, const int thisPos, const int thisSlice, + const size_t appetite) { + size_t startSize = params.size(); + // insert normal arguments - for (; params.size() < numParams && pos < size();) { + while (params.size() < numParams + && params.size() - startSize < appetite + && pos < size() + && !scriptToPutAround.nucleus()) { MathAtom & cell = operator[](pos); // fix cursor vector 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 +819,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 +831,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; @@ -722,45 +841,47 @@ void MathData::collectParameters(Cursor & cur, } -int MathData::pos2x(size_type pos) const +int MathData::pos2x(BufferView const * bv, size_type pos) const { - return pos2x(pos, 0); + return pos2x(bv, pos, 0); } -int MathData::pos2x(size_type pos, int glue) const +int MathData::pos2x(BufferView const * bv, size_type pos, int glue) const { int x = 0; size_type target = min(pos, size()); + CoordCacheBase const & coords = bv->coordCache().getInsets(); for (size_type i = 0; i < target; ++i) { const_iterator it = begin() + i; if ((*it)->getChar() == ' ') x += glue; //lyxerr << "char: " << (*it)->getChar() // << "width: " << (*it)->width() << endl; - x += atom_dims_[i].wid; + x += coords.dim((*it).nucleus()).wid; } return x; } -MathData::size_type MathData::x2pos(int targetx) const +MathData::size_type MathData::x2pos(BufferView const * bv, int targetx) const { - return x2pos(targetx, 0); + return x2pos(bv, targetx, 0); } -MathData::size_type MathData::x2pos(int targetx, int glue) const +MathData::size_type MathData::x2pos(BufferView const * bv, int targetx, int glue) const { const_iterator it = begin(); int lastx = 0; int currx = 0; + CoordCacheBase const & coords = bv->coordCache().getInsets(); // find first position after targetx for (; currx < targetx && it < end(); ++it) { lastx = currx; if ((*it)->getChar() == ' ') currx += glue; - currx += atom_dims_[it - begin()].wid; + currx += coords.dim((*it).nucleus()).wid; } /**