X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2FParagraph.cpp;h=e80ddc1de3070113d72a892d5c9cb0f967e91d2f;hb=7c6ae4d7401639ac0dd2eb83b0c1b7c61b0b8c13;hp=50b0bcfb00dca066acc1493986449a87547d0c96;hpb=d866717ef7503a1373dd1cb3925e1ac97b079192;p=lyx.git diff --git a/src/Paragraph.cpp b/src/Paragraph.cpp index 50b0bcfb00..e80ddc1de3 100644 --- a/src/Paragraph.cpp +++ b/src/Paragraph.cpp @@ -43,7 +43,6 @@ #include "TextClass.h" #include "TexRow.h" #include "Text.h" -#include "VSpace.h" #include "WordLangTuple.h" #include "WordList.h" @@ -112,7 +111,7 @@ public: if (range_.first > pos) { range_.first += offset; range_.last += offset; - } else if (range_.last > pos) { + } else if (range_.last >= pos) { range_.last += offset; } } @@ -135,7 +134,7 @@ public: 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(); @@ -312,8 +311,8 @@ public: OutputParams const &) const; /// This could go to ParagraphParameters if we want to. - int endTeXParParams(BufferParams const &, otexstream &, - OutputParams const &) const; + bool endTeXParParams(BufferParams const &, otexstream &, + OutputParams const &) const; /// void latexInset(BufferParams const &, @@ -331,11 +330,13 @@ public: /// void latexSpecialChar( otexstream & os, + BufferParams const & bparams, OutputParams const & runparams, Font const & running_font, Change const & running_change, Layout const & style, pos_type & i, + pos_type end_pos, unsigned int & column); /// @@ -345,6 +346,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, @@ -354,6 +361,7 @@ public: bool latexSpecialPhrase( otexstream & os, pos_type & i, + pos_type end_pos, unsigned int & column, OutputParams const & runparams); @@ -398,7 +406,10 @@ public: } void requestSpellCheck(pos_type pos) { - speller_state_.needsRefresh(pos); + if (pos == -1) + speller_state_.needsCompleteRefresh(speller_state_.currentChangeNumber()); + else + speller_state_.needsRefresh(pos); } void readySpellCheck() { @@ -848,8 +859,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; @@ -965,7 +982,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 = @@ -1066,6 +1083,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 @@ -1089,7 +1107,7 @@ void Paragraph::Private::latexInset(BufferParams const & bparams, // ArabTeX, though, cannot handle this special behavior, it seems. bool arabtex = basefont.language()->lang() == "arabic_arabtex" || running_font.language()->lang() == "arabic_arabtex"; - if (open_font && inset->noFontChange()) { + if (open_font && !inset->inheritFont()) { bool closeLanguage = arabtex || basefont.isRightToLeft() == running_font.isRightToLeft(); unsigned int count = running_font.latexWriteEndChanges(os, @@ -1115,6 +1133,8 @@ void Paragraph::Private::latexInset(BufferParams const & bparams, int prev_rows = os.texrow().rows(); try { + runparams.lastid = id_; + runparams.lastpos = i; inset->latex(os, runparams); } catch (EncodingException & e) { // add location information and throw again. @@ -1143,34 +1163,45 @@ 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, Layout const & style, pos_type & i, + 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') - // FIXME UNICODE: This can fail if c cannot - // be encoded in the current encoding. + if (c != '\0') { + Encoding const * const enc = runparams.encoding; + 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. @@ -1237,15 +1268,16 @@ void Paragraph::Private::latexSpecialChar(otexstream & os, default: // LyX, LaTeX etc. - if (latexSpecialPhrase(os, i, column, runparams)) + if (latexSpecialPhrase(os, i, end_pos, column, runparams)) return; if (c == '\0') 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; @@ -1253,20 +1285,50 @@ void Paragraph::Private::latexSpecialChar(otexstream & os, } } string script; - docstring const latex = encoding.latexChar(c); + pair 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; } @@ -1301,6 +1363,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) { @@ -1323,7 +1405,10 @@ bool Paragraph::Private::latexSpecialTypewriter(char_type const c, otexstream & } -bool Paragraph::Private::latexSpecialPhrase(otexstream & os, pos_type & i, +/// \param end_pos +/// If [start_pos, end_pos) does not include entirely the special phrase, then +/// do not apply the macro transformation. +bool Paragraph::Private::latexSpecialPhrase(otexstream & os, pos_type & i, pos_type end_pos, unsigned int & column, OutputParams const & runparams) { // FIXME: if we have "LaTeX" with a font @@ -1333,7 +1418,8 @@ bool Paragraph::Private::latexSpecialPhrase(otexstream & os, pos_type & i, // "words" for some definition of word for (size_t pnr = 0; pnr < phrases_nr; ++pnr) { - if (!isTextAt(special_phrases[pnr].phrase, i)) + if (!isTextAt(special_phrases[pnr].phrase, i) + || (end_pos != -1 && i + int(special_phrases[pnr].phrase.size()) > end_pos)) continue; if (runparams.moving_arg) os << "\\protect"; @@ -1384,7 +1470,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. @@ -1624,6 +1710,7 @@ void Paragraph::appendChar(char_type c, Font const & font, // when appending characters, no need to update tables d->text_.push_back(c); setFont(d->text_.size() - 1, font); + d->requestSpellCheck(d->text_.size() - 1); } @@ -1644,6 +1731,7 @@ void Paragraph::appendString(docstring const & s, Font const & font, for (size_t i = oldsize; i != newsize; ++i) { // track change d->changes_.insert(change, i); + d->requestSpellCheck(i); } d->fontlist_.set(oldsize, font); d->fontlist_.set(newsize - 1, font); @@ -1823,16 +1911,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 = ']'; @@ -1853,9 +1948,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; } @@ -2202,15 +2296,13 @@ int Paragraph::Private::startTeXParParams(BufferParams const & bparams, } -int Paragraph::Private::endTeXParParams(BufferParams const & bparams, +bool Paragraph::Private::endTeXParParams(BufferParams const & bparams, otexstream & os, OutputParams const & runparams) const { - int column = 0; - LyXAlignment const curAlign = params_.align(); if (curAlign == layout_->align) - return column; + return false; switch (curAlign) { case LYX_ALIGN_NONE: @@ -2222,13 +2314,12 @@ int Paragraph::Private::endTeXParParams(BufferParams const & bparams, case LYX_ALIGN_LEFT: case LYX_ALIGN_RIGHT: case LYX_ALIGN_CENTER: - if (runparams.moving_arg) { + if (runparams.moving_arg) os << "\\protect"; - column = 8; - } break; } + string output; string const end_tag = "\n\\par\\end"; InsetCode code = ownerCode(); bool const lastpar = runparams.isLastPar; @@ -2241,33 +2332,27 @@ int Paragraph::Private::endTeXParParams(BufferParams const & bparams, case LYX_ALIGN_DECIMAL: break; case LYX_ALIGN_LEFT: { - string output; if (owner_->getParLanguage(bparams)->babel() != "hebrew") output = corrected_env(end_tag, "flushleft", code, lastpar); else output = corrected_env(end_tag, "flushright", code, lastpar); os << from_ascii(output); - adjust_column(output, column); break; } case LYX_ALIGN_RIGHT: { - string output; if (owner_->getParLanguage(bparams)->babel() != "hebrew") output = corrected_env(end_tag, "flushright", code, lastpar); else output = corrected_env(end_tag, "flushleft", code, lastpar); os << from_ascii(output); - adjust_column(output, column); break; } case LYX_ALIGN_CENTER: { - string output; output = corrected_env(end_tag, "center", code, lastpar); os << from_ascii(output); - adjust_column(output, column); break; } } - return column; + return !output.empty() || lastpar; } @@ -2305,6 +2390,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); @@ -2364,8 +2453,8 @@ void Paragraph::latex(BufferParams const & bparams, runparams); } - Change const & change = runparams.inDeletedInset ? runparams.changeOfDeletedInset - : lookupChange(i); + Change const & change = runparams.inDeletedInset + ? runparams.changeOfDeletedInset : lookupChange(i); if (bparams.outputChanges && runningChange != change) { if (open_font) { @@ -2498,8 +2587,8 @@ 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, - style, i, column); + d->latexSpecialChar(os, bparams, rp, running_font, runningChange, + style, i, end_pos, column); } catch (EncodingException & e) { if (runparams.dryrun) { os << "<" << _("LyX Warning: ") @@ -2720,10 +2809,11 @@ docstring Paragraph::simpleLyXHTMLOnePar(Buffer const & buf, bool emph_flag = false; bool bold_flag = false; - string closing_tag; Layout const & style = *d->layout_; + xs.startParagraph(allowEmpty()); + if (!runparams.for_toc && runparams.html_make_pars) { // generate a magic label for this paragraph string const attr = "id='" + magicLabel() + "'"; @@ -2773,7 +2863,7 @@ 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) xs << c; @@ -2803,6 +2893,7 @@ docstring Paragraph::simpleLyXHTMLOnePar(Buffer const & buf, } xs.closeFontTags(); + xs.endParagraph(); return retval; } @@ -2834,14 +2925,40 @@ bool Paragraph::isLineSeparator(pos_type pos) const bool Paragraph::isWordSeparator(pos_type pos) const { - if (Inset const * inset = getInset(pos)) - return !inset->isLetter(); if (pos == size()) return true; + if (Inset const * inset = getInset(pos)) + return !inset->isLetter(); + // if we have a hard hyphen (no en- or emdash) or apostrophe + // we pass this to the spell checker + // FIXME: this method is subject to change, visit + // https://bugzilla.mozilla.org/show_bug.cgi?id=355178 + // to get an impression how complex this is. + if (isHardHyphenOrApostrophe(pos)) + return false; + char_type const c = d->text_[pos]; + // We want to pass the escape chars to the spellchecker + docstring const escape_chars = from_utf8(lyxrc.spellchecker_esc_chars); + return !isLetterChar(c) && !isDigitASCII(c) && !contains(escape_chars, c); +} + + +bool Paragraph::isHardHyphenOrApostrophe(pos_type pos) const +{ + pos_type const psize = size(); + if (pos >= psize) + return false; char_type const c = d->text_[pos]; - // We want to pass the ' and escape chars to the spellchecker - static docstring const quote = from_utf8(lyxrc.spellchecker_esc_chars + '\''); - return (!isLetterChar(c) && !isDigitASCII(c) && !contains(quote, c)); + if (c != '-' && c != '\'') + return false; + int nextpos = pos + 1; + int prevpos = pos > 0 ? pos - 1 : 0; + if ((nextpos == psize || isSpace(nextpos)) + && (pos == 0 || isSpace(prevpos))) + return false; + return c == '\'' + || ((nextpos == psize || d->text_[nextpos] != '-') + && (pos == 0 || d->text_[prevpos] != '-')); } @@ -2897,9 +3014,9 @@ void Paragraph::changeLanguage(BufferParams const & bparams, if (font.language() == from) { font.setLanguage(to); setFont(i, font); + d->requestSpellCheck(i); } } - d->requestSpellCheck(size()); } @@ -3133,70 +3250,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), + 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), 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; } @@ -3333,12 +3441,12 @@ int Paragraph::find(docstring const & str, bool cs, bool mw, int i = 0; pos_type const parsize = d->text_.size(); for (i = 0; i < strsize && pos < parsize; ++i, ++pos) { - // Ignore ligature break and hyphenation chars while searching + // Ignore "invisible" letters such as ligature breaks + // and hyphenation chars while searching while (pos < parsize - 1 && isInset(pos)) { - const InsetSpecialChar *isc = dynamic_cast(getInset(pos)); - if (isc == 0 - || (isc->kind() != InsetSpecialChar::HYPHENATION - && isc->kind() != InsetSpecialChar::LIGATURE_BREAK)) + odocstringstream os; + getInset(pos)->toString(os); + if (!getInset(pos)->isLetter() || !os.str().empty()) break; pos++; } @@ -3451,16 +3559,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()) @@ -3576,7 +3681,7 @@ Language * Paragraph::Private::getSpellLanguage(pos_type const from) const void Paragraph::requestSpellCheck(pos_type pos) { - d->requestSpellCheck(pos == -1 ? size() : pos); + d->requestSpellCheck(pos); } @@ -3632,6 +3737,7 @@ SpellChecker::Result Paragraph::spellCheck(pos_type & from, pos_type & to, return result; if (needsSpellCheck() || check_learned) { + pos_type end = to; if (!d->ignoreWord(word)) { bool const trailing_dot = to < size() && d->text_[to] == '.'; result = speller->check(wl); @@ -3643,28 +3749,33 @@ SpellChecker::Result Paragraph::spellCheck(pos_type & from, pos_type & to, word << "\" [" << from << ".." << to << "]"); } else { - // spell check with dot appended failed + // spell check with dot appended failed too // restore original word/lang value word = asString(from, to, AS_STR_INSETS | AS_STR_SKIPDELETE); wl = WordLangTuple(word, lang); } } } - d->setMisspelled(from, to, result); + if (!SpellChecker::misspelled(result)) { + // area up to the begin of the next word is not misspelled + while (end < size() && isWordSeparator(end)) + ++end; + } + d->setMisspelled(from, end, result); } else { result = d->speller_state_.getState(from); } - bool const misspelled_ = SpellChecker::misspelled(result) ; - if (misspelled_ && do_suggestion) - speller->suggest(wl, suggestions); - else if (misspelled_) + if (do_suggestion) + suggestions.clear(); + + if (SpellChecker::misspelled(result)) { LYXERR(Debug::GUI, "misspelled word: \"" << word << "\" [" << from << ".." << to << "]"); - else - suggestions.clear(); - + if (do_suggestion) + speller->suggest(wl, suggestions); + } return result; } @@ -3756,9 +3867,9 @@ void Paragraph::spellCheck() const bool Paragraph::isMisspelled(pos_type pos, bool check_boundary) const { bool result = SpellChecker::misspelled(d->speller_state_.getState(pos)); - if (result || pos <= 0 || pos >= size()) + if (result || pos <= 0 || pos > size()) return result; - if (check_boundary && isWordSeparator(pos)) + if (check_boundary && (pos == size() || isWordSeparator(pos))) result = SpellChecker::misspelled(d->speller_state_.getState(pos - 1)); return result; }