TextEditor: improve sort selected lines

Try to get a sensible scope when there is no selection when sorting
lines.

Use the indent level of the current block and select all blocks that are
not empty with the same indent level around that block before sorting
the lines.

Change-Id: I68cbd95f95a0cc4425a0339b992225c3946a6858
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: Marcus Tillmanns <marcus.tillmanns@qt.io>
This commit is contained in:
David Schulz
2023-10-23 13:38:10 +02:00
parent 30ef90c322
commit 2e93051aaf
5 changed files with 70 additions and 44 deletions

View File

@@ -668,8 +668,6 @@ public:
void transformSelection(TransformationMethod method);
void transformSelectedLines(ListTransformationMethod method);
void slotUpdateExtraAreaWidth(std::optional<int> width = {});
void slotUpdateRequest(const QRect &r, int dy);
void slotUpdateBlockNotify(const QTextBlock &);
@@ -2420,9 +2418,72 @@ void TextEditorWidget::lowercaseSelection()
d->transformSelection([](const QString &str) { return str.toLower(); });
}
void TextEditorWidget::sortSelectedLines()
void TextEditorWidget::sortLines()
{
d->transformSelectedLines([](QStringList &list) { list.sort(); });
if (d->m_cursors.hasMultipleCursors())
return;
QTextCursor cursor = textCursor();
if (!cursor.hasSelection()) {
// try to get a sensible scope for the sort
const QTextBlock currentBlock = cursor.block();
QString text = currentBlock.text();
if (text.simplified().isEmpty())
return;
const TabSettings ts = textDocument()->tabSettings();
const int currentIndent = ts.columnAt(text, TabSettings::firstNonSpace(text));
int anchor = currentBlock.position();
for (auto block = currentBlock.previous(); block.isValid(); block = block.previous()) {
text = block.text();
if (text.simplified().isEmpty()
|| ts.columnAt(text, TabSettings::firstNonSpace(text)) != currentIndent) {
break;
}
anchor = block.position();
}
int pos = currentBlock.position();
for (auto block = currentBlock.next(); block.isValid(); block = block.next()) {
text = block.text();
if (text.simplified().isEmpty()
|| ts.columnAt(text, TabSettings::firstNonSpace(text)) != currentIndent) {
break;
}
pos = block.position();
}
if (anchor == pos)
return;
cursor.setPosition(anchor);
cursor.setPosition(pos, QTextCursor::KeepAnchor);
cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
}
const bool downwardDirection = cursor.anchor() < cursor.position();
int startPosition = cursor.selectionStart();
int endPosition = cursor.selectionEnd();
cursor.setPosition(startPosition);
cursor.movePosition(QTextCursor::StartOfBlock);
startPosition = cursor.position();
cursor.setPosition(endPosition, QTextCursor::KeepAnchor);
if (cursor.positionInBlock() == 0)
cursor.movePosition(QTextCursor::PreviousBlock, QTextCursor::KeepAnchor);
cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
endPosition = qMax(cursor.position(), endPosition);
const QString text = cursor.selectedText();
QStringList lines = text.split(QChar::ParagraphSeparator);
lines.sort();
cursor.insertText(lines.join(QChar::ParagraphSeparator));
// (re)select the changed lines
// Note: this assumes the transformation did not change the length
cursor.setPosition(downwardDirection ? startPosition : endPosition);
cursor.setPosition(downwardDirection ? endPosition : startPosition, QTextCursor::KeepAnchor);
setTextCursor(cursor);
}
void TextEditorWidget::indent()
@@ -9039,41 +9100,6 @@ void TextEditorWidgetPrivate::transformSelection(TransformationMethod method)
q->setMultiTextCursor(cursor);
}
void TextEditorWidgetPrivate::transformSelectedLines(ListTransformationMethod method)
{
if (!method || m_cursors.hasMultipleCursors())
return;
QTextCursor cursor = q->textCursor();
if (!cursor.hasSelection())
return;
const bool downwardDirection = cursor.anchor() < cursor.position();
int startPosition = cursor.selectionStart();
int endPosition = cursor.selectionEnd();
cursor.setPosition(startPosition);
cursor.movePosition(QTextCursor::StartOfBlock);
startPosition = cursor.position();
cursor.setPosition(endPosition, QTextCursor::KeepAnchor);
if (cursor.positionInBlock() == 0)
cursor.movePosition(QTextCursor::PreviousBlock, QTextCursor::KeepAnchor);
cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
endPosition = qMax(cursor.position(), endPosition);
const QString text = cursor.selectedText();
QStringList lines = text.split(QChar::ParagraphSeparator);
method(lines);
cursor.insertText(lines.join(QChar::ParagraphSeparator));
// (re)select the changed lines
// Note: this assumes the transformation did not change the length
cursor.setPosition(downwardDirection ? startPosition : endPosition);
cursor.setPosition(downwardDirection ? endPosition : startPosition, QTextCursor::KeepAnchor);
q->setTextCursor(cursor);
}
void TextEditorWidget::inSnippetMode(bool *active)
{
*active = d->m_snippetOverlay->isVisible();

View File

@@ -426,7 +426,7 @@ public:
void uppercaseSelection();
void lowercaseSelection();
void sortSelectedLines();
void sortLines();
void cleanWhitespace();

View File

@@ -366,8 +366,8 @@ void TextEditorActionHandlerPrivate::createActions()
[] (TextEditorWidget *w) { w->lowercaseSelection(); }, true, Tr::tr("Lowercase Selection"),
QKeySequence(Core::useMacShortcuts ? Tr::tr("Meta+U") : Tr::tr("Alt+U")),
G_EDIT_TEXT, advancedEditMenu);
m_modifyingActions << registerAction(SORT_SELECTED_LINES,
[] (TextEditorWidget *w) { w->sortSelectedLines(); }, false, Tr::tr("&Sort Selected Lines"),
m_modifyingActions << registerAction(SORT_LINES,
[] (TextEditorWidget *w) { w->sortLines(); }, false, Tr::tr("&Sort Lines"),
QKeySequence(Core::useMacShortcuts ? Tr::tr("Meta+Shift+S") : Tr::tr("Alt+Shift+S")),
G_EDIT_TEXT, advancedEditMenu);
registerAction(FOLD,

View File

@@ -157,7 +157,7 @@ const char INSERT_LINE_ABOVE[] = "TextEditor.InsertLineAboveCurrentLine";
const char INSERT_LINE_BELOW[] = "TextEditor.InsertLineBelowCurrentLine";
const char UPPERCASE_SELECTION[] = "TextEditor.UppercaseSelection";
const char LOWERCASE_SELECTION[] = "TextEditor.LowercaseSelection";
const char SORT_SELECTED_LINES[] = "TextEditor.SortSelectedLines";
const char SORT_LINES[] = "TextEditor.SortSelectedLines";
const char CUT_LINE[] = "TextEditor.CutLine";
const char COPY_LINE[] = "TextEditor.CopyLine";
const char ADD_SELECT_NEXT_FIND_MATCH[] = "TextEditor.AddSelectionNextFindMatch";

View File

@@ -15,7 +15,7 @@ def main():
"visible='1' window=':Qt Creator_Core::Internal::MainWindow'}", 3000)
placeCursorToLine(editor, "bbb")
invokeMenuItem("Edit", "Select All")
invokeMenuItem("Edit", "Advanced", "Sort Selected Lines")
invokeMenuItem("Edit", "Advanced", "Sort Lines")
test.verify(waitFor("str(editor.plainText) == sorted", 2000),
"Verify that sorted text\n%s\nmatches the expected text\n%s" % (editor.plainText, sorted))
invokeMenuItem('File', 'Revert "unsorted.txt" to Saved')