diff --git a/src/EditProfileDialog.h b/EditProfileDialog.h --- a/src/EditProfileDialog.h +++ b/EditProfileDialog.h @@ -161,6 +161,7 @@ void toggleDropUrlsAsText(bool); void toggleCopyTextToClipboard(bool); void toggleTrimTrailingSpacesInSelectedText(bool); + void toggleTrimLeadingSpacesInSelectedText(bool); void pasteFromX11Selection(); void pasteFromClipboard(); diff --git a/src/EditProfileDialog.cpp b/EditProfileDialog.cpp --- a/src/EditProfileDialog.cpp +++ b/EditProfileDialog.cpp @@ -1160,6 +1160,10 @@ _ui->trimTrailingSpacesButton, Profile::TrimTrailingSpacesInSelectedText, SLOT(toggleTrimTrailingSpacesInSelectedText(bool)) }, + { + _ui->trimLeadingSpacesButton, Profile::TrimLeadingSpacesInSelectedText, + SLOT(toggleTrimLeadingSpacesInSelectedText(bool)) + }, { _ui->openLinksByDirectClickButton, Profile::OpenLinksByDirectClickEnabled, SLOT(toggleOpenLinksByDirectClick(bool)) @@ -1356,6 +1360,11 @@ updateTempProfileProperty(Profile::TrimTrailingSpacesInSelectedText, enable); } +void EditProfileDialog::toggleTrimLeadingSpacesInSelectedText(bool enable) +{ + updateTempProfileProperty(Profile::TrimLeadingSpacesInSelectedText, enable); +} + void EditProfileDialog::pasteFromX11Selection() { updateTempProfileProperty(Profile::MiddleClickPasteMode, Enum::PasteFromX11Selection); diff --git a/src/EditProfileDialog.ui b/EditProfileDialog.ui --- a/src/EditProfileDialog.ui +++ b/EditProfileDialog.ui @@ -935,6 +935,16 @@ + + + + Trim leading spaces in selected text, useful in some instances + + + Trim leading spaces + + + diff --git a/src/Emulation.cpp b/Emulation.cpp --- a/src/Emulation.cpp +++ b/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); } diff --git a/src/Filter.cpp b/Filter.cpp --- a/src/Filter.cpp +++ b/Filter.cpp @@ -143,6 +143,7 @@ PlainTextDecoder decoder; decoder.setTrailingWhitespace(false); + decoder.setLeadingWhitespace(false); // setup new shared buffers for the filters to process on auto newBuffer = new QString(); diff --git a/src/Profile.h b/Profile.h --- a/src/Profile.h +++ b/Profile.h @@ -207,6 +207,8 @@ AutoCopySelectedText, /** (bool) If true, trailing spaces are trimmed in selected text */ TrimTrailingSpacesInSelectedText, + /** (bool) If true, leading spaces are trimmed in selected text */ + TrimLeadingSpacesInSelectedText, /** (bool) If true, then dropped URLs will be pasted as text without asking */ DropUrlsAsText, /** (bool) If true, middle mouse button pastes from X Selection */ diff --git a/src/Profile.cpp b/Profile.cpp --- a/src/Profile.cpp +++ b/Profile.cpp @@ -115,6 +115,7 @@ , { DropUrlsAsText , "DropUrlsAsText" , INTERACTION_GROUP , QVariant::Bool } , { AutoCopySelectedText , "AutoCopySelectedText" , INTERACTION_GROUP , QVariant::Bool } , { TrimTrailingSpacesInSelectedText , "TrimTrailingSpacesInSelectedText" , INTERACTION_GROUP , QVariant::Bool } + , { TrimLeadingSpacesInSelectedText , "TrimLeadingSpacesInSelectedText" , INTERACTION_GROUP , QVariant::Bool } , { PasteFromSelectionEnabled , "PasteFromSelectionEnabled" , INTERACTION_GROUP , QVariant::Bool } , { PasteFromClipboardEnabled , "PasteFromClipboardEnabled" , INTERACTION_GROUP , QVariant::Bool } , { MiddleClickPasteMode, "MiddleClickPasteMode" , INTERACTION_GROUP , QVariant::Int } @@ -188,6 +189,7 @@ setProperty(CtrlRequiredForDrag, true); setProperty(AutoCopySelectedText, false); setProperty(TrimTrailingSpacesInSelectedText, false); + setProperty(TrimLeadingSpacesInSelectedText, false); setProperty(DropUrlsAsText, false); setProperty(PasteFromSelectionEnabled, true); setProperty(PasteFromClipboardEnabled, false); diff --git a/src/Screen.h b/Screen.h --- a/src/Screen.h +++ b/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; diff --git a/src/Screen.cpp b/Screen.cpp --- a/src/Screen.cpp +++ b/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() diff --git a/src/ScreenWindow.h b/ScreenWindow.h --- a/src/ScreenWindow.h +++ b/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: /** diff --git a/src/ScreenWindow.cpp b/ScreenWindow.cpp --- a/src/ScreenWindow.cpp +++ b/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) diff --git a/src/SessionController.cpp b/SessionController.cpp --- a/src/SessionController.cpp +++ b/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); diff --git a/src/TerminalCharacterDecoder.h b/TerminalCharacterDecoder.h --- a/src/TerminalCharacterDecoder.h +++ b/TerminalCharacterDecoder.h @@ -85,6 +85,17 @@ * in the output. */ bool trailingWhitespace() const; + /** + * 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; /** * Returns of character positions in the output stream * at which new lines where added. Returns an empty if setTrackLinePositions() is false or if @@ -103,6 +114,7 @@ private: QTextStream *_output; bool _includeTrailingWhitespace; + bool _includeLeadingWhitespace; bool _recordLinePositions; QList _linePositions; diff --git a/src/TerminalCharacterDecoder.cpp b/TerminalCharacterDecoder.cpp --- a/src/TerminalCharacterDecoder.cpp +++ b/TerminalCharacterDecoder.cpp @@ -34,6 +34,7 @@ PlainTextDecoder::PlainTextDecoder() : _output(nullptr) , _includeTrailingWhitespace(true) + , _includeLeadingWhitespace(true) , _recordLinePositions(false) { } @@ -45,6 +46,14 @@ { return _includeTrailingWhitespace; } +void PlainTextDecoder::setLeadingWhitespace(bool enable) +{ + _includeLeadingWhitespace = enable; +} +bool PlainTextDecoder::leadingWhitespace() const +{ + return _includeLeadingWhitespace; +} void PlainTextDecoder::begin(QTextStream* output) { _output = output; @@ -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); diff --git a/src/TerminalDisplay.h b/TerminalDisplay.h --- a/src/TerminalDisplay.h +++ b/TerminalDisplay.h @@ -223,6 +223,22 @@ return _trimTrailingSpaces; } + /** + * 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; + } + void setLineSpacing(uint); uint lineSpacing() const; @@ -971,6 +987,7 @@ SessionController *_sessionController; bool _trimTrailingSpaces; // trim trailing spaces in selected text + bool _trimLeadingSpaces; // trim leading spaces in selected text bool _mouseWheelZoom; // enable mouse wheel zooming or not int _margin; // the contents margin diff --git a/src/TerminalDisplay.cpp b/TerminalDisplay.cpp --- a/src/TerminalDisplay.cpp +++ b/TerminalDisplay.cpp @@ -378,6 +378,7 @@ , _printerFriendly(false) , _sessionController(nullptr) , _trimTrailingSpaces(false) + , _trimLeadingSpaces(false) , _margin(1) , _centerContents(false) , _opacity(1.0) @@ -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); diff --git a/src/TerminalDisplayAccessible.cpp b/TerminalDisplayAccessible.cpp --- a/src/TerminalDisplayAccessible.cpp +++ b/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 diff --git a/src/ViewManager.cpp b/ViewManager.cpp --- a/src/ViewManager.cpp +++ b/ViewManager.cpp @@ -909,6 +909,7 @@ view->setBidiEnabled(profile->bidiRenderingEnabled()); view->setLineSpacing(profile->lineSpacing()); view->setTrimTrailingSpaces(profile->property(Profile::TrimTrailingSpacesInSelectedText)); + view->setTrimLeadingSpaces(profile->property(Profile::TrimLeadingSpacesInSelectedText)); view->setOpenLinksByDirectClick(profile->property(Profile::OpenLinksByDirectClickEnabled));