KTextEditor

marks.cpp
1/*
2 SPDX-FileCopyrightText: KDE Developers
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#include "marks.h"
8#include "katedocument.h"
9#include "kateview.h"
10#include <vimode/inputmodemanager.h>
11#include <vimode/modes/normalvimode.h>
12
13#include <KLocalizedString>
14
15using namespace KateVi;
16
17namespace
18{
19const QChar BeginEditYanked = QLatin1Char('[');
20const QChar EndEditYanked = QLatin1Char(']');
21const QChar LastChange = QLatin1Char('.');
22const QChar InsertStopped = QLatin1Char('^');
23const QChar SelectionBegin = QLatin1Char('<');
24const QChar SelectionEnd = QLatin1Char('>');
25const QChar FirstUserMark = QLatin1Char('a');
26const QChar LastUserMark = QLatin1Char('z');
27const QChar BeforeJump = QLatin1Char('\'');
28const QChar BeforeJumpAlter = QLatin1Char('`');
29const QChar UserMarks[] = {QLatin1Char('a'), QLatin1Char('b'), QLatin1Char('c'), QLatin1Char('d'), QLatin1Char('e'), QLatin1Char('f'), QLatin1Char('g'),
30 QLatin1Char('h'), QLatin1Char('i'), QLatin1Char('j'), QLatin1Char('k'), QLatin1Char('l'), QLatin1Char('m'), QLatin1Char('n'),
31 QLatin1Char('o'), QLatin1Char('p'), QLatin1Char('q'), QLatin1Char('r'), QLatin1Char('s'), QLatin1Char('t'), QLatin1Char('u'),
32 QLatin1Char('v'), QLatin1Char('w'), QLatin1Char('x'), QLatin1Char('y'), QLatin1Char('z')};
33}
34
35Marks::Marks(InputModeManager *imm)
36 : m_inputModeManager(imm)
37 , m_doc(imm->view()->doc())
38 , m_settingMark(false)
39{
40 connect(m_doc, &KTextEditor::DocumentPrivate::markChanged, this, &Marks::markChanged);
41}
42
43void Marks::readSessionConfig(const KConfigGroup &config)
44{
45 QStringList marks = config.readEntry("ViMarks", QStringList());
46 for (int i = 0; i + 2 < marks.size(); i += 3) {
47 KTextEditor::Cursor c(marks.at(i + 1).toInt(), marks.at(i + 2).toInt());
48 setMark(marks.at(i).at(0), c);
49 }
50
51 syncViMarksAndBookmarks();
52}
53
54void Marks::writeSessionConfig(KConfigGroup &config) const
55{
56 if (m_marks.isEmpty()) {
57 return;
58 }
59
61 l.reserve(m_marks.size());
62 for (const auto &[key, value] : m_marks.asKeyValueRange()) {
63 l << key << QString::number(value->line()) << QString::number(value->column());
64 }
65 config.writeEntry("ViMarks", l);
66}
67
68void Marks::setMark(const QChar &_mark, const KTextEditor::Cursor pos)
69{
70 // move on insert is type based, this allows to reuse cursors!
71 // reuse is important for editing intensive things like replace-all
72 const bool moveoninsert = _mark != BeginEditYanked;
73
74 m_settingMark = true;
75
76 // ` and ' is the same register (position before jump)
77 const QChar mark = (_mark == BeforeJumpAlter) ? BeforeJump : _mark;
78
79 // if we have already a cursor for this type: adjust it
80 bool needToAdjustVisibleMark = true;
81 if (KTextEditor::MovingCursor *oldCursor = m_marks.value(mark)) {
82 // cleanup mark display only if line changes
83 needToAdjustVisibleMark = oldCursor->line() != pos.line();
84 if (needToAdjustVisibleMark) {
85 int number_of_marks = 0;
86 const auto keys = m_marks.keys();
87 for (QChar c : keys) {
88 if (m_marks.value(c)->line() == oldCursor->line()) {
89 number_of_marks++;
90 }
91 }
92 if (number_of_marks == 1) {
93 m_doc->removeMark(oldCursor->line(), KTextEditor::Document::markType01);
94 }
95 }
96
97 // adjust position
98 oldCursor->setPosition(pos);
99 } else {
100 // if no old mark of that type, create new one
103 m_marks.insert(mark, m_doc->newMovingCursor(pos, behavior));
104 }
105
106 // Showing what mark we set, can be skipped if we did not change the line
107 if (isShowable(mark)) {
108 if (needToAdjustVisibleMark && !(m_doc->mark(pos.line()) & KTextEditor::Document::markType01)) {
109 m_doc->addMark(pos.line(), KTextEditor::Document::markType01);
110 }
111
112 // only show message for active view
113 if (m_inputModeManager->view()->viewInputMode() == KTextEditor::View::ViInputMode) {
114 if (m_doc->activeView() == m_inputModeManager->view()) {
115 m_inputModeManager->getViNormalMode()->message(i18n("Mark set: %1", mark));
116 }
117 }
118 }
119
120 m_settingMark = false;
121}
122
123KTextEditor::Cursor Marks::getMarkPosition(const QChar &mark) const
124{
125 if (m_marks.contains(mark)) {
126 KTextEditor::MovingCursor *c = m_marks.value(mark);
127 return KTextEditor::Cursor(c->line(), c->column());
128 }
129
131}
132
134{
135 Q_UNUSED(doc)
136
137 if (mark.type != KTextEditor::Document::Bookmark || m_settingMark) {
138 return;
139 }
140
142 const auto keys = m_marks.keys();
143 for (QChar markerChar : keys) {
144 if (m_marks.value(markerChar)->line() == mark.line) {
145 m_marks.remove(markerChar);
146 }
147 }
148 } else if (action == KTextEditor::Document::MarkAdded) {
149 bool freeMarkerCharFound = false;
150
151 for (const QChar &markerChar : UserMarks) {
152 if (!m_marks.value(markerChar)) {
153 setMark(markerChar, KTextEditor::Cursor(mark.line, 0));
154 freeMarkerCharFound = true;
155 break;
156 }
157 }
158
159 // only show error when we are in Vi input mode
160 if (!freeMarkerCharFound && m_inputModeManager->view()->viewInputMode() == KTextEditor::View::ViInputMode) {
161 m_inputModeManager->getViNormalMode()->error(i18n("There are no more chars for the next bookmark."));
162 }
163 }
164}
165
166void Marks::syncViMarksAndBookmarks()
167{
168 const QHash<int, KTextEditor::Mark *> &marks = m_doc->marks();
169
170 // Each bookmark should have a vi mark on the same line.
171 for (auto mark : marks) {
173 continue;
174 }
175
176 bool thereIsViMarkForThisLine = false;
177 for (auto cursor : std::as_const(m_marks)) {
178 if (cursor->line() == mark->line) {
179 thereIsViMarkForThisLine = true;
180 break;
181 }
182 }
183
184 if (thereIsViMarkForThisLine) {
185 continue;
186 }
187
188 for (const QChar &markerChar : UserMarks) {
189 if (!m_marks.value(markerChar)) {
190 setMark(markerChar, KTextEditor::Cursor(mark->line, 0));
191 break;
192 }
193 }
194 }
195
196 // For showable vi mark a line should be bookmarked.
197 const auto keys = m_marks.keys();
198 for (QChar markChar : keys) {
199 if (!isShowable(markChar)) {
200 continue;
201 }
202
203 bool thereIsKateMarkForThisLine = false;
204 for (auto mark : marks) {
206 continue;
207 }
208
209 if (m_marks.value(markChar)->line() == mark->line) {
210 thereIsKateMarkForThisLine = true;
211 break;
212 }
213 }
214
215 if (!thereIsKateMarkForThisLine) {
216 m_doc->addMark(m_marks.value(markChar)->line(), KTextEditor::Document::markType01);
217 }
218 }
219}
220
221QString Marks::getMarksOnTheLine(int line) const
222{
223 QString res;
224 const auto keys = m_marks.keys();
225 for (QChar markerChar : keys) {
226 if (m_marks.value(markerChar)->line() == line) {
227 res += markerChar + QLatin1Char(':') + QString::number(m_marks.value(markerChar)->column()) + QLatin1Char(' ');
228 }
229 }
230
231 return res;
232}
233
234bool Marks::isShowable(const QChar &mark)
235{
236 return FirstUserMark <= mark && mark <= LastUserMark;
237}
238
239void Marks::setStartEditYanked(const KTextEditor::Cursor pos)
240{
241 setMark(BeginEditYanked, pos);
242}
243
244void Marks::setFinishEditYanked(const KTextEditor::Cursor pos)
245{
246 setMark(EndEditYanked, pos);
247}
248
249void Marks::setLastChange(const KTextEditor::Cursor pos)
250{
251 setMark(LastChange, pos);
252}
253
254void Marks::setInsertStopped(const KTextEditor::Cursor pos)
255{
256 setMark(InsertStopped, pos);
257}
258
259void Marks::setSelectionStart(const KTextEditor::Cursor pos)
260{
261 setMark(SelectionBegin, pos);
262}
263
264void Marks::setSelectionFinish(const KTextEditor::Cursor pos)
265{
266 setMark(SelectionEnd, pos);
267}
268
269void Marks::setUserMark(const QChar &mark, const KTextEditor::Cursor pos)
270{
271 Q_ASSERT(FirstUserMark <= mark && mark <= LastUserMark);
272 setMark(mark, pos);
273}
274
275KTextEditor::Cursor Marks::getStartEditYanked() const
276{
277 return getMarkPosition(BeginEditYanked);
278}
279
280KTextEditor::Cursor Marks::getFinishEditYanked() const
281{
282 return getMarkPosition(EndEditYanked);
283}
284
285KTextEditor::Cursor Marks::getSelectionStart() const
286{
287 return getMarkPosition(SelectionBegin);
288}
289
290KTextEditor::Cursor Marks::getSelectionFinish() const
291{
292 return getMarkPosition(SelectionEnd);
293}
294
295KTextEditor::Cursor Marks::getLastChange() const
296{
297 return getMarkPosition(LastChange);
298}
299
300KTextEditor::Cursor Marks::getInsertStopped() const
301{
302 return getMarkPosition(InsertStopped);
303}
void writeEntry(const char *key, const char *value, WriteConfigFlags pFlags=Normal)
QString readEntry(const char *key, const char *aDefault=nullptr) const
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
static constexpr Cursor invalid() noexcept
Returns an invalid cursor.
Definition cursor.h:112
A KParts derived class representing a text document.
Definition document.h:284
@ markType01
Bookmark.
Definition document.h:1553
MarkChangeAction
Possible actions on a mark.
Definition document.h:1658
@ MarkAdded
action: a mark was added.
Definition document.h:1659
@ MarkRemoved
action: a mark was removed.
Definition document.h:1660
uint type
The mark types in the line, combined with logical OR.
Definition document.h:79
int line
The line that contains the mark.
Definition document.h:76
A Cursor which is bound to a specific Document, and maintains its position.
virtual int column() const =0
Retrieve the column on which this cursor is situated.
InsertBehavior
Insert behavior of this cursor, should it stay if text is insert at its position or should it move.
@ StayOnInsert
stay on insert
@ MoveOnInsert
move on insert
virtual int line() const =0
Retrieve the line on which this cursor is situated.
@ ViInputMode
Vi mode.
Definition view.h:288
QString i18n(const char *text, const TYPE &arg...)
const_reference at(qsizetype i) const const
void reserve(qsizetype size)
qsizetype size() const const
bool contains(const Key &key) const const
iterator insert(const Key &key, const T &value)
bool isEmpty() const const
QList< Key > keys() const const
size_type remove(const Key &key)
size_type size() const const
T value(const Key &key, const T &defaultValue) const const
QString number(double n, char format, int precision)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:15:44 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.