File indexing completed on 2024-04-28 07:46:44
0001 /* 0002 SPDX-FileCopyrightText: 2008-2009 Erlend Hamberg <ehamberg@gmail.com> 0003 SPDX-FileCopyrightText: 2011 Svyatoslav Kuzmich <svatoslav1@gmail.com> 0004 SPDX-FileCopyrightText: 2012 Vegard Øye 0005 SPDX-FileCopyrightText: 2013 Simon St James <kdedevel@etotheipiplusone.com> 0006 0007 SPDX-License-Identifier: LGPL-2.0-or-later 0008 */ 0009 0010 #include "katecommandrangeexpressionparser.h" 0011 0012 #include "katedocument.h" 0013 #include "kateview.h" 0014 0015 #include <QStringList> 0016 0017 using KTextEditor::Cursor; 0018 using KTextEditor::Range; 0019 0020 CommandRangeExpressionParser::CommandRangeExpressionParser() 0021 { 0022 m_line = QStringLiteral("\\d+"); 0023 m_lastLine = QStringLiteral("\\$"); 0024 m_thisLine = QStringLiteral("\\."); 0025 0026 m_forwardSearch = QStringLiteral("/([^/]*)/?"); 0027 m_forwardSearch2 = QStringLiteral("/[^/]*/?"); // no group 0028 m_backwardSearch = QStringLiteral("\\?([^?]*)\\??"); 0029 m_backwardSearch2 = QStringLiteral("\\?[^?]*\\??"); // no group 0030 m_base = QLatin1String("(?:%1)").arg(m_mark) + QLatin1String("|(?:%1)").arg(m_line) + QLatin1String("|(?:%1)").arg(m_thisLine) 0031 + QLatin1String("|(?:%1)").arg(m_lastLine) + QLatin1String("|(?:%1)").arg(m_forwardSearch2) + QLatin1String("|(?:%1)").arg(m_backwardSearch2); 0032 m_offset = QLatin1String("[+-](?:%1)?").arg(m_base); 0033 0034 // The position regexp contains two groups: the base and the offset. 0035 // The offset may be empty. 0036 m_position = QStringLiteral("(%1)((?:%2)*)").arg(m_base, m_offset); 0037 0038 // The range regexp contains seven groups: the first is the start position, the second is 0039 // the base of the start position, the third is the offset of the start position, the 0040 // fourth is the end position including a leading comma, the fifth is end position 0041 // without the comma, the sixth is the base of the end position, and the seventh is the 0042 // offset of the end position. The third and fourth groups may be empty, and the 0043 // fifth, sixth and seventh groups are contingent on the fourth group. 0044 m_cmdRangeRegex.setPattern(QStringLiteral("^(%1)((?:,(%1))?)").arg(m_position)); 0045 } 0046 0047 Range CommandRangeExpressionParser::parseRangeExpression(const QString &command, 0048 KTextEditor::ViewPrivate *view, 0049 QString &destRangeExpression, 0050 QString &destTransformedCommand) 0051 { 0052 CommandRangeExpressionParser rangeExpressionParser; 0053 return rangeExpressionParser.parseRangeExpression(command, destRangeExpression, destTransformedCommand, view); 0054 } 0055 0056 Range CommandRangeExpressionParser::parseRangeExpression(const QString &command, 0057 QString &destRangeExpression, 0058 QString &destTransformedCommand, 0059 KTextEditor::ViewPrivate *view) 0060 { 0061 Range parsedRange(0, -1, 0, -1); 0062 if (command.isEmpty()) { 0063 return parsedRange; 0064 } 0065 QString commandTmp = command; 0066 bool leadingRangeWasPercent = false; 0067 // expand '%' to '1,$' ("all lines") if at the start of the line 0068 if (commandTmp.at(0) == QLatin1Char('%')) { 0069 commandTmp.replace(0, 1, QStringLiteral("1,$")); 0070 leadingRangeWasPercent = true; 0071 } 0072 0073 const auto match = m_cmdRangeRegex.match(commandTmp); 0074 if (match.hasMatch() && match.capturedLength(0) > 0) { 0075 commandTmp.remove(m_cmdRangeRegex); 0076 0077 const QString position_string1 = match.captured(1); 0078 QString position_string2 = match.captured(4); 0079 int position1 = calculatePosition(position_string1, view); 0080 0081 int position2; 0082 if (!position_string2.isEmpty()) { 0083 // remove the comma 0084 position_string2 = match.captured(5); 0085 position2 = calculatePosition(position_string2, view); 0086 } else { 0087 position2 = position1; 0088 } 0089 0090 // special case: if the command is just a number with an optional +/- prefix, rewrite to "goto" 0091 if (commandTmp.isEmpty()) { 0092 commandTmp = QStringLiteral("goto %1").arg(position1); 0093 } else { 0094 parsedRange.setRange(KTextEditor::Range(position1 - 1, 0, position2 - 1, 0)); 0095 } 0096 0097 destRangeExpression = leadingRangeWasPercent ? QStringLiteral("%") : match.captured(0); 0098 destTransformedCommand = commandTmp; 0099 } 0100 0101 return parsedRange; 0102 } 0103 0104 int CommandRangeExpressionParser::calculatePosition(const QString &string, KTextEditor::ViewPrivate *view) 0105 { 0106 int pos = 0; 0107 std::vector<bool> operators_list; 0108 const QStringList split = string.split(QRegularExpression(QStringLiteral("[-+](?!([+-]|$))"))); 0109 std::vector<int> values; 0110 0111 for (const QString &line : split) { 0112 pos += line.size(); 0113 0114 if (pos < string.size()) { 0115 if (string.at(pos) == QLatin1Char('+')) { 0116 operators_list.push_back(true); 0117 } else if (string.at(pos) == QLatin1Char('-')) { 0118 operators_list.push_back(false); 0119 } else { 0120 Q_ASSERT(false); 0121 } 0122 } 0123 0124 ++pos; 0125 0126 static const auto lineRe = QRegularExpression(QRegularExpression::anchoredPattern(m_line)); 0127 static const auto lastLineRe = QRegularExpression(QRegularExpression::anchoredPattern(m_lastLine)); 0128 static const auto thisLineRe = QRegularExpression(QRegularExpression::anchoredPattern(m_thisLine)); 0129 static const auto forwardSearchRe = QRegularExpression(QRegularExpression::anchoredPattern(m_forwardSearch)); 0130 static const auto backwardSearchRe = QRegularExpression(QRegularExpression::anchoredPattern(m_backwardSearch)); 0131 0132 QRegularExpressionMatch rmatch; 0133 if (lineRe.match(line).hasMatch()) { 0134 values.push_back(line.toInt()); 0135 } else if (lastLineRe.match(line).hasMatch()) { 0136 values.push_back(view->doc()->lines()); 0137 } else if (thisLineRe.match(line).hasMatch()) { 0138 values.push_back(view->cursorPosition().line() + 1); 0139 } else if (line.contains(forwardSearchRe, &rmatch)) { 0140 const QString pattern = rmatch.captured(1); 0141 const int matchLine = 0142 view->doc()->searchText(Range(view->cursorPosition(), view->doc()->documentEnd()), pattern, KTextEditor::Regex).first().start().line(); 0143 values.push_back((matchLine < 0) ? -1 : matchLine + 1); 0144 } else if (line.contains(backwardSearchRe, &rmatch)) { 0145 const QString pattern = rmatch.captured(1); 0146 const int matchLine = view->doc()->searchText(Range(Cursor(0, 0), view->cursorPosition()), pattern, KTextEditor::Regex).first().start().line(); 0147 values.push_back((matchLine < 0) ? -1 : matchLine + 1); 0148 } 0149 } 0150 0151 if (values.empty()) { 0152 return -1; 0153 } 0154 0155 int result = values.at(0); 0156 for (size_t i = 0; i < operators_list.size(); ++i) { 0157 if (operators_list.at(i) == true) { 0158 result += values.at(i + 1); 0159 } else { 0160 result -= values.at(i + 1); 0161 } 0162 } 0163 0164 return result; 0165 }