X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2FParagraph.cpp;h=8ac87d31c2d2060cfa08f56d30d417a110c02023;hb=7a000652c02f98dcff4d3c611b22af244409df73;hp=e7d835fc634c2257bfeb5ba8386c9468c431a9c7;hpb=f3a0e8ff9a9b914d40eb520bb31674d1ad4eb0d0;p=lyx.git diff --git a/src/Paragraph.cpp b/src/Paragraph.cpp index e7d835fc63..8ac87d31c2 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" @@ -56,6 +58,7 @@ #include "support/debug.h" #include "support/docstring_list.h" +#include "support/ExceptionMessage.h" #include "support/gettext.h" #include "support/lassert.h" #include "support/lstrings.h" @@ -305,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). @@ -316,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 &, @@ -340,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( @@ -352,7 +355,7 @@ public: Layout const & style, pos_type & i, pos_type end_pos, - unsigned int & column); + unsigned int & column) const; /// bool latexSpecialT1( @@ -399,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) { @@ -458,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 { @@ -591,10 +595,12 @@ struct ChangesMonitor { * 2. and we are not in the situation where the buffer has changes * and new changes are added to the paragraph. */ - if (par_.isChanged() != was_changed_ - && par_.inInset().isBufferValid() - && !(par_.inInset().buffer().areChangesPresent() && par_.isChanged())) - par_.inInset().buffer().forceUpdate(); + try { + if (par_.isChanged() != was_changed_ + && par_.inInset().isBufferValid() + && !(par_.inInset().buffer().areChangesPresent() && par_.isChanged())) + par_.inInset().buffer().forceUpdate(); + } catch(support::ExceptionMessage const &) {} } private: @@ -827,6 +833,10 @@ void Paragraph::Private::insertChar(pos_type pos, char_type c, // Update list of misspelled positions speller_state_.increasePosAfterPos(pos); + + // Update bookmarks + theSession().bookmarks().adjustPosAfterPos(inset_owner_->buffer().fileName(), + id_, pos, 1); } @@ -911,6 +921,10 @@ bool Paragraph::eraseChar(pos_type pos, bool trackChanges) d->speller_state_.decreasePosAfterPos(pos); d->speller_state_.refreshLast(size()); + // Update bookmarks + theSession().bookmarks().adjustPosAfterPos(d->inset_owner_->buffer().fileName(), + d->id_, pos, -1); + return true; } @@ -932,7 +946,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 @@ -969,7 +983,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; @@ -1022,7 +1036,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); @@ -1188,7 +1202,7 @@ 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); @@ -2512,7 +2526,9 @@ void Paragraph::latex(BufferParams const & bparams, // Do we have an open font change? bool open_font = false; - Change runningChange = Change(Change::UNCHANGED); + Change runningChange = + runparams.inDeletedInset && !inInset().canTrackChanges() + ? runparams.changeOfDeletedInset : Change(Change::UNCHANGED); Encoding const * const prev_encoding = runparams.encoding; @@ -2557,7 +2573,7 @@ void Paragraph::latex(BufferParams const & bparams, runparams); runningChange = Change(Change::UNCHANGED); - os << "}] "; + os << (isEnvSeparator(i) ? "}]~" : "}] "); column +=3; } // For InTitle commands, we have already opened a group @@ -3047,8 +3063,9 @@ void Paragraph::latex(BufferParams const & bparams, os << "{\\" << font.latexSize() << "\\par}"; } - column += Changes::latexMarkChange(os, bparams, runningChange, - Change(Change::UNCHANGED), runparams); + if (!runparams.inDeletedInset || inInset().canTrackChanges()) + column += Changes::latexMarkChange(os, bparams, runningChange, + Change(Change::UNCHANGED), runparams); // Needed if there is an optional argument but no contents. if (body_pos > 0 && body_pos == size()) { @@ -3413,38 +3430,62 @@ 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; - - 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; - - vector tagsToOpen; - vector tagsToClose; - + std::vector prependedParagraphs; std::vector generatedParagraphs; - DocBookFontState old_fs = fs; + std::vector appendedParagraphs; 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). - // 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; + // 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); + } + } + } + + // 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. + + DocBookFontState fs; // Track whether we have opened font tags + DocBookFontState old_fs = fs; + + 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; + + vector tagsToOpen; + vector tagsToClose; // Parsing main loop. for (pos_type i = initial; i < size(); ++i) { + bool ignore_fonts_i = ignore_fonts + || style.docbooknofontinside() + || (getInset(i) && getInset(i)->getLayout().docbooknofontinside()); + // Don't show deleted material in the output. if (isDeleted(i)) continue; @@ -3452,7 +3493,7 @@ std::vector Paragraph::simpleDocBookOnePar(Buffer const & buf, // If this is an InsetNewline, generate a new paragraph. Also reset the fonts, so that tags are closed in // this paragraph. if (getInset(i) && getInset(i)->lyxCode() == NEWLINE_CODE) { - if (!ignore_fonts) + if (!ignore_fonts_i) xs->closeFontTags(); // Output one paragraph (i.e. one string entry in generatedParagraphs). @@ -3465,7 +3506,7 @@ std::vector Paragraph::simpleDocBookOnePar(Buffer const & buf, xs = new XMLStream(os); // Restore the fonts for the new paragraph, so that the right tags are opened for the new entry. - if (!ignore_fonts) { + if (!ignore_fonts_i) { font_old = outerfont.fontInfo(); fs = old_fs; } @@ -3473,24 +3514,23 @@ std::vector Paragraph::simpleDocBookOnePar(Buffer const & buf, // Determine which tags should be opened or closed regarding fonts. Font const font = getFont(buf.masterBuffer()->params(), i, outerfont); - if (!ignore_fonts) { - tie(tagsToOpen, tagsToClose) = computeDocBookFontSwitch(font_old, font, default_family, fs); - - // FIXME XHTML - // Other such tags? What about the other text ranges? - - vector::const_iterator cit = tagsToClose.begin(); - vector::const_iterator cen = tagsToClose.end(); - for (; cit != cen; ++cit) - *xs << *cit; - - // Deal with the delayed characters *after* closing font tags. - if (!delayedChars.empty()) { - for (char_type c: delayedChars) - *xs << c; - delayedChars.clear(); - } - + tie(tagsToOpen, tagsToClose) = computeDocBookFontSwitch(font_old, font, default_family, fs); + + if (!ignore_fonts_i) { + vector::const_iterator cit = tagsToClose.begin(); + vector::const_iterator cen = tagsToClose.end(); + for (; cit != cen; ++cit) + *xs << *cit; + } + + // Deal with the delayed characters *after* closing font tags. + if (!delayedChars.empty()) { + for (char_type c: delayedChars) + *xs << c; + delayedChars.clear(); + } + + if (!ignore_fonts_i) { vector::const_iterator sit = tagsToOpen.begin(); vector::const_iterator sen = tagsToOpen.end(); for (; sit != sen; ++sit) @@ -3500,9 +3540,15 @@ std::vector Paragraph::simpleDocBookOnePar(Buffer const & buf, tagsToOpen.clear(); } + // 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. @@ -3510,7 +3556,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 @@ -3532,14 +3578,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); - return generatedParagraphs; + appendedParagraphs.push_back(os.str()); + os.str(from_ascii("")); + } + } + } + + return std::make_tuple(prependedParagraphs, generatedParagraphs, appendedParagraphs); } @@ -4141,6 +4203,14 @@ docstring Paragraph::asString(pos_type beg, pos_type end, int options, const Out if (c == META_INSET && (options & AS_STR_PLAINTEXT)) { LASSERT(runparams != nullptr, return docstring()); getInset(i)->plaintext(os, *runparams); + } else if (c == META_INSET && (options & AS_STR_MATHED) + && getInset(i)->lyxCode() == REF_CODE) { + Buffer const & buf = getInset(i)->buffer(); + OutputParams rp(&buf.params().encoding()); + Font const font(inherit_font, buf.params().language); + rp.local_font = &font; + otexstream ots(os); + getInset(i)->latex(ots, rp); } else { getInset(i)->toString(os); } @@ -4487,10 +4557,12 @@ int Paragraph::find(docstring const & str, bool cs, bool mw, break; odocstringstream os; inset->toString(os); - if (!os.str().empty()) { - int const insetstringsize = os.str().length(); + docstring const insetstring = os.str(); + if (!insetstring.empty()) { + int const insetstringsize = insetstring.length(); for (int j = 0; j < insetstringsize && pos < parsize; ++i, ++j) { - if (str[i] != os.str()[j]) { + if ((cs && str[i] != insetstring[j]) + || (!cs && uppercase(str[i]) != uppercase(insetstring[j]))) { nonmatch = true; break; } @@ -4798,7 +4870,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); @@ -4810,10 +4884,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 << "\" [" << @@ -4860,6 +4934,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, @@ -4881,8 +4956,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 @@ -4891,12 +4967,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) { @@ -4925,9 +5010,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 {