2022-08-19 15:59:36 +02:00
|
|
|
// Copyright (C) 2016 The Qt Company Ltd.
|
2022-12-21 10:12:09 +01:00
|
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
2009-05-14 16:37:17 +02:00
|
|
|
|
|
|
|
|
#include "uncommentselection.h"
|
2021-06-28 09:13:57 +02:00
|
|
|
|
|
|
|
|
#include "qtcassert.h"
|
2022-05-25 06:30:04 +02:00
|
|
|
#include "multitextcursor.h"
|
2021-06-28 09:13:57 +02:00
|
|
|
|
2012-02-15 10:42:41 +01:00
|
|
|
#include <QTextBlock>
|
2022-05-24 13:45:35 +02:00
|
|
|
#include <QTextCursor>
|
2022-05-17 22:25:02 +02:00
|
|
|
#include <QTextDocument>
|
2009-05-14 16:37:17 +02:00
|
|
|
|
2021-06-28 09:13:57 +02:00
|
|
|
namespace Utils {
|
2010-04-30 13:08:06 +02:00
|
|
|
|
2017-04-24 16:01:14 +02:00
|
|
|
CommentDefinition CommentDefinition::CppStyle = CommentDefinition("//", "/*", "*/");
|
|
|
|
|
CommentDefinition CommentDefinition::HashStyle = CommentDefinition("#");
|
|
|
|
|
|
2018-07-23 10:45:40 +02:00
|
|
|
CommentDefinition::CommentDefinition() = default;
|
2010-04-30 13:08:06 +02:00
|
|
|
|
2017-04-24 16:01:14 +02:00
|
|
|
CommentDefinition::CommentDefinition(const QString &single, const QString &multiStart,
|
|
|
|
|
const QString &multiEnd)
|
2018-07-23 10:45:40 +02:00
|
|
|
: singleLine(single),
|
2017-04-24 16:01:14 +02:00
|
|
|
multiLineStart(multiStart),
|
|
|
|
|
multiLineEnd(multiEnd)
|
2014-07-30 16:01:34 +02:00
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool CommentDefinition::isValid() const
|
|
|
|
|
{
|
|
|
|
|
return hasSingleLineStyle() || hasMultiLineStyle();
|
|
|
|
|
}
|
|
|
|
|
|
2013-05-25 00:42:44 +02:00
|
|
|
bool CommentDefinition::hasSingleLineStyle() const
|
2010-04-30 13:08:06 +02:00
|
|
|
{
|
2013-05-25 00:42:44 +02:00
|
|
|
return !singleLine.isEmpty();
|
2010-04-30 13:08:06 +02:00
|
|
|
}
|
|
|
|
|
|
2013-05-25 00:42:44 +02:00
|
|
|
bool CommentDefinition::hasMultiLineStyle() const
|
2010-04-30 13:08:06 +02:00
|
|
|
{
|
2013-05-25 00:42:44 +02:00
|
|
|
return !multiLineStart.isEmpty() && !multiLineEnd.isEmpty();
|
2010-04-30 13:08:06 +02:00
|
|
|
}
|
|
|
|
|
|
2013-05-25 00:42:44 +02:00
|
|
|
static bool isComment(const QString &text, int index,
|
|
|
|
|
const QString &commentType)
|
2010-04-30 13:08:06 +02:00
|
|
|
{
|
|
|
|
|
const int length = commentType.length();
|
|
|
|
|
|
|
|
|
|
Q_ASSERT(text.length() - index >= length);
|
|
|
|
|
|
|
|
|
|
int i = 0;
|
|
|
|
|
while (i < length) {
|
|
|
|
|
if (text.at(index + i) != commentType.at(i))
|
|
|
|
|
return false;
|
|
|
|
|
++i;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2021-06-28 09:13:57 +02:00
|
|
|
QTextCursor unCommentSelection(const QTextCursor &cursorIn,
|
|
|
|
|
const CommentDefinition &definition,
|
|
|
|
|
bool preferSingleLine)
|
2009-05-14 16:37:17 +02:00
|
|
|
{
|
2014-07-30 16:01:34 +02:00
|
|
|
if (!definition.isValid())
|
2021-09-06 07:22:07 +02:00
|
|
|
return cursorIn;
|
2010-04-30 13:08:06 +02:00
|
|
|
|
2021-09-06 07:22:07 +02:00
|
|
|
QTextCursor cursor = cursorIn;
|
2009-05-14 16:37:17 +02:00
|
|
|
QTextDocument *doc = cursor.document();
|
|
|
|
|
cursor.beginEditBlock();
|
|
|
|
|
|
|
|
|
|
int pos = cursor.position();
|
|
|
|
|
int anchor = cursor.anchor();
|
|
|
|
|
int start = qMin(anchor, pos);
|
|
|
|
|
int end = qMax(anchor, pos);
|
|
|
|
|
bool anchorIsStart = (anchor == start);
|
|
|
|
|
|
|
|
|
|
QTextBlock startBlock = doc->findBlock(start);
|
|
|
|
|
QTextBlock endBlock = doc->findBlock(end);
|
|
|
|
|
|
|
|
|
|
if (end > start && endBlock.position() == end) {
|
|
|
|
|
--end;
|
|
|
|
|
endBlock = endBlock.previous();
|
|
|
|
|
}
|
|
|
|
|
|
2010-04-30 13:08:06 +02:00
|
|
|
bool doMultiLineStyleUncomment = false;
|
|
|
|
|
bool doMultiLineStyleComment = false;
|
|
|
|
|
bool doSingleLineStyleUncomment = false;
|
2009-05-14 16:37:17 +02:00
|
|
|
|
|
|
|
|
bool hasSelection = cursor.hasSelection();
|
|
|
|
|
|
2021-02-04 10:59:05 +01:00
|
|
|
if (hasSelection && definition.hasMultiLineStyle() && !preferSingleLine) {
|
2010-04-30 13:08:06 +02:00
|
|
|
|
2009-05-14 16:37:17 +02:00
|
|
|
QString startText = startBlock.text();
|
|
|
|
|
int startPos = start - startBlock.position();
|
2013-05-25 00:42:44 +02:00
|
|
|
const int multiLineStartLength = definition.multiLineStart.length();
|
2009-05-14 16:37:17 +02:00
|
|
|
bool hasLeadingCharacters = !startText.left(startPos).trimmed().isEmpty();
|
|
|
|
|
|
2010-04-30 13:08:06 +02:00
|
|
|
if (startPos >= multiLineStartLength
|
|
|
|
|
&& isComment(startText,
|
|
|
|
|
startPos - multiLineStartLength,
|
2013-05-25 00:42:44 +02:00
|
|
|
definition.multiLineStart)) {
|
2010-04-30 13:08:06 +02:00
|
|
|
startPos -= multiLineStartLength;
|
|
|
|
|
start -= multiLineStartLength;
|
|
|
|
|
}
|
2009-05-14 16:37:17 +02:00
|
|
|
|
2013-05-25 00:42:44 +02:00
|
|
|
bool hasSelStart = startPos <= startText.length() - multiLineStartLength
|
|
|
|
|
&& isComment(startText, startPos, definition.multiLineStart);
|
2009-05-14 16:37:17 +02:00
|
|
|
|
|
|
|
|
QString endText = endBlock.text();
|
|
|
|
|
int endPos = end - endBlock.position();
|
2013-05-25 00:42:44 +02:00
|
|
|
const int multiLineEndLength = definition.multiLineEnd.length();
|
2010-04-30 13:08:06 +02:00
|
|
|
bool hasTrailingCharacters =
|
2013-05-25 00:42:44 +02:00
|
|
|
!endText.left(endPos).remove(definition.singleLine).trimmed().isEmpty()
|
2010-04-30 13:08:06 +02:00
|
|
|
&& !endText.mid(endPos).trimmed().isEmpty();
|
|
|
|
|
|
|
|
|
|
if (endPos <= endText.length() - multiLineEndLength
|
2013-05-25 00:42:44 +02:00
|
|
|
&& isComment(endText, endPos, definition.multiLineEnd)) {
|
2010-04-30 13:08:06 +02:00
|
|
|
endPos += multiLineEndLength;
|
|
|
|
|
end += multiLineEndLength;
|
2009-05-14 16:37:17 +02:00
|
|
|
}
|
|
|
|
|
|
2013-05-25 00:42:44 +02:00
|
|
|
bool hasSelEnd = endPos >= multiLineEndLength
|
|
|
|
|
&& isComment(endText, endPos - multiLineEndLength, definition.multiLineEnd);
|
2009-05-14 16:37:17 +02:00
|
|
|
|
2010-04-30 13:08:06 +02:00
|
|
|
doMultiLineStyleUncomment = hasSelStart && hasSelEnd;
|
|
|
|
|
doMultiLineStyleComment = !doMultiLineStyleUncomment
|
|
|
|
|
&& (hasLeadingCharacters
|
|
|
|
|
|| hasTrailingCharacters
|
|
|
|
|
|| !definition.hasSingleLineStyle());
|
|
|
|
|
} else if (!hasSelection && !definition.hasSingleLineStyle()) {
|
|
|
|
|
|
|
|
|
|
QString text = startBlock.text().trimmed();
|
2021-02-04 10:59:05 +01:00
|
|
|
|
2013-05-25 00:42:44 +02:00
|
|
|
doMultiLineStyleUncomment = text.startsWith(definition.multiLineStart)
|
|
|
|
|
&& text.endsWith(definition.multiLineEnd);
|
2010-04-30 13:08:06 +02:00
|
|
|
doMultiLineStyleComment = !doMultiLineStyleUncomment && !text.isEmpty();
|
|
|
|
|
|
|
|
|
|
start = startBlock.position();
|
|
|
|
|
end = endBlock.position() + endBlock.length() - 1;
|
2010-05-07 14:17:48 +02:00
|
|
|
|
|
|
|
|
if (doMultiLineStyleUncomment) {
|
|
|
|
|
int offset = 0;
|
|
|
|
|
text = startBlock.text();
|
|
|
|
|
const int length = text.length();
|
|
|
|
|
while (offset < length && text.at(offset).isSpace())
|
|
|
|
|
++offset;
|
|
|
|
|
start += offset;
|
|
|
|
|
}
|
2009-05-14 16:37:17 +02:00
|
|
|
}
|
|
|
|
|
|
2010-04-30 13:08:06 +02:00
|
|
|
if (doMultiLineStyleUncomment) {
|
2009-05-14 16:37:17 +02:00
|
|
|
cursor.setPosition(end);
|
2010-04-30 13:08:06 +02:00
|
|
|
cursor.movePosition(QTextCursor::PreviousCharacter,
|
|
|
|
|
QTextCursor::KeepAnchor,
|
2013-05-25 00:42:44 +02:00
|
|
|
definition.multiLineEnd.length());
|
2009-05-14 16:37:17 +02:00
|
|
|
cursor.removeSelectedText();
|
|
|
|
|
cursor.setPosition(start);
|
2010-04-30 13:08:06 +02:00
|
|
|
cursor.movePosition(QTextCursor::NextCharacter,
|
|
|
|
|
QTextCursor::KeepAnchor,
|
2013-05-25 00:42:44 +02:00
|
|
|
definition.multiLineStart.length());
|
2009-05-14 16:37:17 +02:00
|
|
|
cursor.removeSelectedText();
|
2010-04-30 13:08:06 +02:00
|
|
|
} else if (doMultiLineStyleComment) {
|
2009-05-14 16:37:17 +02:00
|
|
|
cursor.setPosition(end);
|
2013-05-25 00:42:44 +02:00
|
|
|
cursor.insertText(definition.multiLineEnd);
|
2009-05-14 16:37:17 +02:00
|
|
|
cursor.setPosition(start);
|
2013-05-25 00:42:44 +02:00
|
|
|
cursor.insertText(definition.multiLineStart);
|
2009-05-14 16:37:17 +02:00
|
|
|
} else {
|
|
|
|
|
endBlock = endBlock.next();
|
2010-04-30 13:08:06 +02:00
|
|
|
doSingleLineStyleUncomment = true;
|
2009-05-14 16:37:17 +02:00
|
|
|
for (QTextBlock block = startBlock; block != endBlock; block = block.next()) {
|
2010-03-26 14:28:26 +01:00
|
|
|
QString text = block.text().trimmed();
|
2013-05-25 00:42:44 +02:00
|
|
|
if (!text.isEmpty() && !text.startsWith(definition.singleLine)) {
|
2010-04-30 13:08:06 +02:00
|
|
|
doSingleLineStyleUncomment = false;
|
2009-05-14 16:37:17 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-04-30 13:08:06 +02:00
|
|
|
|
2013-05-25 00:42:44 +02:00
|
|
|
const int singleLineLength = definition.singleLine.length();
|
2009-05-14 16:37:17 +02:00
|
|
|
for (QTextBlock block = startBlock; block != endBlock; block = block.next()) {
|
2010-04-30 13:08:06 +02:00
|
|
|
if (doSingleLineStyleUncomment) {
|
2009-05-14 16:37:17 +02:00
|
|
|
QString text = block.text();
|
|
|
|
|
int i = 0;
|
2010-04-30 13:08:06 +02:00
|
|
|
while (i <= text.size() - singleLineLength) {
|
2013-05-25 00:42:44 +02:00
|
|
|
if (isComment(text, i, definition.singleLine)) {
|
2009-05-14 16:37:17 +02:00
|
|
|
cursor.setPosition(block.position() + i);
|
2010-04-30 13:08:06 +02:00
|
|
|
cursor.movePosition(QTextCursor::NextCharacter,
|
|
|
|
|
QTextCursor::KeepAnchor,
|
|
|
|
|
singleLineLength);
|
2009-05-14 16:37:17 +02:00
|
|
|
cursor.removeSelectedText();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (!text.at(i).isSpace())
|
|
|
|
|
break;
|
|
|
|
|
++i;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2013-05-25 00:42:44 +02:00
|
|
|
const QString text = block.text();
|
2020-06-13 23:39:57 +03:00
|
|
|
for (QChar c : text) {
|
2010-03-26 14:28:26 +01:00
|
|
|
if (!c.isSpace()) {
|
2013-05-25 00:42:44 +02:00
|
|
|
if (definition.isAfterWhiteSpaces)
|
2010-04-30 13:08:06 +02:00
|
|
|
cursor.setPosition(block.position() + text.indexOf(c));
|
|
|
|
|
else
|
|
|
|
|
cursor.setPosition(block.position());
|
2013-05-25 00:42:44 +02:00
|
|
|
cursor.insertText(definition.singleLine);
|
2010-03-26 14:28:26 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-05-14 16:37:17 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-11 15:41:05 +02:00
|
|
|
cursor.endEditBlock();
|
|
|
|
|
|
2021-09-06 07:22:07 +02:00
|
|
|
cursor = cursorIn;
|
2009-05-14 16:37:17 +02:00
|
|
|
// adjust selection when commenting out
|
2010-04-30 13:08:06 +02:00
|
|
|
if (hasSelection && !doMultiLineStyleUncomment && !doSingleLineStyleUncomment) {
|
|
|
|
|
if (!doMultiLineStyleComment)
|
|
|
|
|
start = startBlock.position(); // move the comment into the selection
|
2009-05-14 16:37:17 +02:00
|
|
|
int lastSelPos = anchorIsStart ? cursor.position() : cursor.anchor();
|
|
|
|
|
if (anchorIsStart) {
|
|
|
|
|
cursor.setPosition(start);
|
|
|
|
|
cursor.setPosition(lastSelPos, QTextCursor::KeepAnchor);
|
|
|
|
|
} else {
|
|
|
|
|
cursor.setPosition(lastSelPos);
|
|
|
|
|
cursor.setPosition(start, QTextCursor::KeepAnchor);
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-09-06 07:22:07 +02:00
|
|
|
return cursor;
|
2009-05-14 16:37:17 +02:00
|
|
|
}
|
2021-06-28 09:13:57 +02:00
|
|
|
|
|
|
|
|
MultiTextCursor unCommentSelection(const MultiTextCursor &cursorIn,
|
|
|
|
|
const CommentDefinition &definiton,
|
|
|
|
|
bool preferSingleLine)
|
|
|
|
|
{
|
|
|
|
|
if (cursorIn.isNull())
|
|
|
|
|
return cursorIn;
|
|
|
|
|
if (!cursorIn.hasMultipleCursors())
|
|
|
|
|
return MultiTextCursor({unCommentSelection(cursorIn.mainCursor(), definiton, preferSingleLine)});
|
|
|
|
|
QMap<int, QTextCursor> cursors;
|
|
|
|
|
for (const QTextCursor &c : cursorIn) {
|
|
|
|
|
QTextBlock block = c.document()->findBlock(c.selectionStart());
|
|
|
|
|
QTC_ASSERT(block.isValid(), continue);
|
|
|
|
|
QTextBlock end = c.document()->findBlock(c.selectionEnd());
|
|
|
|
|
QTC_ASSERT(end.isValid(), continue);
|
|
|
|
|
end = end.next();
|
|
|
|
|
while (block != end && block.isValid()) {
|
|
|
|
|
if (!cursors.contains(block.blockNumber()))
|
|
|
|
|
cursors.insert(block.blockNumber(), QTextCursor(block));
|
|
|
|
|
block = block.next();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (const QTextCursor &c : cursors)
|
|
|
|
|
unCommentSelection(c, definiton, /*always prefer single line for multi cursor*/ true);
|
|
|
|
|
return cursorIn;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace Utils
|