Index: src/EditProfileDialog.h =================================================================== --- src/EditProfileDialog.h +++ src/EditProfileDialog.h @@ -160,6 +160,7 @@ void toggleCtrlRequiredForDrag(bool); void toggleDropUrlsAsText(bool); void toggleCopyTextToClipboard(bool); + void toggleTrimLeadingSpacesInSelectedText(bool); void toggleTrimTrailingSpacesInSelectedText(bool); void pasteFromX11Selection(); void pasteFromClipboard(); Index: src/EditProfileDialog.cpp =================================================================== --- src/EditProfileDialog.cpp +++ src/EditProfileDialog.cpp @@ -1156,6 +1156,10 @@ _ui->copyTextToClipboardButton, Profile::AutoCopySelectedText, SLOT(toggleCopyTextToClipboard(bool)) }, + { + _ui->trimLeadingSpacesButton, Profile::TrimLeadingSpacesInSelectedText, + SLOT(toggleTrimLeadingSpacesInSelectedText(bool)) + }, { _ui->trimTrailingSpacesButton, Profile::TrimTrailingSpacesInSelectedText, SLOT(toggleTrimTrailingSpacesInSelectedText(bool)) @@ -1351,6 +1355,11 @@ updateTempProfileProperty(Profile::AutoCopySelectedText, enable); } +void EditProfileDialog::toggleTrimLeadingSpacesInSelectedText(bool enable) +{ + updateTempProfileProperty(Profile::TrimLeadingSpacesInSelectedText, enable); +} + void EditProfileDialog::toggleTrimTrailingSpacesInSelectedText(bool enable) { updateTempProfileProperty(Profile::TrimTrailingSpacesInSelectedText, enable); Index: src/EditProfileDialog.ui =================================================================== --- src/EditProfileDialog.ui +++ src/EditProfileDialog.ui @@ -925,6 +925,16 @@ + + + + Trim leading spaces in selected text, useful in some instances + + + Trim leading spaces + + + Index: src/Emulation.cpp =================================================================== --- src/Emulation.cpp +++ src/Emulation.cpp @@ -100,7 +100,7 @@ void Emulation::checkSelectedText() { - QString text = _currentScreen->selectedText(true); + QString text = _currentScreen->selectedText(true, false, false, false); emit selectionChanged(text); } Index: src/Filter.cpp =================================================================== --- src/Filter.cpp +++ src/Filter.cpp @@ -142,6 +142,7 @@ reset(); PlainTextDecoder decoder; + decoder.setLeadingWhitespace(false); decoder.setTrailingWhitespace(false); // setup new shared buffers for the filters to process on Index: src/Profile.h =================================================================== --- src/Profile.h +++ src/Profile.h @@ -205,6 +205,8 @@ CtrlRequiredForDrag, /** (bool) If true, automatically copy selected text into the clipboard */ AutoCopySelectedText, + /** (bool) If true, leading spaces are trimmed in selected text */ + TrimLeadingSpacesInSelectedText, /** (bool) If true, trailing spaces are trimmed in selected text */ TrimTrailingSpacesInSelectedText, /** (bool) If true, then dropped URLs will be pasted as text without asking */ Index: src/Profile.cpp =================================================================== --- src/Profile.cpp +++ src/Profile.cpp @@ -114,6 +114,7 @@ , { CtrlRequiredForDrag, "CtrlRequiredForDrag" , INTERACTION_GROUP , QVariant::Bool } , { DropUrlsAsText , "DropUrlsAsText" , INTERACTION_GROUP , QVariant::Bool } , { AutoCopySelectedText , "AutoCopySelectedText" , INTERACTION_GROUP , QVariant::Bool } + , { TrimLeadingSpacesInSelectedText , "TrimLeadingSpacesInSelectedText" , INTERACTION_GROUP , QVariant::Bool } , { TrimTrailingSpacesInSelectedText , "TrimTrailingSpacesInSelectedText" , INTERACTION_GROUP , QVariant::Bool } , { PasteFromSelectionEnabled , "PasteFromSelectionEnabled" , INTERACTION_GROUP , QVariant::Bool } , { PasteFromClipboardEnabled , "PasteFromClipboardEnabled" , INTERACTION_GROUP , QVariant::Bool } @@ -187,6 +188,7 @@ setProperty(OpenLinksByDirectClickEnabled, false); setProperty(CtrlRequiredForDrag, true); setProperty(AutoCopySelectedText, false); + setProperty(TrimLeadingSpacesInSelectedText, false); setProperty(TrimTrailingSpacesInSelectedText, false); setProperty(DropUrlsAsText, false); setProperty(PasteFromSelectionEnabled, true); Index: src/Screen.h =================================================================== --- src/Screen.h +++ src/Screen.h @@ -443,10 +443,13 @@ * be inserted into the returned text at the end of each terminal line. * @param trimTrailingSpaces Specifies whether trailing spaces should be * trimmed in the returned text. + * @param trimLeadingSpaces Specifies whether leading spaces should be + * trimmed in the returned text. * @param html Specifies if returned text should have HTML tags. */ - QString selectedText(bool preserveLineBreaks, bool trimTrailingSpaces = false, - bool html = false) const; + QString selectedText(bool preserveLineBreaks, + bool trimTrailingSpaces, bool trimLeadingSpaces, + bool html) const; /** * Convenience method. Returns the text between two indices. @@ -456,10 +459,13 @@ * be inserted into the returned text at the end of each terminal line. * @param trimTrailingSpaces Specifies whether trailing spaces should be * trimmed in the returned text. + * @param trimLeadingSpaces Specifies whether leading spaces should be + * trimmed in the returned text. * @param html Specifies if returned text should have HTML tags. */ QString text(int startIndex, int endIndex, bool preserveLineBreaks, - bool trimTrailingSpaces = false, bool html = false) const; + bool trimTrailingSpaces, bool trimLeadingSpaces, + bool html) const; /** * Copies part of the output to a stream. @@ -481,9 +487,12 @@ * be inserted into the returned text at the end of each terminal line. * @param trimTrailingSpaces Specifies whether trailing spaces should be * trimmed in the returned text. + * @param trimLeadingSpaces Specifies whether leading spaces should be + * trimmed in the returned text. */ void writeSelectionToStream(TerminalCharacterDecoder *decoder, bool - preserveLineBreaks = true, bool trimTrailingSpaces = false) const; + preserveLineBreaks, + bool trimTrailingSpaces, bool trimLeadingSpaces) const; /** * Checks if the text between from and to is inside the current @@ -602,7 +611,7 @@ //appendNewLine - if true a new line character (\n) is appended to the end of the line int copyLineToStream(int line, int start, int count, TerminalCharacterDecoder *decoder, bool appendNewLine, bool preserveLineBreaks, - bool trimTrailingSpaces) const; + bool trimTrailingSpaces, bool trimLeadingSpaces) const; //fills a section of the screen image with the character 'c' //the parameters are specified as offsets from the start of the screen image. @@ -634,7 +643,7 @@ // copies text from 'startIndex' to 'endIndex' to a stream // startIndex and endIndex are positions generated using the loc(x,y) macro void writeToStream(TerminalCharacterDecoder *decoder, int startIndex, int endIndex, - bool preserveLineBreaks = true, bool trimTrailingSpaces = false) const; + bool preserveLineBreaks, bool trimTrailingSpaces, bool trimLeadingSpaces) const; // copies 'count' lines from the screen buffer into 'dest', // starting from 'startLine', where 0 is the first line in the screen buffer void copyFromScreen(Character *dest, int startLine, int count) const; Index: src/Screen.cpp =================================================================== --- src/Screen.cpp +++ src/Screen.cpp @@ -1087,15 +1087,15 @@ return pos >= _selTopLeft && pos <= _selBottomRight && columnInSelection; } -QString Screen::selectedText(bool preserveLineBreaks, bool trimTrailingSpaces, bool html) const +QString Screen::selectedText(bool preserveLineBreaks, bool trimTrailingSpaces, bool trimLeadingSpaces, bool html) const { if (!isSelectionValid()) return QString(); - return text(_selTopLeft, _selBottomRight, preserveLineBreaks, trimTrailingSpaces, html); + return text(_selTopLeft, _selBottomRight, preserveLineBreaks, trimTrailingSpaces, trimLeadingSpaces, html); } -QString Screen::text(int startIndex, int endIndex, bool preserveLineBreaks, bool trimTrailingSpaces, bool html) const +QString Screen::text(int startIndex, int endIndex, bool preserveLineBreaks, bool trimTrailingSpaces, bool trimLeadingSpaces, bool html) const { QString result; QTextStream stream(&result, QIODevice::ReadWrite); @@ -1114,7 +1114,7 @@ } decoder->begin(&stream); - writeToStream(decoder, startIndex, endIndex, preserveLineBreaks, trimTrailingSpaces); + writeToStream(decoder, startIndex, endIndex, preserveLineBreaks, trimTrailingSpaces, trimLeadingSpaces); decoder->end(); return result; @@ -1127,17 +1127,17 @@ void Screen::writeSelectionToStream(TerminalCharacterDecoder* decoder , bool preserveLineBreaks, - bool trimTrailingSpaces) const + bool trimTrailingSpaces, bool trimLeadingSpaces) const { if (!isSelectionValid()) return; - writeToStream(decoder, _selTopLeft, _selBottomRight, preserveLineBreaks, trimTrailingSpaces); + writeToStream(decoder, _selTopLeft, _selBottomRight, preserveLineBreaks, trimTrailingSpaces, trimLeadingSpaces); } void Screen::writeToStream(TerminalCharacterDecoder* decoder, int startIndex, int endIndex, bool preserveLineBreaks, - bool trimTrailingSpaces) const + bool trimTrailingSpaces, bool trimLeadingSpaces) const { const int top = startIndex / _columns; const int left = startIndex % _columns; @@ -1161,7 +1161,8 @@ decoder, appendNewLine, preserveLineBreaks, - trimTrailingSpaces); + trimTrailingSpaces, + trimLeadingSpaces); // if the selection goes beyond the end of the last line then // append a new line character. @@ -1183,7 +1184,8 @@ TerminalCharacterDecoder* decoder, bool appendNewLine, bool preserveLineBreaks, - bool trimTrailingSpaces) const + bool trimTrailingSpaces, + bool trimLeadingSpaces) const { //buffer to hold characters for decoding //the buffer is static to avoid initializing every @@ -1275,6 +1277,25 @@ } } + if (trimLeadingSpaces) { + int spacesCount = 0; + for (spacesCount = 0; spacesCount < count; spacesCount++) { + if (!QChar(characterBuffer[spacesCount].character).isSpace()) { + break; + } + } + + if (spacesCount >= count) { + return 0; + } + + for (int i=0; i < count - spacesCount; i++) { + characterBuffer[i] = characterBuffer[i + spacesCount]; + } + + count -= spacesCount; + } + //decode line and write to text stream decoder->decodeLine((Character*) characterBuffer , count, currentLineProperties); @@ -1284,7 +1305,7 @@ void Screen::writeLinesToStream(TerminalCharacterDecoder* decoder, int fromLine, int toLine) const { - writeToStream(decoder, loc(0, fromLine), loc(_columns - 1, toLine)); + writeToStream(decoder, loc(0, fromLine), loc(_columns - 1, toLine), true, false, false); } void Screen::addHistLine() Index: src/ScreenWindow.h =================================================================== --- src/ScreenWindow.h +++ src/ScreenWindow.h @@ -229,10 +229,12 @@ * * @param preserveLineBreaks See Screen::selectedText() * @param trimTrailingSpaces See Screen::selectedText() + * @param trimLeadingSpaces See Screen::selectedText() * @param html Specifies if returned text should have HTML tags. */ - QString selectedText(bool preserveLineBreaks, bool trimTrailingSpaces = false, - bool html = false) const; + QString selectedText(bool preserveLineBreaks, + bool trimTrailingSpaces, bool trimLeadingSpaces, + bool html) const; public Q_SLOTS: /** Index: src/ScreenWindow.cpp =================================================================== --- src/ScreenWindow.cpp +++ src/ScreenWindow.cpp @@ -125,10 +125,11 @@ return result; } -QString ScreenWindow::selectedText(bool preserveLineBreaks, bool trimTrailingSpaces, +QString ScreenWindow::selectedText(bool preserveLineBreaks, + bool trimTrailingSpaces, bool trimLeadingSpaces, bool html) const { - return _screen->selectedText(preserveLineBreaks, trimTrailingSpaces, html); + return _screen->selectedText(preserveLineBreaks, trimTrailingSpaces, trimLeadingSpaces, html); } void ScreenWindow::getSelectionStart(int &column, int &line) Index: src/SessionController.cpp =================================================================== --- src/SessionController.cpp +++ src/SessionController.cpp @@ -1148,7 +1148,7 @@ void SessionController::searchBarEvent() { - QString selectedText = _view->screenWindow()->selectedText(true, true); + QString selectedText = _view->screenWindow()->selectedText(true, true, true, false); if (!selectedText.isEmpty()) _searchBar->setSearchText(selectedText); Index: src/TerminalCharacterDecoder.h =================================================================== --- src/TerminalCharacterDecoder.h +++ src/TerminalCharacterDecoder.h @@ -74,6 +74,17 @@ public: PlainTextDecoder(); + /** + * Set whether leading whitespace at the end of lines should be included + * in the output. + * Defaults to true. + */ + void setLeadingWhitespace(bool enable); + /** + * Returns whether leading whitespace at the end of lines is included + * in the output. + */ + bool leadingWhitespace() const; /** * Set whether trailing whitespace at the end of lines should be included * in the output. @@ -102,6 +113,7 @@ private: QTextStream *_output; + bool _includeLeadingWhitespace; bool _includeTrailingWhitespace; bool _recordLinePositions; Index: src/TerminalCharacterDecoder.cpp =================================================================== --- src/TerminalCharacterDecoder.cpp +++ src/TerminalCharacterDecoder.cpp @@ -33,10 +33,19 @@ using namespace Konsole; PlainTextDecoder::PlainTextDecoder() : _output(nullptr) + , _includeLeadingWhitespace(true) , _includeTrailingWhitespace(true) , _recordLinePositions(false) { } +void PlainTextDecoder::setLeadingWhitespace(bool enable) +{ + _includeLeadingWhitespace = enable; +} +bool PlainTextDecoder::leadingWhitespace() const +{ + return _includeLeadingWhitespace; +} void PlainTextDecoder::setTrailingWhitespace(bool enable) { _includeTrailingWhitespace = enable; @@ -82,12 +91,26 @@ QString plainText; plainText.reserve(count); - int outputCount = count; + // If we should remove leading whitespace find the first non-space character + int start = 0; + if (!_includeLeadingWhitespace) { + for (start = 0; start < count; start++) { + if (!characters[start].isSpace()) { + break; + } + } + } + + int outputCount = count - start; + + if (outputCount <= 0) { + return; + } // if inclusion of trailing whitespace is disabled then find the end of the // line if (!_includeTrailingWhitespace) { - for (int i = count - 1 ; i >= 0 ; i--) { + for (int i = count - 1 ; i >= start ; i--) { if (!characters[i].isSpace()) break; else @@ -97,7 +120,7 @@ // find out the last technically real character in the line int realCharacterGuard = -1; - for (int i = count - 1 ; i >= 0 ; i--) { + for (int i = count - 1 ; i >= start ; i--) { // FIXME: the special case of '\n' here is really ugly // Maybe the '\n' should be added after calling this method in // Screen::copyLineToStream() @@ -107,7 +130,7 @@ } } - for (int i = 0; i < outputCount;) { + for (int i = start; i < outputCount;) { if ((characters[i].rendition & RE_EXTENDED_CHAR) != 0) { ushort extendedCharLength = 0; const ushort* chars = ExtendedCharTable::instance.lookupExtendedChar(characters[i].character, extendedCharLength); Index: src/TerminalDisplay.h =================================================================== --- src/TerminalDisplay.h +++ src/TerminalDisplay.h @@ -207,6 +207,22 @@ return _openLinksByDirectClick; } + /** + * Sets whether leading spaces should be trimmed in selected text. + */ + void setTrimLeadingSpaces(bool enabled) + { + _trimLeadingSpaces = enabled; + } + + /** + * Returns true if leading spaces should be trimmed in selected text. + */ + bool trimLeadingSpaces() const + { + return _trimLeadingSpaces; + } + /** * Sets whether trailing spaces should be trimmed in selected text. */ @@ -970,6 +986,7 @@ SessionController *_sessionController; + bool _trimLeadingSpaces; // trim leading spaces in selected text bool _trimTrailingSpaces; // trim trailing spaces in selected text bool _mouseWheelZoom; // enable mouse wheel zooming or not Index: src/TerminalDisplay.cpp =================================================================== --- src/TerminalDisplay.cpp +++ src/TerminalDisplay.cpp @@ -377,6 +377,7 @@ , _useFontLineCharacters(false) , _printerFriendly(false) , _sessionController(nullptr) + , _trimLeadingSpaces(false) , _trimTrailingSpaces(false) , _margin(1) , _centerContents(false) @@ -2893,10 +2894,10 @@ if (_screenWindow == nullptr) return; - QString text = _screenWindow->selectedText(_preserveLineBreaks, _trimTrailingSpaces); + QString text = _screenWindow->selectedText(_preserveLineBreaks, _trimTrailingSpaces, _trimLeadingSpaces, false); if (text.isEmpty()) return; - QString html = _screenWindow->selectedText(_preserveLineBreaks, _trimTrailingSpaces, true); + QString html = _screenWindow->selectedText(_preserveLineBreaks, _trimTrailingSpaces, _trimLeadingSpaces, true); auto mimeData = new QMimeData; mimeData->setText(text); @@ -2915,10 +2916,10 @@ if (_screenWindow == nullptr) return; - QString text = _screenWindow->selectedText(_preserveLineBreaks, _trimTrailingSpaces); + QString text = _screenWindow->selectedText(_preserveLineBreaks, _trimTrailingSpaces, _trimLeadingSpaces, false); if (text.isEmpty()) return; - QString html = _screenWindow->selectedText(_preserveLineBreaks, _trimTrailingSpaces, true); + QString html = _screenWindow->selectedText(_preserveLineBreaks, _trimTrailingSpaces, _trimLeadingSpaces, true); auto mimeData = new QMimeData; mimeData->setText(text); Index: src/TerminalDisplayAccessible.cpp =================================================================== --- src/TerminalDisplayAccessible.cpp +++ src/TerminalDisplayAccessible.cpp @@ -99,7 +99,7 @@ } return display->screenWindow()->screen()->text(0, display->_usedColumns * display->_usedLines, - true); + true, false, false, false); } void TerminalDisplayAccessible::addSelection(int startOffset, int endOffset) @@ -186,7 +186,7 @@ return QString(); } - return display()->screenWindow()->screen()->text(startOffset, endOffset, true); + return display()->screenWindow()->screen()->text(startOffset, endOffset, true, false, false, false); } TerminalDisplay *TerminalDisplayAccessible::display() const Index: src/ViewManager.cpp =================================================================== --- src/ViewManager.cpp +++ src/ViewManager.cpp @@ -908,6 +908,7 @@ view->setDropUrlsAsText(profile->property(Profile::DropUrlsAsText)); view->setBidiEnabled(profile->bidiRenderingEnabled()); view->setLineSpacing(profile->lineSpacing()); + view->setTrimLeadingSpaces(profile->property(Profile::TrimLeadingSpacesInSelectedText)); view->setTrimTrailingSpaces(profile->property(Profile::TrimTrailingSpacesInSelectedText)); view->setOpenLinksByDirectClick(profile->property(Profile::OpenLinksByDirectClickEnabled));