KTextEditor

katecommandrangeexpressionparser.cpp
1/*
2 SPDX-FileCopyrightText: 2008-2009 Erlend Hamberg <ehamberg@gmail.com>
3 SPDX-FileCopyrightText: 2011 Svyatoslav Kuzmich <svatoslav1@gmail.com>
4 SPDX-FileCopyrightText: 2012 Vegard Øye
5 SPDX-FileCopyrightText: 2013 Simon St James <kdedevel@etotheipiplusone.com>
6
7 SPDX-License-Identifier: LGPL-2.0-or-later
8*/
9
10#include "katecommandrangeexpressionparser.h"
11
12#include "katedocument.h"
13#include "kateview.h"
14
15#include <QStringList>
16
19
20CommandRangeExpressionParser::CommandRangeExpressionParser()
21{
22 m_line = QStringLiteral("\\d+");
23 m_lastLine = QStringLiteral("\\$");
24 m_thisLine = QStringLiteral("\\.");
25
26 m_forwardSearch = QStringLiteral("/([^/]*)/?");
27 m_forwardSearch2 = QStringLiteral("/[^/]*/?"); // no group
28 m_backwardSearch = QStringLiteral("\\?([^?]*)\\??");
29 m_backwardSearch2 = QStringLiteral("\\?[^?]*\\??"); // no group
30 m_base = QLatin1String("(?:%1)").arg(m_mark) + QLatin1String("|(?:%1)").arg(m_line) + QLatin1String("|(?:%1)").arg(m_thisLine)
31 + QLatin1String("|(?:%1)").arg(m_lastLine) + QLatin1String("|(?:%1)").arg(m_forwardSearch2) + QLatin1String("|(?:%1)").arg(m_backwardSearch2);
32 m_offset = QLatin1String("[+-](?:%1)?").arg(m_base);
33
34 // The position regexp contains two groups: the base and the offset.
35 // The offset may be empty.
36 m_position = QStringLiteral("(%1)((?:%2)*)").arg(m_base, m_offset);
37
38 // The range regexp contains seven groups: the first is the start position, the second is
39 // the base of the start position, the third is the offset of the start position, the
40 // fourth is the end position including a leading comma, the fifth is end position
41 // without the comma, the sixth is the base of the end position, and the seventh is the
42 // offset of the end position. The third and fourth groups may be empty, and the
43 // fifth, sixth and seventh groups are contingent on the fourth group.
44 m_cmdRangeRegex.setPattern(QStringLiteral("^(%1)((?:,(%1))?)").arg(m_position));
45}
46
47Range CommandRangeExpressionParser::parseRangeExpression(const QString &command,
48 KTextEditor::ViewPrivate *view,
49 QString &destRangeExpression,
50 QString &destTransformedCommand)
51{
52 CommandRangeExpressionParser rangeExpressionParser;
53 return rangeExpressionParser.parseRangeExpression(command, destRangeExpression, destTransformedCommand, view);
54}
55
56Range CommandRangeExpressionParser::parseRangeExpression(const QString &command,
57 QString &destRangeExpression,
58 QString &destTransformedCommand,
59 KTextEditor::ViewPrivate *view)
60{
61 Range parsedRange(0, -1, 0, -1);
62 if (command.isEmpty()) {
63 return parsedRange;
64 }
65 QString commandTmp = command;
66 bool leadingRangeWasPercent = false;
67 // expand '%' to '1,$' ("all lines") if at the start of the line
68 if (commandTmp.at(0) == QLatin1Char('%')) {
69 commandTmp.replace(0, 1, QStringLiteral("1,$"));
70 leadingRangeWasPercent = true;
71 }
72
73 const auto match = m_cmdRangeRegex.match(commandTmp);
74 if (match.hasMatch() && match.capturedLength(0) > 0) {
75 commandTmp.remove(m_cmdRangeRegex);
76
77 const QString position_string1 = match.captured(1);
78 QString position_string2 = match.captured(4);
79 int position1 = calculatePosition(position_string1, view);
80
81 int position2;
82 if (!position_string2.isEmpty()) {
83 // remove the comma
84 position_string2 = match.captured(5);
85 position2 = calculatePosition(position_string2, view);
86 } else {
87 position2 = position1;
88 }
89
90 // special case: if the command is just a number with an optional +/- prefix, rewrite to "goto"
91 if (commandTmp.isEmpty()) {
92 commandTmp = QStringLiteral("goto %1").arg(position1);
93 } else {
94 parsedRange.setRange(KTextEditor::Range(position1 - 1, 0, position2 - 1, 0));
95 }
96
97 destRangeExpression = leadingRangeWasPercent ? QStringLiteral("%") : match.captured(0);
98 destTransformedCommand = commandTmp;
99 }
100
101 return parsedRange;
102}
103
104int CommandRangeExpressionParser::calculatePosition(const QString &string, KTextEditor::ViewPrivate *view)
105{
106 int pos = 0;
107 std::vector<bool> operators_list;
108 const QStringList split = string.split(QRegularExpression(QStringLiteral("[-+](?!([+-]|$))")));
109 std::vector<int> values;
110
111 for (const QString &line : split) {
112 pos += line.size();
113
114 if (pos < string.size()) {
115 if (string.at(pos) == QLatin1Char('+')) {
116 operators_list.push_back(true);
117 } else if (string.at(pos) == QLatin1Char('-')) {
118 operators_list.push_back(false);
119 } else {
120 Q_ASSERT(false);
121 }
122 }
123
124 ++pos;
125
126 static const auto lineRe = QRegularExpression(QRegularExpression::anchoredPattern(m_line));
127 static const auto lastLineRe = QRegularExpression(QRegularExpression::anchoredPattern(m_lastLine));
128 static const auto thisLineRe = QRegularExpression(QRegularExpression::anchoredPattern(m_thisLine));
129 static const auto forwardSearchRe = QRegularExpression(QRegularExpression::anchoredPattern(m_forwardSearch));
130 static const auto backwardSearchRe = QRegularExpression(QRegularExpression::anchoredPattern(m_backwardSearch));
131
133 if (lineRe.match(line).hasMatch()) {
134 values.push_back(line.toInt());
135 } else if (lastLineRe.match(line).hasMatch()) {
136 values.push_back(view->doc()->lines());
137 } else if (thisLineRe.match(line).hasMatch()) {
138 values.push_back(view->cursorPosition().line() + 1);
139 } else if (line.contains(forwardSearchRe, &rmatch)) {
140 const QString pattern = rmatch.captured(1);
141 const int matchLine =
142 view->doc()->searchText(Range(view->cursorPosition(), view->doc()->documentEnd()), pattern, KTextEditor::Regex).first().start().line();
143 values.push_back((matchLine < 0) ? -1 : matchLine + 1);
144 } else if (line.contains(backwardSearchRe, &rmatch)) {
145 const QString pattern = rmatch.captured(1);
146 const int matchLine = view->doc()->searchText(Range(Cursor(0, 0), view->cursorPosition()), pattern, KTextEditor::Regex).first().start().line();
147 values.push_back((matchLine < 0) ? -1 : matchLine + 1);
148 }
149 }
150
151 if (values.empty()) {
152 return -1;
153 }
154
155 int result = values.at(0);
156 for (size_t i = 0; i < operators_list.size(); ++i) {
157 if (operators_list.at(i) == true) {
158 result += values.at(i + 1);
159 } else {
160 result -= values.at(i + 1);
161 }
162 }
163
164 return result;
165}
The Cursor represents a position in a Document.
Definition cursor.h:75
constexpr int line() const noexcept
Retrieve the line on which this cursor is situated.
Definition cursor.h:174
KTextEditor::Cursor documentEnd() const override
End position of the document.
int lines() const override
Get the count of lines of the document.
An object representing a section of text, from one Cursor to another.
KCOREADDONS_EXPORT Result match(QStringView pattern, QStringView str)
@ Regex
Treats the pattern as a regular expression.
Definition document.h:51
const_reference at(qsizetype i) const const
T & first()
QRegularExpressionMatch match(QStringView subjectView, qsizetype offset, MatchType matchType, MatchOptions matchOptions) const const
QString anchoredPattern(QStringView expression)
void setPattern(const QString &pattern)
QString captured(QStringView name) const const
QString arg(Args &&... args) const const
const QChar at(qsizetype position) const const
bool isEmpty() const const
QString & remove(QChar ch, Qt::CaseSensitivity cs)
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:11:27 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.