X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2FParagraph.cpp;h=4ce94415f796f23e1f83a60a378e1257d1d77145;hb=874f559bc78f1572a3daeeea375f7b4be376bbdd;hp=b7616ad68c1953710aaccbf4bb87a4a29ca43e41;hpb=7114de5175e0d34275b39bb998dd65186d1d219f;p=features.git diff --git a/src/Paragraph.cpp b/src/Paragraph.cpp index b7616ad68c..4ce94415f7 100644 --- a/src/Paragraph.cpp +++ b/src/Paragraph.cpp @@ -37,6 +37,7 @@ #include "output_xhtml.h" #include "output_docbook.h" #include "ParagraphParameters.h" +#include "Session.h" #include "SpellChecker.h" #include "texstream.h" #include "TexRow.h" @@ -47,6 +48,7 @@ #include "frontends/alert.h" +#include "insets/InsetArgument.h" #include "insets/InsetBibitem.h" #include "insets/InsetLabel.h" #include "insets/InsetSpecialChar.h" @@ -69,8 +71,8 @@ using namespace std; using namespace lyx::support; -// OSX clang, gcc < 4.8.0, and msvc < 2015 do not support C++11 thread_local -#if defined(__APPLE__) || (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 8) +// OSX clang and msvc < 2015 do not support C++11 thread_local +#if defined(__APPLE__) #define THREAD_LOCAL_STATIC static __thread #elif defined(_MSC_VER) && (_MSC_VER < 1900) #define THREAD_LOCAL_STATIC static __declspec(thread) @@ -306,7 +308,7 @@ public: /// \return the number of characters written. int latexSurrogatePair(BufferParams const &, otexstream & os, char_type c, char_type next, - OutputParams const &); + OutputParams const &) const; /// Output a space in appropriate formatting (or a surrogate pair /// if the next character is a combining character). @@ -317,7 +319,7 @@ public: pos_type i, unsigned int & column, Font const & font, - Layout const & style); + Layout const & style) const; /// This could go to ParagraphParameters if we want to. int startTeXParParams(BufferParams const &, otexstream &, @@ -341,7 +343,7 @@ public: unsigned int & column, bool const fontswitch_inset, bool const closeLanguage, - bool const lang_switched_at_inset); + bool const lang_switched_at_inset) const; /// void latexSpecialChar( @@ -353,7 +355,7 @@ public: Layout const & style, pos_type & i, pos_type end_pos, - unsigned int & column); + unsigned int & column) const; /// bool latexSpecialT1( @@ -400,7 +402,7 @@ public: return speller_change_number > speller_state_.currentChangeNumber(); } - bool ignoreWord(docstring const & word) const ; + bool ignoreWord(docstring const & word) const; void setMisspelled(pos_type from, pos_type to, SpellChecker::Result state) { @@ -459,10 +461,11 @@ public: return numskips; } - void markMisspelledWords(pos_type const & first, pos_type const & last, - SpellChecker::Result result, - docstring const & word, - SkipPositions const & skips); + void markMisspelledWords(Language const * lang, + pos_type const & first, pos_type const & last, + SpellChecker::Result result, + docstring const & word, + SkipPositions const & skips); InsetCode ownerCode() const { @@ -830,6 +833,11 @@ void Paragraph::Private::insertChar(pos_type pos, char_type c, // Update list of misspelled positions speller_state_.increasePosAfterPos(pos); + + // Update bookmarks + if (inset_owner_ && inset_owner_->isBufferValid()) + theSession().bookmarks().adjustPosAfterPos(inset_owner_->buffer().fileName(), + id_, pos, 1); } @@ -914,6 +922,11 @@ bool Paragraph::eraseChar(pos_type pos, bool trackChanges) d->speller_state_.decreasePosAfterPos(pos); d->speller_state_.refreshLast(size()); + // Update bookmarks + if (d->inset_owner_ && d->inset_owner_->isBufferValid()) + theSession().bookmarks().adjustPosAfterPos(d->inset_owner_->buffer().fileName(), + d->id_, pos, -1); + return true; } @@ -935,7 +948,7 @@ int Paragraph::eraseChars(pos_type start, pos_type end, bool trackChanges) // Handle combining characters int Paragraph::Private::latexSurrogatePair(BufferParams const & bparams, otexstream & os, char_type c, char_type next, - OutputParams const & runparams) + OutputParams const & runparams) const { // Writing next here may circumvent a possible font change between // c and next. Since next is only output if it forms a surrogate pair @@ -972,7 +985,7 @@ bool Paragraph::Private::simpleTeXBlanks(BufferParams const & bparams, pos_type i, unsigned int & column, Font const & font, - Layout const & style) + Layout const & style) const { if (style.pass_thru || runparams.pass_thru) return false; @@ -1025,7 +1038,7 @@ void Paragraph::Private::latexInset(BufferParams const & bparams, unsigned int & column, bool const fontswitch_inset, bool const closeLanguage, - bool const lang_switched_at_inset) + bool const lang_switched_at_inset) const { Inset * inset = owner_->getInset(i); LBUFERR(inset); @@ -1135,6 +1148,7 @@ void Paragraph::Private::latexInset(BufferParams const & bparams, bool const cprotect = textinset ? textinset->hasCProtectContent(runparams.moving_arg) && !textinset->text().isMainText() + && inset->lyxCode() != BRANCH_CODE : false; unsigned int count2 = basefont.latexWriteStartChanges(os, bparams, rp, running_font, @@ -1191,30 +1205,45 @@ void Paragraph::Private::latexSpecialChar(otexstream & os, Layout const & style, pos_type & i, pos_type end_pos, - unsigned int & column) + unsigned int & column) const { char_type const c = owner_->getUChar(bparams, runparams, i); - if (style.pass_thru || runparams.pass_thru || (runparams.for_searchAdv != OutputParams::NoSearch) + if (style.pass_thru || runparams.pass_thru || runparams.find_effective() || contains(style.pass_thru_chars, c) || contains(runparams.pass_thru_chars, c)) { - if (runparams.for_searchAdv != OutputParams::NoSearch) { - if (c == '\\') + if (runparams.find_effective()) { + switch (c) { + case '\\': os << "\\\\"; - else if (c == '{') + return; + case '{': os << "\\braceleft "; - else if (c == '}') + return; + case '}': os << "\\braceright "; - else if (c != '\0') + return; + case '$': + os << "\\lyxdollar "; + return; + case '~': + os << "\\lyxtilde "; + return; + case ' ': + case '\0': + break; + default: os.put(c); + return; + } } else if (c != '\0') { Encoding const * const enc = runparams.encoding; if (enc && !enc->encodable(c)) throw EncodingException(c); os.put(c); + return; } - return; } // TIPA uses its own T3 encoding @@ -1226,7 +1255,7 @@ void Paragraph::Private::latexSpecialChar(otexstream & os, // non-standard font encoding. If we are using such a language, // we do not output special T1 chars. if (!runparams.inIPA && !running_font.language()->internalFontEncoding() - && !runparams.isFullUnicode() && bparams.main_font_encoding() == "T1" + && !runparams.isFullUnicode() && runparams.main_fontenc == "T1" && latexSpecialT1(c, os, i, column)) return; // NOTE: "fontspec" (non-TeX fonts) sets the font encoding to "TU" (untill 2017 "EU1" or "EU2") @@ -1518,12 +1547,12 @@ void Paragraph::Private::validate(LaTeXFeatures & features) const if (c == 0x0022) { if (features.runparams().isFullUnicode() && bp.useNonTeXFonts) features.require("textquotedblp"); - else if (bp.main_font_encoding() != "T1" + else if (features.runparams().main_fontenc != "T1" || ((&owner_->getFontSettings(bp, i))->language()->internalFontEncoding())) features.require("textquotedbl"); - } else if (ci.textfeature() && contains(ci.textpreamble(), '=')) { + } else if (ci.textFeature() && contains(ci.textPreamble(), '=')) { // features that depend on the font or input encoding - string feats = ci.textpreamble(); + string feats = ci.textPreamble(); string fontenc = (&owner_->getFontSettings(bp, i))->language()->fontenc(bp); if (fontenc.empty()) fontenc = features.runparams().main_fontenc; @@ -1898,7 +1927,8 @@ FontSpan Paragraph::fontSpan(pos_type pos) const // This should not happen, but if so, we take no chances. LYXERR0("Paragraph::fontSpan: position not found in fontinfo table!"); - LASSERT(false, return FontSpan(pos, pos)); + LASSERT(false, /**/); + return FontSpan(pos, pos); } @@ -2254,6 +2284,22 @@ bool Paragraph::isPassThru() const return inInset().isPassThru() || d->layout_->pass_thru; } + +bool Paragraph::parbreakIsNewline() const +{ + return inInset().getLayout().parbreakIsNewline() || d->layout_->parbreak_is_newline; +} + + +bool Paragraph::isPartOfTextSequence() const +{ + for (pos_type i = 0; i < size(); ++i) { + if (!isInset(i) || getInset(i)->isPartOfTextSequence()) + return true; + } + return false; +} + namespace { // paragraphs inside floats need different alignment tags to avoid @@ -2325,13 +2371,31 @@ int Paragraph::Private::startTeXParParams(BufferParams const & bparams, (layout_->toggle_indent != ITOGGLE_NEVER) : (layout_->toggle_indent == ITOGGLE_ALWAYS); - if (canindent && params_.noindent() && !layout_->pass_thru) { - os << "\\noindent "; - column += 10; - } - LyXAlignment const curAlign = params_.align(); + // Do not output \\noindent for paragraphs + // 1. that cannot have indentation or are indented always, + // 2. that are not part of the immediate text sequence (e.g., contain only floats), + // 3. that are PassThru, + // 4. or that are centered. + if (canindent && params_.noindent() + && owner_->isPartOfTextSequence() + && !layout_->pass_thru + && curAlign != LYX_ALIGN_CENTER) { + if (!owner_->empty() + && owner_->getInset(0) + && owner_->getInset(0)->lyxCode() == VSPACE_CODE) + // If the paragraph starts with a vspace, the \\noindent + // needs to come after that (as it leaves vmode). + // If the paragraph consists only of the vspace, + // \\noindent is not needed at all. + runparams.need_noindent = owner_->size() > 1; + else { + os << "\\noindent" << termcmd; + column += 10; + } + } + if (curAlign == layout_->align) return column; @@ -2462,7 +2526,7 @@ void Paragraph::latex(BufferParams const & bparams, OutputParams const & runparams, int start_pos, int end_pos, bool force) const { - LYXERR(Debug::LATEX, "Paragraph::latex... " << this); + LYXERR(Debug::OUTFILE, "Paragraph::latex... " << this); // FIXME This check should not be needed. Perhaps issue an // error if it triggers. @@ -2491,10 +2555,10 @@ void Paragraph::latex(BufferParams const & bparams, pos_type body_pos = beginOfBody(); unsigned int column = 0; - // If we are inside an non inheritFont() inset, the real outerfont is local_font - Font const real_outerfont = (!inInset().inheritFont() - && runparams.local_font != nullptr) - ? Font(runparams.local_font->fontInfo()) : outerfont; + // If we are inside an non inheritFont() inset, + // the outerfont is the buffer's main font + Font const real_outerfont = + inInset().inheritFont() ? outerfont : Font(bparams.getFont()); if (body_pos > 0) { // the optional argument is kept in curly brackets in @@ -2562,7 +2626,7 @@ void Paragraph::latex(BufferParams const & bparams, runparams); runningChange = Change(Change::UNCHANGED); - os << (isEnvSeparator(i) ? "}]~" : "}] "); + os << ((isEnvSeparator(i) && !runparams.find_effective()) ? "}]~" : "}] "); column +=3; } // For InTitle commands, we have already opened a group @@ -2592,10 +2656,10 @@ void Paragraph::latex(BufferParams const & bparams, // Check whether a display math inset follows bool output_changes; - if (runparams.for_searchAdv == OutputParams::NoSearch) + if (!runparams.find_effective()) output_changes = bparams.output_changes; else - output_changes = (runparams.for_searchAdv == OutputParams::SearchWithDeleted); + output_changes = runparams.find_with_deleted(); if (c == META_INSET && i >= start_pos && (end_pos == -1 || i < end_pos)) { if (isDeleted(i)) @@ -2681,7 +2745,12 @@ void Paragraph::latex(BufferParams const & bparams, && getInset(i) && getInset(i)->allowMultiPar() && getInset(i)->lyxCode() != ERT_CODE - && getInset(i)->producesOutput(); + && (getInset(i)->producesOutput() + // FIXME Something more general? + // Comments do not "produce output" but are still + // part of the TeX source and require font switches + // to be closed (otherwise LaTeX fails). + || getInset(i)->layoutName() == "Note:Comment"); bool closeLanguage = false; bool lang_switched_at_inset = false; @@ -2720,9 +2789,11 @@ void Paragraph::latex(BufferParams const & bparams, os << '}'; column += 1; } - if (closeLanguage) + if (closeLanguage) { // Force language closing current_font.setLanguage(basefont.language()); + langClosed = true; + } Font const nextfont = (i == body_pos-1) ? basefont : current_font; bool needPar = false; column += running_font.latexWriteEndChanges( @@ -2735,12 +2806,12 @@ void Paragraph::latex(BufferParams const & bparams, column += Changes::latexMarkChange(os, bparams, Change(Change::UNCHANGED), Change(Change::DELETED), rp); } - open_font = false; // Has the language been closed in the latexWriteEndChanges() call above? - langClosed = running_font.language() != basefont.language() + langClosed |= running_font.language() != basefont.language() && running_font.language() != nextfont.language() && (running_font.language()->encoding()->package() != Encoding::CJK); running_font = basefont; + open_font &= !langClosed; } // if necessary, close language environment before opening CJK @@ -2753,7 +2824,8 @@ void Paragraph::latex(BufferParams const & bparams, string end_tag = subst(lang_end_command, "$$lang", running_lang); os << from_ascii(end_tag); column += end_tag.length(); - popLanguageName(); + if (!languageStackEmpty()) + popLanguageName(); } // Switch file encoding if necessary (and allowed) @@ -2805,6 +2877,7 @@ void Paragraph::latex(BufferParams const & bparams, bool const cprotect = textinset ? textinset->hasCProtectContent(runparams.moving_arg) && !textinset->text().isMainText() + && inInset().lyxCode() != BRANCH_CODE : false; column += current_font.latexWriteStartChanges(ots, bparams, runparams, basefont, last_font, false, @@ -2838,9 +2911,14 @@ void Paragraph::latex(BufferParams const & bparams, column += Changes::latexMarkChange(os, bparams, Change(Change::UNCHANGED), change, rp); } - } else { + } else {// if fontswitch_inset + if (current_font != running_font || !langClosed) + // font is still open in fontswitch_insets if we have + // a non-lang font difference or if the language + // is the only difference but has not been forcedly + // closed meanwhile + open_font = true; running_font = current_font; - open_font = !langClosed; } } @@ -2909,7 +2987,7 @@ void Paragraph::latex(BufferParams const & bparams, d->latexInset(bparams, os, rp, running_font, basefont, real_outerfont, open_font, runningChange, style, i, column, fontswitch_inset, - closeLanguage, lang_switched_at_inset); + closeLanguage, (lang_switched_at_inset || langClosed)); if (fontswitch_inset) { if (open_font) { bool needPar = false; @@ -3072,7 +3150,7 @@ void Paragraph::latex(BufferParams const & bparams, os << setEncoding(prev_encoding->iconvName()); } - LYXERR(Debug::LATEX, "Paragraph::latex... done " << this); + LYXERR(Debug::OUTFILE, "Paragraph::latex... done " << this); } @@ -3419,35 +3497,55 @@ std::tuple, vector> computeDocBookFontSwit } // anonymous namespace -std::vector Paragraph::simpleDocBookOnePar(Buffer const & buf, +std::tuple, std::vector, std::vector> + Paragraph::simpleDocBookOnePar(Buffer const & buf, OutputParams const & runparams, Font const & outerfont, pos_type initial, bool is_last_par, bool ignore_fonts) const { - // Track whether we have opened these tags - DocBookFontState fs; + std::vector prependedParagraphs; + std::vector generatedParagraphs; + std::vector appendedParagraphs; + odocstringstream os; - Layout const & style = *d->layout_; - FontInfo font_old = - style.labeltype == LABEL_MANUAL ? style.labelfont : style.font; + // If there is an argument that must be output before the main tag, do it before handling the rest of the paragraph. + // Also tag all arguments that shouldn't go in the main content right now, so that they are never generated at the + // wrong place. + OutputParams rp = runparams; + for (pos_type i = initial; i < size(); ++i) { + if (getInset(i) && getInset(i)->lyxCode() == ARG_CODE) { + const InsetArgument * arg = getInset(i)->asInsetArgument(); + if (arg->docbookargumentbeforemaintag()) { + auto xs_local = XMLStream(os); + arg->docbook(xs_local, rp); + + prependedParagraphs.push_back(os.str()); + os.str(from_ascii("")); + + rp.docbook_prepended_arguments.insert(arg); + } else if (arg->docbookargumentaftermaintag()) { + rp.docbook_appended_arguments.insert(arg); + } + } + } - string const default_family = - buf.masterBuffer()->params().fonts_default_family; + // State variables for the main loop. + auto xs = new XMLStream(os); // XMLStream has no copy constructor: to create a new object, the only solution + // is to hold a pointer to the XMLStream (xs = XMLStream(os) is not allowed once the first object is built). + std::vector delayedChars; // When a font tag ends with a space, output it after the closing font tag. + // This requires to store delayed characters at some point. - vector tagsToOpen; - vector tagsToClose; + DocBookFontState fs; // Track whether we have opened font tags + DocBookFontState old_fs = fs; - std::vector generatedParagraphs; - DocBookFontState old_fs = fs; - odocstringstream os; - auto * xs = new XMLStream(os); // XMLStream has no copy constructor: to create a new object, the only solution - // is to hold a pointer to the XMLStream (xs = XMLStream(os) is not allowed once the first object is built). + Layout const & style = *d->layout_; + FontInfo font_old = style.labeltype == LABEL_MANUAL ? style.labelfont : style.font; + string const default_family = buf.masterBuffer()->params().fonts_default_family; - // When a font tag ends with a space, output it after the closing font tag. This requires to store delayed - // characters at some point. - std::vector delayedChars; + vector tagsToOpen; + vector tagsToClose; // Parsing main loop. for (pos_type i = initial; i < size(); ++i) { @@ -3511,8 +3609,13 @@ std::vector Paragraph::simpleDocBookOnePar(Buffer const & buf, // Finally, write the next character or inset. if (Inset const * inset = getInset(i)) { - if (!runparams.for_toc || inset->isInToc()) { - OutputParams np = runparams; + bool inset_is_argument_elsewhere = getInset(i)->asInsetArgument() && + rp.docbook_appended_arguments.find(inset->asInsetArgument()) != rp.docbook_appended_arguments.end() && + rp.docbook_prepended_arguments.find(inset->asInsetArgument()) != rp.docbook_prepended_arguments.end(); + + if ((!rp.for_toc || inset->isInToc()) && !inset_is_argument_elsewhere) { + // Arguments may need to be output + OutputParams np = rp; np.local_font = &font; // TODO: special case will bite here. @@ -3520,7 +3623,7 @@ std::vector Paragraph::simpleDocBookOnePar(Buffer const & buf, inset->docbook(*xs, np); } } else { - char_type c = getUChar(buf.masterBuffer()->params(), runparams, i); + char_type c = getUChar(buf.masterBuffer()->params(), rp, i); if (lyx::isSpace(c) && !ignore_fonts) delayedChars.push_back(c); else @@ -3542,14 +3645,30 @@ std::vector Paragraph::simpleDocBookOnePar(Buffer const & buf, // In listings, new lines (i.e. \n characters in the output) are very important. Avoid generating one for the // last line to get a clean output. - if (runparams.docbook_in_listing && !is_last_par) + if (rp.docbook_in_listing && !is_last_par) *xs << xml::CR(); // Finalise the last (and most likely only) paragraph. generatedParagraphs.push_back(os.str()); - delete xs; + os.str(from_ascii("")); + delete xs; + + // If there is an argument that must be output after the main tag, do it after handling the rest of the paragraph. + for (pos_type i = initial; i < size(); ++i) { + if (getInset(i) && getInset(i)->lyxCode() == ARG_CODE) { + const InsetArgument * arg = getInset(i)->asInsetArgument(); + if (arg->docbookargumentaftermaintag()) { + // Don't use rp, as this argument would not generate anything. + auto xs_local = XMLStream(os); + arg->docbook(xs_local, runparams); + + appendedParagraphs.push_back(os.str()); + os.str(from_ascii("")); + } + } + } - return generatedParagraphs; + return std::make_tuple(prependedParagraphs, generatedParagraphs, appendedParagraphs); } @@ -3901,6 +4020,8 @@ docstring Paragraph::simpleLyXHTMLOnePar(Buffer const & buf, runparams, i); if (c == ' ' && (style.free_spacing || runparams.free_spacing)) xs << XMLStream::ESCAPE_NONE << " "; + else if (c == '\'') + xs << XMLStream::ESCAPE_NONE << "’"; else xs << c; } @@ -4019,27 +4140,12 @@ bool Paragraph::needsCProtection(bool const fragile) const } // now check whether we have insets that need cprotection - pos_type size = pos_type(d->text_.size()); - for (pos_type i = 0; i < size; ++i) { - if (!isInset(i)) + for (auto const & icit : d->insetlist_) { + Inset const * ins = icit.inset; + if (!ins) continue; - Inset const * ins = getInset(i); if (ins->needsCProtection(maintext, fragile)) return true; - // Now check math environments - InsetMath const * im = getInset(i)->asInsetMath(); - if (!im || im->cell(0).empty()) - continue; - switch(im->cell(0)[0]->lyxCode()) { - case MATH_AMSARRAY_CODE: - case MATH_SUBSTACK_CODE: - case MATH_ENV_CODE: - case MATH_XYMATRIX_CODE: - // these need cprotection - return true; - default: - break; - } } return false; @@ -4479,7 +4585,6 @@ void Paragraph::changeCase(BufferParams const & bparams, pos_type pos, } } - int Paragraph::find(docstring const & str, bool cs, bool mw, pos_type start_pos, bool del) const { @@ -4504,7 +4609,14 @@ int Paragraph::find(docstring const & str, bool cs, bool mw, if (!inset->isLetter() && !inset->isChar()) break; odocstringstream os; - inset->toString(os); + if (inset->lyxCode() == lyx::QUOTE_CODE) { + OutputParams op(0); + op.find_set_feature(OutputParams::SearchQuick); + inset->plaintext(os, op); + } + else { + inset->toString(os); + } docstring const insetstring = os.str(); if (!insetstring.empty()) { int const insetstringsize = insetstring.length(); @@ -4520,9 +4632,10 @@ int Paragraph::find(docstring const & str, bool cs, bool mw, } if (nonmatch || i == strsize) break; - if (cs && str[i] != d->text_[pos]) + char_type dp = d->text_[pos]; + if (cs && str[i] != dp) break; - if (!cs && uppercase(str[i]) != uppercase(d->text_[pos])) + if (!cs && uppercase(str[i]) != uppercase(dp)) break; } @@ -4769,6 +4882,19 @@ Language * Paragraph::Private::getSpellLanguage(pos_type const from) const void Paragraph::requestSpellCheck(pos_type pos) { d->requestSpellCheck(pos); + if (pos == -1) { + // Also request spellcheck within (text) insets + for (auto const & insets : insetList()) { + if (!insets.inset->asInsetText()) + continue; + ParagraphList & inset_pars = + insets.inset->asInsetText()->paragraphs(); + ParagraphList::iterator pit = inset_pars.begin(); + ParagraphList::iterator pend = inset_pars.end(); + for (; pit != pend; ++pit) + pit->requestSpellCheck(); + } + } } @@ -4818,7 +4944,9 @@ SpellChecker::Result Paragraph::spellCheck(pos_type & from, pos_type & to, docstring word = asString(from, to, AS_STR_INSETS | AS_STR_SKIPDELETE); Language * lang = d->getSpellLanguage(from); - if (getFontSettings(d->inset_owner_->buffer().params(), from).fontInfo().nospellcheck() == FONT_ON) + BufferParams const & bparams = d->inset_owner_->buffer().params(); + + if (getFontSettings(bparams, from).fontInfo().nospellcheck() == FONT_ON) return result; wl = WordLangTuple(word, lang); @@ -4830,10 +4958,10 @@ SpellChecker::Result Paragraph::spellCheck(pos_type & from, pos_type & to, pos_type end = to; if (!d->ignoreWord(word)) { bool const trailing_dot = to < size() && d->text_[to] == '.'; - result = speller->check(wl); + result = speller->check(wl, bparams.spellignore()); if (SpellChecker::misspelled(result) && trailing_dot) { wl = WordLangTuple(word.append(from_ascii(".")), lang); - result = speller->check(wl); + result = speller->check(wl, bparams.spellignore()); if (!SpellChecker::misspelled(result)) { LYXERR(Debug::GUI, "misspelled word is correct with dot: \"" << word << "\" [" << @@ -4880,6 +5008,7 @@ void Paragraph::anonymize() void Paragraph::Private::markMisspelledWords( + Language const * lang, pos_type const & first, pos_type const & last, SpellChecker::Result result, docstring const & word, @@ -4901,8 +5030,9 @@ void Paragraph::Private::markMisspelledWords( int wlen = 0; speller->misspelledWord(index, wstart, wlen); /// should not happen if speller supports range checks - if (!wlen) continue; - docstring const misspelled = word.substr(wstart, wlen); + if (!wlen) + continue; + WordLangTuple const candidate(word.substr(wstart, wlen), lang); wstart += first + numskipped; if (snext < wstart) { /// mark the range of correct spelling @@ -4911,12 +5041,21 @@ void Paragraph::Private::markMisspelledWords( wstart - 1, SpellChecker::WORD_OK); } snext = wstart + wlen; + // Check whether the candidate is in the document's local dict + SpellChecker::Result actresult = result; + if (inset_owner_->buffer().params().spellignored(candidate)) + actresult = SpellChecker::DOCUMENT_LEARNED_WORD; numskipped += countSkips(it, et, snext); /// mark the range of misspelling - setMisspelled(wstart, snext, result); - LYXERR(Debug::GUI, "misspelled word: \"" << - misspelled << "\" [" << - wstart << ".." << (snext-1) << "]"); + setMisspelled(wstart, snext, actresult); + if (actresult == SpellChecker::DOCUMENT_LEARNED_WORD) + LYXERR(Debug::GUI, "local dictionary word: \"" << + candidate.word() << "\" [" << + wstart << ".." << (snext-1) << "]"); + else + LYXERR(Debug::GUI, "misspelled word: \"" << + candidate.word() << "\" [" << + wstart << ".." << (snext-1) << "]"); ++snext; } if (snext <= last) { @@ -4945,9 +5084,10 @@ void Paragraph::spellCheck() const // start the spell checker on the unit of meaning docstring word = asString(first, last, AS_STR_INSETS + AS_STR_SKIPDELETE); WordLangTuple wl = WordLangTuple(word, lang); + BufferParams const & bparams = d->inset_owner_->buffer().params(); SpellChecker::Result result = !word.empty() ? - speller->check(wl) : SpellChecker::WORD_OK; - d->markMisspelledWords(first, last, result, word, skips); + speller->check(wl, bparams.spellignore()) : SpellChecker::WORD_OK; + d->markMisspelledWords(lang, first, last, result, word, skips); first = ++last; } } else {