]> git.lyx.org Git - lyx.git/blobdiff - src/Paragraph.cpp
Add a member to TocItem that tells us whether the item in question
[lyx.git] / src / Paragraph.cpp
index fe7dc3e2e53d281dfba11a94ae7842afe69192ae..3ed382a23ea96912bb6cf6c04f604a39efc7be70 100644 (file)
@@ -43,7 +43,6 @@
 #include "TextClass.h"
 #include "TexRow.h"
 #include "Text.h"
-#include "VSpace.h"
 #include "WordLangTuple.h"
 #include "WordList.h"
 
@@ -70,8 +69,10 @@ using namespace lyx::support;
 namespace lyx {
 
 namespace {
+
 /// Inset identifier (above 0x10ffff, for ucs-4)
 char_type const META_INSET = 0x200001;
+
 }
 
 
@@ -130,12 +131,13 @@ private:
 
 class SpellCheckerState {
 public:
-       SpellCheckerState() {
+       SpellCheckerState()
+       {
                needs_refresh_ = true;
                current_change_number_ = 0;
        }
 
-       void setRange(FontSpan const fp, SpellChecker::Result state)
+       void setRange(FontSpan const fp, SpellChecker::Result state)
        {
                Ranges result;
                RangesIterator et = ranges_.end();
@@ -210,20 +212,24 @@ public:
                return empty_;
        }
 
-       bool needsRefresh() const {
+       bool needsRefresh() const
+       {
                return needs_refresh_;
        }
 
-       SpellChecker::ChangeNumber currentChangeNumber() const {
+       SpellChecker::ChangeNumber currentChangeNumber() const
+       {
                return current_change_number_;
        }
 
-       void refreshRange(pos_type & first, pos_type & last) const {
+       void refreshRange(pos_type & first, pos_type & last) const
+       {
                first = refresh_.first;
                last = refresh_.last;
        }
 
-       void needsRefresh(pos_type pos) {
+       void needsRefresh(pos_type pos)
+       {
                if (needs_refresh_ && pos != -1) {
                        if (pos < refresh_.first)
                                refresh_.first = pos;
@@ -238,13 +244,13 @@ public:
                needs_refresh_ = pos != -1;
        }
 
-       void needsCompleteRefresh(SpellChecker::ChangeNumber change_number) {
+       void needsCompleteRefresh(SpellChecker::ChangeNumber change_number)
+       {
                needs_refresh_ = true;
                refresh_.first = 0;
                refresh_.last = -1;
                current_change_number_ = change_number;
        }
-
 private:
        typedef vector<SpellResultRange> Ranges;
        typedef Ranges::const_iterator RangesIterator;
@@ -331,6 +337,7 @@ public:
        ///
        void latexSpecialChar(
                                   otexstream & os,
+                                  BufferParams const & bparams,
                                   OutputParams const & runparams,
                                   Font const & running_font,
                                   Change const & running_change,
@@ -346,6 +353,12 @@ public:
                pos_type i,
                unsigned int & column);
        ///
+       bool latexSpecialT3(
+               char_type const c,
+               otexstream & os,
+               pos_type i,
+               unsigned int & column);
+       ///
        bool latexSpecialTypewriter(
                char_type const c,
                otexstream & os,
@@ -378,9 +391,10 @@ public:
        Language * getSpellLanguage(pos_type const from) const;
 
        Language * locateSpellRange(pos_type & from, pos_type & to,
-                                                               SkipPositions & skips) const;
+                                   SkipPositions & skips) const;
 
-       bool hasSpellerChange() const {
+       bool hasSpellerChange() const
+       {
                SpellChecker::ChangeNumber speller_change_number = 0;
                if (theSpellChecker())
                        speller_change_number = theSpellChecker()->changeNumber();
@@ -399,14 +413,16 @@ public:
                speller_state_.setRange(fp, state);
        }
 
-       void requestSpellCheck(pos_type pos) {
+       void requestSpellCheck(pos_type pos)
+       {
                if (pos == -1)
                        speller_state_.needsCompleteRefresh(speller_state_.currentChangeNumber());
                else
                        speller_state_.needsRefresh(pos);
        }
 
-       void readySpellCheck() {
+       void readySpellCheck()
+       {
                speller_state_.needsRefresh(-1);
        }
 
@@ -569,9 +585,9 @@ Paragraph::Private::Private(Private const & p, Paragraph * owner,
 
 
 void Paragraph::addChangesToToc(DocIterator const & cdit,
-       Buffer const & buf) const
+       Buffer const & buf, bool output_active) const
 {
-       d->changes_.addToToc(cdit, buf);
+       d->changes_.addToToc(cdit, buf, output_active);
 }
 
 
@@ -853,8 +869,14 @@ int Paragraph::Private::latexSurrogatePair(otexstream & os, char_type c,
        // FIXME: change tracking
        // Is this correct WRT change tracking?
        Encoding const & encoding = *(runparams.encoding);
-       docstring const latex1 = encoding.latexChar(next);
-       docstring const latex2 = encoding.latexChar(c);
+       docstring latex1 = encoding.latexChar(next).first;
+       if (runparams.inIPA) {
+               string const tipashortcut = Encodings::TIPAShortcut(next);
+               if (!tipashortcut.empty()) {
+                       latex1 = from_ascii(tipashortcut);
+               }
+       }
+       docstring const latex2 = encoding.latexChar(c).first;
        if (docstring(1, next) == latex1) {
                // the encoding supports the combination
                os << latex2 << latex1;
@@ -970,7 +992,7 @@ int Paragraph::Private::writeScriptChars(otexstream & os,
                // Stop here if there is a font attribute or encoding change.
                if (found && cit != end && prev_font != cit->font())
                        break;
-               docstring const latex = encoding.latexChar(next);
+               docstring const latex = encoding.latexChar(next).first;
                docstring::size_type const b1 =
                                        latex.find_first_of(from_ascii("{"));
                docstring::size_type const b2 =
@@ -1024,7 +1046,9 @@ void Paragraph::Private::latexInset(BufferParams const & bparams,
        LASSERT(inset, /**/);
 
        if (style.pass_thru) {
-               inset->plaintext(os.os(), runparams);
+               odocstringstream ods;
+               inset->plaintext(ods, runparams);
+               os << ods.str();
                return;
        }
 
@@ -1071,6 +1095,7 @@ void Paragraph::Private::latexInset(BufferParams const & bparams,
        odocstream::pos_type const len = os.os().tellp();
 
        if (inset->forceLTR()
+           && !runparams.use_polyglossia
            && running_font.isRightToLeft()
            // ERT is an exception, it should be output with no
            // decorations at all
@@ -1150,6 +1175,7 @@ void Paragraph::Private::latexInset(BufferParams const & bparams,
 
 
 void Paragraph::Private::latexSpecialChar(otexstream & os,
+                                         BufferParams const & bparams,
                                          OutputParams const & runparams,
                                          Font const & running_font,
                                          Change const & running_change,
@@ -1158,29 +1184,36 @@ void Paragraph::Private::latexSpecialChar(otexstream & os,
                                          pos_type end_pos,
                                          unsigned int & column)
 {
-       char_type const c = text_[i];
+       // With polyglossia, brackets and stuff need not be reversed
+       // in RTL scripts (see bug #8251)
+       char_type const c = (runparams.use_polyglossia) ?
+               owner_->getUChar(bparams, i) : text_[i];
 
        if (style.pass_thru || runparams.pass_thru) {
                if (c != '\0') {
                        Encoding const * const enc = runparams.encoding;
-                       if (enc && enc->latexChar(c, true).empty())
+                       if (enc && !enc->encodable(c))
                                throw EncodingException(c);
                        os.put(c);
                }
                return;
        }
 
+       // TIPA uses its own T3 encoding
+       if (runparams.inIPA && latexSpecialT3(c, os, i, column))
+               return;
        // If T1 font encoding is used, use the special
        // characters it provides.
        // NOTE: some languages reset the font encoding
        // internally
-       if (!running_font.language()->internalFontEncoding()
+       if (!runparams.inIPA && !running_font.language()->internalFontEncoding()
            && lyxrc.fontenc == "T1" && latexSpecialT1(c, os, i, column))
                return;
 
        // \tt font needs special treatment
-       if (running_font.fontInfo().family() == TYPEWRITER_FAMILY
-               && latexSpecialTypewriter(c, os, i, column))
+       if (!runparams.inIPA
+            && running_font.fontInfo().family() == TYPEWRITER_FAMILY
+            && latexSpecialTypewriter(c, os, i, column))
                return;
 
        // Otherwise, we use what LaTeX provides us.
@@ -1254,8 +1287,9 @@ void Paragraph::Private::latexSpecialChar(otexstream & os,
                        return;
 
                Encoding const & encoding = *(runparams.encoding);
+               char_type next = '\0';
                if (i + 1 < int(text_.size())) {
-                       char_type next = text_[i + 1];
+                       next = text_[i + 1];
                        if (Encodings::isCombiningChar(next)) {
                                column += latexSurrogatePair(os, c, next, runparams) - 1;
                                ++i;
@@ -1263,20 +1297,50 @@ void Paragraph::Private::latexSpecialChar(otexstream & os,
                        }
                }
                string script;
-               docstring const latex = encoding.latexChar(c);
+               pair<docstring, bool> latex = encoding.latexChar(c);
+               docstring nextlatex;
+               bool nexttipas = false;
+               string nexttipashortcut;
+               if (next != '\0' && next != META_INSET) {
+                       nextlatex = encoding.latexChar(next).first;
+                       if (runparams.inIPA) {
+                               nexttipashortcut = Encodings::TIPAShortcut(next);
+                               nexttipas = !nexttipashortcut.empty();
+                       }
+               }
+               bool tipas = false;
+               if (runparams.inIPA) {
+                       string const tipashortcut = Encodings::TIPAShortcut(c);
+                       if (!tipashortcut.empty()) {
+                               latex.first = from_ascii(tipashortcut);
+                               latex.second = false;
+                               tipas = true;
+                       }
+               }
                if (Encodings::isKnownScriptChar(c, script)
-                   && prefixIs(latex, from_ascii("\\" + script)))
-                       column += writeScriptChars(os, latex,
+                   && prefixIs(latex.first, from_ascii("\\" + script)))
+                       column += writeScriptChars(os, latex.first,
                                        running_change, encoding, i) - 1;
-               else if (latex.length() > 1 && latex[latex.length() - 1] != '}') {
+               else if (latex.second
+                        && ((!prefixIs(nextlatex, '\\')
+                              && !prefixIs(nextlatex, '{')
+                              && !prefixIs(nextlatex, '}'))
+                            || (nexttipas
+                                && !prefixIs(from_ascii(nexttipashortcut), '\\')))
+                        && !tipas) {
                        // Prevent eating of a following
                        // space or command corruption by
                        // following characters
-                       column += latex.length() + 1;
-                       os << latex << "{}";
+                       if (next == ' ' || next == '\0') {
+                               column += latex.first.length() + 1;
+                               os << latex.first << "{}";
+                       } else {
+                               column += latex.first.length();
+                               os << latex.first << " ";
+                       }
                } else {
-                       column += latex.length() - 1;
-                       os << latex;
+                       column += latex.first.length() - 1;
+                       os << latex.first;
                }
                break;
        }
@@ -1311,6 +1375,26 @@ bool Paragraph::Private::latexSpecialT1(char_type const c, otexstream & os,
 }
 
 
+bool Paragraph::Private::latexSpecialT3(char_type const c, otexstream & os,
+       pos_type /*i*/, unsigned int & column)
+{
+       switch (c) {
+       case '*':
+       case '[':
+       case ']':
+       case '\"':
+               os.put(c);
+               return true;
+       case '|':
+               os << "\\textvertline{}";
+               column += 14;
+               return true;
+       default:
+               return false;
+       }
+}
+
+
 bool Paragraph::Private::latexSpecialTypewriter(char_type const c, otexstream & os,
        pos_type i, unsigned int & column)
 {
@@ -1379,18 +1463,25 @@ void Paragraph::Private::validate(LaTeXFeatures & features) const
                        // we have to provide all the optional arguments here, even though
                        // the last one is the only one we care about.
                        // Separate handling of optional argument inset.
-                       if (layout_->optargs != 0 || layout_->reqargs != 0)
-                               latexArgInsets(*owner_, os, features.runparams(),
-                                       layout_->reqargs, layout_->optargs);
-                       else
-                               os << from_ascii(layout_->latexparam());
+                       if (!layout_->latexargs().empty()) {
+                               OutputParams rp = features.runparams();
+                               rp.local_font = &owner_->getFirstFontSettings(bp);
+                               latexArgInsets(*owner_, os, rp, layout_->latexargs());
+                       }
+                       os << from_ascii(layout_->latexparam());
                }
                docstring::size_type const length = ods.str().length();
                // this will output "{" at the beginning, but not at the end
                owner_->latex(bp, f, os, features.runparams(), 0, -1, true);
                if (ods.str().length() > length) {
-                       if (is_command)
+                       if (is_command) {
                                ods << '}';
+                               if (!layout_->postcommandargs().empty()) {
+                                       OutputParams rp = features.runparams();
+                                       rp.local_font = &owner_->getFirstFontSettings(bp);
+                                       latexArgInsets(*owner_, os, rp, layout_->postcommandargs(), "post:");
+                               }
+                       }
                        string const snippet = to_utf8(ods.str());
                        features.addPreambleSnippet(snippet);
                }
@@ -1398,7 +1489,7 @@ void Paragraph::Private::validate(LaTeXFeatures & features) const
 
        if (features.runparams().flavor == OutputParams::HTML
            && layout_->htmltitle()) {
-               features.setHTMLTitle(owner_->asString(AS_STR_INSETS));
+               features.setHTMLTitle(owner_->asString(AS_STR_INSETS | AS_STR_SKIPDELETE));
        }
 
        // check the params.
@@ -1839,16 +1930,23 @@ FontSize Paragraph::highestFontInRange
 char_type Paragraph::getUChar(BufferParams const & bparams, pos_type pos) const
 {
        char_type c = d->text_[pos];
-       if (!lyxrc.rtl_support)
+       if (!lyxrc.rtl_support || !getFontSettings(bparams, pos).isRightToLeft())
                return c;
 
+       // FIXME: The arabic special casing is due to the difference of arabic
+       // round brackets input introduced in r18599. Check if this should be
+       // unified with Hebrew or at least if all bracket types should be
+       // handled the same (file format change in either case).
+       string const & lang = getFontSettings(bparams, pos).language()->lang();
+       bool const arabic = lang == "arabic_arabtex" || lang == "arabic_arabi"
+               || lang == "farsi";
        char_type uc = c;
        switch (c) {
        case '(':
-               uc = ')';
+               uc = arabic ? c : ')';
                break;
        case ')':
-               uc = '(';
+               uc = arabic ? c : '(';
                break;
        case '[':
                uc = ']';
@@ -1869,9 +1967,8 @@ char_type Paragraph::getUChar(BufferParams const & bparams, pos_type pos) const
                uc = '<';
                break;
        }
-       if (uc != c && getFontSettings(bparams, pos).isRightToLeft())
-               return uc;
-       return c;
+
+       return uc;
 }
 
 
@@ -1989,8 +2086,7 @@ docstring Paragraph::expandParagraphLabel(Layout const & layout,
        bool const in_appendix = process_appendix && d->params_.appendix();
        docstring fmt = translateIfPossible(layout.labelstring(in_appendix), lang);
 
-       if (fmt.empty() && layout.labeltype == LABEL_COUNTER
-           && !layout.counter.empty())
+       if (fmt.empty() && !layout.counter.empty())
                return tclass.counters().theCounter(layout.counter, lang);
 
        // handle 'inherited level parts' in 'fmt',
@@ -2081,7 +2177,7 @@ bool Paragraph::usePlainLayout() const
 
 bool Paragraph::isPassThru() const
 {
-       return inInset().getLayout().isPassThru() || d->layout_->pass_thru;
+       return inInset().isPassThru() || d->layout_->pass_thru;
 }
 
 namespace {
@@ -2312,6 +2408,10 @@ void Paragraph::latex(BufferParams const & bparams,
        if (body_pos > 0) {
                // the optional argument is kept in curly brackets in
                // case it contains a ']'
+               // This is not strictly needed, but if this is changed it
+               // would be a file format change, and tex2lyx would need
+               // to be adjusted, since it unconditionally removes the
+               // braces when it parses \item.
                os << "[{";
                column += 2;
                basefont = getLabelFont(bparams, outerfont);
@@ -2336,6 +2436,10 @@ void Paragraph::latex(BufferParams const & bparams,
                        os << '{';
                        ++column;
                }
+               if (!style.leftdelim().empty()) {
+                       os << style.leftdelim();
+                       column += style.leftdelim().size();
+               }
                if (allowcust)
                        column += d->startTeXParParams(bparams, os, runparams);
        }
@@ -2366,6 +2470,11 @@ void Paragraph::latex(BufferParams const & bparams,
                                ++column;
                        }
 
+                       if (!style.leftdelim().empty()) {
+                               os << style.leftdelim();
+                               column += style.leftdelim().size();
+                       }
+
                        if (allowcust)
                                column += d->startTeXParParams(bparams, os,
                                                            runparams);
@@ -2505,7 +2614,7 @@ void Paragraph::latex(BufferParams const & bparams,
                } else {
                        if (i >= start_pos && (end_pos == -1 || i < end_pos)) {
                                try {
-                                       d->latexSpecialChar(os, rp, running_font, runningChange,
+                                       d->latexSpecialChar(os, bparams, rp, running_font, runningChange,
                                                            style, i, end_pos, column);
                                } catch (EncodingException & e) {
                                if (runparams.dryrun) {
@@ -2556,6 +2665,11 @@ void Paragraph::latex(BufferParams const & bparams,
                os << "}]~";
        }
 
+       if (!style.rightdelim().empty()) {
+               os << style.rightdelim();
+               column += style.rightdelim().size();
+       }
+
        if (allowcust && d->endTeXParParams(bparams, os, runparams)
            && runparams.encoding != prev_encoding) {
                runparams.encoding = prev_encoding;
@@ -2781,9 +2895,9 @@ docstring Paragraph::simpleLyXHTMLOnePar(Buffer const & buf,
                                retval += inset->xhtml(xs, np);
                        }
                } else {
-                       char_type c = d->text_[i];
+                       char_type c = getUChar(buf.params(), i);
 
-                       if (style.pass_thru)
+                       if (style.pass_thru || runparams.pass_thru)
                                xs << c;
                        else if (c == '-') {
                                docstring str;
@@ -2932,9 +3046,9 @@ void Paragraph::changeLanguage(BufferParams const & bparams,
                if (font.language() == from) {
                        font.setLanguage(to);
                        setFont(i, font);
+                       d->requestSpellCheck(i);
                }
        }
-       d->requestSpellCheck(size());
 }
 
 
@@ -3168,70 +3282,61 @@ char_type Paragraph::transformChar(char_type c, pos_type pos) const
 }
 
 
-int Paragraph::checkBiblio(Buffer const & buffer)
+bool Paragraph::brokenBiblio() const
 {
-       // FIXME From JS:
-       // This is getting more and more a mess. ...We really should clean
-       // up this bibitem issue for 1.6.
+       // there is a problem if there is no bibitem at position 0 or
+       // if there is another bibitem in the paragraph.
+       return d->layout_->labeltype == LABEL_BIBLIO
+               && (d->insetlist_.find(BIBITEM_CODE) != 0
+                   || d->insetlist_.find(BIBITEM_CODE, 1) > 0);
+}
+
+
+int Paragraph::fixBiblio(Buffer const & buffer)
+{
+       // FIXME: What about the case where paragraph is not BIBLIO
+       // but there is an InsetBibitem?
+       // FIXME: when there was already an inset at 0, the return value is 1,
+       // which does not tell whether another inset has been remove; the
+       // cursor cannot be correctly updated.
 
-       // Add bibitem insets if necessary
        if (d->layout_->labeltype != LABEL_BIBLIO)
                return 0;
 
-       bool hasbibitem = !d->insetlist_.empty()
-               // Insist on it being in pos 0
-               && d->text_[0] == META_INSET
-               && d->insetlist_.begin()->inset->lyxCode() == BIBITEM_CODE;
-
-       bool track_changes = buffer.params().trackChanges;
-
-       docstring oldkey;
-       docstring oldlabel;
-
-       // remove a bibitem in pos != 0
-       // restore it later in pos 0 if necessary
-       // (e.g. if a user inserts contents _before_ the item)
-       // we're assuming there's only one of these, which there
-       // should be.
-       int erasedInsetPosition = -1;
-       InsetList::iterator it = d->insetlist_.begin();
-       InsetList::iterator end = d->insetlist_.end();
-       for (; it != end; ++it)
-               if (it->inset->lyxCode() == BIBITEM_CODE
-                     && it->pos > 0) {
-                       InsetCommand * olditem = it->inset->asInsetCommand();
-                       oldkey = olditem->getParam("key");
-                       oldlabel = olditem->getParam("label");
-                       erasedInsetPosition = it->pos;
-                       eraseChar(erasedInsetPosition, track_changes);
-                       break;
-       }
+       bool const track_changes = buffer.params().trackChanges;
+       int bibitem_pos = d->insetlist_.find(BIBITEM_CODE);
+       bool const hasbibitem0 = bibitem_pos == 0;
 
-       // There was an InsetBibitem at the beginning, and we didn't
-       // have to erase one.
-       if (hasbibitem && erasedInsetPosition < 0)
+       if (hasbibitem0) {
+               bibitem_pos = d->insetlist_.find(BIBITEM_CODE, 1);
+               // There was an InsetBibitem at pos 0, and no other one => OK
+               if (bibitem_pos == -1)
                        return 0;
+               // there is a bibitem at the 0 position, but since
+               // there is a second one, we copy the second on the
+               // first. We're assuming there are at most two of
+               // these, which there should be.
+               // FIXME: why does it make sense to do that rather
+               // than keep the first? (JMarc)
+               Inset * inset = d->insetlist_.release(bibitem_pos);
+               eraseChar(bibitem_pos, track_changes);
+               d->insetlist_.begin()->inset = inset;
+               return -bibitem_pos;
+       }
+
+       // We need to create an inset at the beginning
+       Inset * inset = 0;
+       if (bibitem_pos > 0) {
+               // there was one somewhere in the paragraph, let's move it
+               inset = d->insetlist_.release(bibitem_pos);
+               eraseChar(bibitem_pos, track_changes);
+       } else
+               // make a fresh one
+               inset = new InsetBibitem(const_cast<Buffer *>(&buffer),
+                                        InsetCommandParams(BIBITEM_CODE));
 
-       // There was an InsetBibitem at the beginning and we did have to
-       // erase one. So we give its properties to the beginning inset.
-       if (hasbibitem) {
-               InsetCommand * inset = d->insetlist_.begin()->inset->asInsetCommand();
-               if (!oldkey.empty())
-                       inset->setParam("key", oldkey);
-               inset->setParam("label", oldlabel);
-               return -erasedInsetPosition;
-       }
-
-       // There was no inset at the beginning, so we need to create one with
-       // the key and label of the one we erased.
-       InsetBibitem * inset =
-               new InsetBibitem(const_cast<Buffer *>(&buffer), InsetCommandParams(BIBITEM_CODE));
-       // restore values of previously deleted item in this par.
-       if (!oldkey.empty())
-               inset->setParam("key", oldkey);
-       inset->setParam("label", oldlabel);
-       insertInset(0, inset,
-                   Change(track_changes ? Change::INSERTED : Change::UNCHANGED));
+       insertInset(0, inset, Change(track_changes ? Change::INSERTED 
+                                                  : Change::UNCHANGED));
 
        return 1;
 }
@@ -3486,16 +3591,13 @@ void Paragraph::locateWord(pos_type & from, pos_type & to,
 
 void Paragraph::collectWords()
 {
-       // This is the value that needs to be exposed in the preferences
-       // to resolve bug #6760.
-       static int minlength = 6;
        pos_type n = size();
        for (pos_type pos = 0; pos < n; ++pos) {
                if (isWordSeparator(pos))
                        continue;
                pos_type from = pos;
                locateWord(from, pos, WHOLE_WORD);
-               if (pos - from >= minlength) {
+               if ((pos - from) >= (int)lyxrc.completion_minlength) {
                        docstring word = asString(from, pos, AS_STR_NONE);
                        FontList::const_iterator cit = d->fontlist_.fontIterator(pos);
                        if (cit == d->fontlist_.end())
@@ -3663,7 +3765,7 @@ SpellChecker::Result Paragraph::spellCheck(pos_type & from, pos_type & to,
 
        wl = WordLangTuple(word, lang);
 
-       if (!word.size())
+       if (word.empty())
                return result;
 
        if (needsSpellCheck() || check_learned) {
@@ -3760,7 +3862,7 @@ void Paragraph::Private::markMisspelledWords(
 void Paragraph::spellCheck() const
 {
        SpellChecker * speller = theSpellChecker();
-       if (!speller || !size() ||!needsSpellCheck())
+       if (!speller || empty() ||!needsSpellCheck())
                return;
        pos_type start;
        pos_type endpos;