Index: src/view/kateview.h =================================================================== --- src/view/kateview.h +++ src/view/kateview.h @@ -135,7 +135,12 @@ private Q_SLOTS: /** - * internal use, apply word wrap + * Wrap lines touched by the selection with respect of existing paragraphs. + * To do so will the paragraph prior to the wrap joined as one single line + * which cause an almost perfect wrapped paragraph as long as there are no + * unneeded spaces exist or some formatting like this comment block. + * Without any selection the current line is wrapped. + * Empty lines around each paragraph are untouched. */ void applyWordWrap(); Index: src/view/kateview.cpp =================================================================== --- src/view/kateview.cpp +++ src/view/kateview.cpp @@ -87,6 +87,8 @@ #include #include +#include + //#define VIEW_RANGE_DEBUG //END includes @@ -499,9 +501,9 @@ a = ac->addAction(QStringLiteral("tools_apply_wordwrap")); a->setText(i18n("Apply &Word Wrap")); - a->setWhatsThis(i18n("Use this command to wrap all lines of the current document which are longer than the width of the" - " current view, to fit into this view.

This is a static word wrap, meaning it is not updated" - " when the view is resized.")); + a->setWhatsThis(i18n("Use this to wrap the current line, or to reformat the selected lines as paragraph, " + "to fit the 'Wrap words at' setting in the configuration dialog.

" + "This is a static word wrap, meaning the document is changed.")); connect(a, SIGNAL(triggered(bool)), SLOT(applyWordWrap())); a = ac->addAction(QStringLiteral("tools_cleanIndent")); @@ -697,7 +699,8 @@ a = m_toggleDynWrap = toggleAction = new KToggleAction(i18n("&Dynamic Word Wrap"), this); ac->addAction(QStringLiteral("view_dynamic_word_wrap"), a); ac->setDefaultShortcut(a, QKeySequence(Qt::Key_F10)); - a->setWhatsThis(i18n("If this option is checked, the text lines will be wrapped at the view border on the screen.")); + a->setWhatsThis(i18n("If this option is checked, the text lines will be wrapped at the view border on the screen.

" + "This is only a view option, meaning the document will not changed.")); connect(a, SIGNAL(triggered(bool)), SLOT(toggleDynWordWrap())); a = m_setDynWrapIndicators = new KSelectAction(i18n("Dynamic Word Wrap Indicators"), this); @@ -2340,11 +2343,48 @@ void KTextEditor::ViewPrivate::applyWordWrap() { - if (selection()) { - doc()->wrapText(selectionRange().start().line(), selectionRange().end().line()); - } else { - doc()->wrapText(0, doc()->lastLine()); + doc()->editStart(); + int first = selectionRange().start().line(); + int last = selectionRange().end().line(); + if (first == last) { + // Either no selection or only one line selected, wrap only the current line + first = cursorPosition().line(); + doc()->wrapText(first, first); + doc()->editEnd(); + return; } + + // Because we shrink and expand lines, we need a powerful "Moving Cursor" + std::unique_ptr curr(doc()->newMovingCursor(KTextEditor::Cursor(selectionRange().start()))); + + // Scan the selected range for paragraphs, whereas each empty line trigger a new paragraph + for (int line = first; line <= selectionRange().end().line(); ++line) { + // Is our first line a somehow filled line? + if(doc()->plainKateTextLine(first)->firstChar() < 0) { + // Fast forward to first non empty line + ++first; + curr->setPosition(curr->line() + 1, 0); + continue; + } + + // Is our current line a somehow filled line? If not, wrap the paragraph + if (doc()->plainKateTextLine(line)->firstChar() < 0) { + curr->setPosition(line, 0); // Set on empty line + doc()->joinLines(first, line - 1); + doc()->wrapText(first, first); + first = curr->line() + 1; + line = first; + } + } + + // If there was no paragraph, we need to wrap now + bool needWrap = (curr->line() != selectionRange().end().line()); + if (needWrap && doc()->plainKateTextLine(first)->firstChar() != -1) { + doc()->joinLines(first, selectionRange().end().line()); + doc()->wrapText(first, first); + } + + doc()->editEnd(); } //END