X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2FParagraph.cpp;h=df8de4c39f09ced1bece8559b5f0405d4eb102b3;hb=55a3dd7b346d29a52ba305a4558e9e380ef50f47;hp=08b143b3646fe78e8c62f49726b6b6c87c7c1d20;hpb=5eed97b56b9a90a493e57061b8cf75d49f8d5d33;p=lyx.git diff --git a/src/Paragraph.cpp b/src/Paragraph.cpp index 08b143b364..df8de4c39f 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; } } @@ -295,7 +294,7 @@ public: /// if the next character is a combining character). /// \return whether a surrogate pair was output. bool simpleTeXBlanks(OutputParams const &, - otexstream &, TexRow & texrow, + otexstream &, pos_type i, unsigned int & column, Font const & font, @@ -308,17 +307,17 @@ public: Change const &, Encoding const &, pos_type & i); /// This could go to ParagraphParameters if we want to. - int startTeXParParams(BufferParams const &, otexstream &, TexRow &, + int startTeXParParams(BufferParams const &, otexstream &, OutputParams const &) const; /// This could go to ParagraphParameters if we want to. - int endTeXParParams(BufferParams const &, otexstream &, TexRow &, - OutputParams const &) const; + bool endTeXParParams(BufferParams const &, otexstream &, + OutputParams const &) const; /// void latexInset(BufferParams const &, otexstream &, - TexRow & texrow, OutputParams &, + OutputParams &, Font & running_font, Font & basefont, Font const & outerfont, @@ -336,6 +335,7 @@ public: Change const & running_change, Layout const & style, pos_type & i, + pos_type end_pos, unsigned int & column); /// @@ -354,6 +354,7 @@ public: bool latexSpecialPhrase( otexstream & os, pos_type & i, + pos_type end_pos, unsigned int & column, OutputParams const & runparams); @@ -398,7 +399,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() { @@ -866,7 +870,7 @@ int Paragraph::Private::latexSurrogatePair(otexstream & os, char_type c, bool Paragraph::Private::simpleTeXBlanks(OutputParams const & runparams, - otexstream & os, TexRow & texrow, + otexstream & os, pos_type i, unsigned int & column, Font const & font, @@ -899,8 +903,7 @@ bool Paragraph::Private::simpleTeXBlanks(OutputParams const & runparams, || text_[i - 1] == ':' || text_[i - 1] == '!'))) { os << '\n'; - texrow.newline(); - texrow.start(owner_->id(), i + 1); + os.texrow().start(owner_->id(), i + 1); column = 0; } else if (style.free_spacing) { os << '~'; @@ -1006,7 +1009,6 @@ bool Paragraph::Private::isTextAt(string const & str, pos_type pos) const void Paragraph::Private::latexInset(BufferParams const & bparams, otexstream & os, - TexRow & texrow, OutputParams & runparams, Font & running_font, Font & basefont, @@ -1049,8 +1051,7 @@ void Paragraph::Private::latexInset(BufferParams const & bparams, os << "\\protect "; } - texrow.newline(); - texrow.start(owner_->id(), i + 1); + os.texrow().start(owner_->id(), i + 1); column = 0; } @@ -1092,7 +1093,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,10 +1116,12 @@ void Paragraph::Private::latexInset(BufferParams const & bparams, } } - int tmp; + int prev_rows = os.texrow().rows(); try { - tmp = inset->latex(os, runparams); + runparams.lastid = id_; + runparams.lastpos = i; + inset->latex(os, runparams); } catch (EncodingException & e) { // add location information and throw again. e.par_id = id_; @@ -1133,9 +1136,8 @@ void Paragraph::Private::latexInset(BufferParams const & bparams, os << '}'; } - if (tmp) { - texrow.newlines(tmp); - texrow.start(owner_->id(), i + 1); + if (os.texrow().rows() > prev_rows) { + os.texrow().start(owner_->id(), i + 1); column = 0; } else { column += (unsigned int)(os.os().tellp() - len); @@ -1152,15 +1154,18 @@ void Paragraph::Private::latexSpecialChar(otexstream & os, Change const & running_change, Layout const & style, pos_type & i, + pos_type end_pos, unsigned int & column) { char_type const c = 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->latexChar(c, true).empty()) + throw EncodingException(c); os.put(c); + } return; } @@ -1241,7 +1246,7 @@ 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') @@ -1327,7 +1332,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 @@ -1337,7 +1345,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"; @@ -1357,15 +1366,15 @@ void Paragraph::Private::validate(LaTeXFeatures & features) const Buffer const & buf = inset_owner_->buffer(); BufferParams const & bp = buf.params(); Font f; - TexRow tr; + TexRow texrow; // Using a string stream here circumvents the encoding // switching machinery of odocstream. Therefore the // output is wrong if this paragraph contains content // that needs to switch encoding. odocstringstream ods; - otexstream os(ods); + otexstream os(ods, texrow); if (is_command) { - ods << '\\' << from_ascii(layout_->latexname()); + os << '\\' << from_ascii(layout_->latexname()); // 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. @@ -1377,7 +1386,7 @@ void Paragraph::Private::validate(LaTeXFeatures & features) const } docstring::size_type const length = ods.str().length(); // this will output "{" at the beginning, but not at the end - owner_->latex(bp, f, os, tr, features.runparams(), 0, -1, true); + owner_->latex(bp, f, os, features.runparams(), 0, -1, true); if (ods.str().length() > length) { if (is_command) ods << '}'; @@ -1628,6 +1637,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); } @@ -1648,6 +1658,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); @@ -2119,13 +2130,12 @@ string const corrected_env(string const & suffix, string const & env, } -void adjust_row_column(string const & str, TexRow & texrow, int & column) +void adjust_column(string const & str, int & column) { if (!contains(str, "\n")) column += str.size(); else { string tmp; - texrow.newline(); column = rsplit(str, tmp, '\n').size(); } } @@ -2134,8 +2144,7 @@ void adjust_row_column(string const & str, TexRow & texrow, int & column) int Paragraph::Private::startTeXParParams(BufferParams const & bparams, - otexstream & os, TexRow & texrow, - OutputParams const & runparams) const + otexstream & os, OutputParams const & runparams) const { int column = 0; @@ -2184,7 +2193,7 @@ int Paragraph::Private::startTeXParParams(BufferParams const & bparams, else output = corrected_env(begin_tag, "flushright", code, lastpar); os << from_ascii(output); - adjust_row_column(output, texrow, column); + adjust_column(output, column); break; } case LYX_ALIGN_RIGHT: { string output; @@ -2193,13 +2202,13 @@ int Paragraph::Private::startTeXParParams(BufferParams const & bparams, else output = corrected_env(begin_tag, "flushleft", code, lastpar); os << from_ascii(output); - adjust_row_column(output, texrow, column); + adjust_column(output, column); break; } case LYX_ALIGN_CENTER: { string output; output = corrected_env(begin_tag, "center", code, lastpar); os << from_ascii(output); - adjust_row_column(output, texrow, column); + adjust_column(output, column); break; } } @@ -2208,16 +2217,13 @@ int Paragraph::Private::startTeXParParams(BufferParams const & bparams, } -int Paragraph::Private::endTeXParParams(BufferParams const & bparams, - otexstream & os, TexRow & texrow, - OutputParams const & runparams) const +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: @@ -2229,13 +2235,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; @@ -2248,40 +2253,34 @@ 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_row_column(output, texrow, 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_row_column(output, texrow, column); break; } case LYX_ALIGN_CENTER: { - string output; output = corrected_env(end_tag, "center", code, lastpar); os << from_ascii(output); - adjust_row_column(output, texrow, column); break; } } - return column; + return !output.empty() || lastpar; } // This one spits out the text of the paragraph void Paragraph::latex(BufferParams const & bparams, Font const & outerfont, - otexstream & os, TexRow & texrow, + otexstream & os, OutputParams const & runparams, int start_pos, int end_pos, bool force) const { @@ -2312,6 +2311,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); @@ -2328,7 +2331,7 @@ void Paragraph::latex(BufferParams const & bparams, Encoding const * const prev_encoding = runparams.encoding; - texrow.start(id(), 0); + os.texrow().start(id(), 0); // if the paragraph is empty, the loop will not be entered at all if (empty()) { @@ -2337,8 +2340,7 @@ void Paragraph::latex(BufferParams const & bparams, ++column; } if (allowcust) - column += d->startTeXParParams(bparams, os, texrow, - runparams); + column += d->startTeXParParams(bparams, os, runparams); } for (pos_type i = 0; i < size(); ++i) { @@ -2369,12 +2371,11 @@ void Paragraph::latex(BufferParams const & bparams, if (allowcust) column += d->startTeXParParams(bparams, os, - texrow, 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) { @@ -2478,8 +2479,7 @@ void Paragraph::latex(BufferParams const & bparams, // latexSpecialChar ignores spaces if // style.pass_thru is false. if (i != body_pos - 1) { - if (d->simpleTeXBlanks( - runparams, os, texrow, + if (d->simpleTeXBlanks(runparams, os, i, column, font, style)) { // A surrogate pair was output. We // must not call latexSpecialChar @@ -2501,8 +2501,7 @@ void Paragraph::latex(BufferParams const & bparams, // and then split to handle the two modes separately. if (c == META_INSET) { if (i >= start_pos && (end_pos == -1 || i < end_pos)) { - d->latexInset(bparams, os, - texrow, rp, running_font, + d->latexInset(bparams, os, rp, running_font, basefont, outerfont, open_font, runningChange, style, i, column); } @@ -2510,7 +2509,7 @@ void Paragraph::latex(BufferParams const & bparams, if (i >= start_pos && (end_pos == -1 || i < end_pos)) { try { d->latexSpecialChar(os, rp, running_font, runningChange, - style, i, column); + style, i, end_pos, column); } catch (EncodingException & e) { if (runparams.dryrun) { os << "<" << _("LyX Warning: ") @@ -2560,7 +2559,7 @@ void Paragraph::latex(BufferParams const & bparams, os << "}]~"; } - if (allowcust && d->endTeXParParams(bparams, os, texrow, runparams) + if (allowcust && d->endTeXParParams(bparams, os, runparams) && runparams.encoding != prev_encoding) { runparams.encoding = prev_encoding; if (!runparams.isFullUnicode()) @@ -2731,10 +2730,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() + "'"; @@ -2814,6 +2814,7 @@ docstring Paragraph::simpleLyXHTMLOnePar(Buffer const & buf, } xs.closeFontTags(); + xs.endParagraph(); return retval; } @@ -2845,13 +2846,40 @@ bool Paragraph::isLineSeparator(pos_type pos) const bool Paragraph::isWordSeparator(pos_type pos) const { + 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 ' and escape chars to the spellchecker - static docstring const quote = from_utf8(lyxrc.spellchecker_esc_chars + '\''); - return (!isLetterChar(c) && !isDigitASCII(c) && !contains(quote, c)) - || pos == size(); + // 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]; + 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] != '-')); } @@ -2907,9 +2935,9 @@ void Paragraph::changeLanguage(BufferParams const & bparams, if (font.language() == from) { font.setLanguage(to); setFont(i, font); + d->requestSpellCheck(i); } } - d->requestSpellCheck(size()); } @@ -3343,12 +3371,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++; } @@ -3586,7 +3614,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); } @@ -3642,6 +3670,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); @@ -3653,28 +3682,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; } @@ -3766,7 +3800,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 && check_boundary && pos > 0 && isWordSeparator(pos)) + if (result || pos <= 0 || pos > size()) + return result; + if (check_boundary && (pos == size() || isWordSeparator(pos))) result = SpellChecker::misspelled(d->speller_state_.getState(pos - 1)); return result; }