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{
57 const auto keys = m_marks.keys();
58 for (QChar key : keys) {
59 l << key << QString::number(m_marks.value(key)->line()) << QString::number(m_marks.value(key)->column());
60 }
61 config.writeEntry("ViMarks", l);
62}
63
64void Marks::setMark(const QChar &_mark, const KTextEditor::Cursor pos)
65{
66 // move on insert is type based, this allows to reuse cursors!
67 // reuse is important for editing intensive things like replace-all
68 const bool moveoninsert = _mark != BeginEditYanked;
69
70 m_settingMark = true;
71
72 // ` and ' is the same register (position before jump)
73 const QChar mark = (_mark == BeforeJumpAlter) ? BeforeJump : _mark;
74
75 // if we have already a cursor for this type: adjust it
76 bool needToAdjustVisibleMark = true;
77 if (KTextEditor::MovingCursor *oldCursor = m_marks.value(mark)) {
78 // cleanup mark display only if line changes
79 needToAdjustVisibleMark = oldCursor->line() != pos.line();
80 if (needToAdjustVisibleMark) {
81 int number_of_marks = 0;
82 const auto keys = m_marks.keys();
83 for (QChar c : keys) {
84 if (m_marks.value(c)->line() == oldCursor->line()) {
85 number_of_marks++;
86 }
87 }
88 if (number_of_marks == 1) {
89 m_doc->removeMark(oldCursor->line(), KTextEditor::Document::markType01);
90 }
91 }
92
93 // adjust position
94 oldCursor->setPosition(pos);
95 } else {
96 // if no old mark of that type, create new one
99 m_marks.insert(mark, m_doc->newMovingCursor(pos, behavior));
100 }
101
102 // Showing what mark we set, can be skipped if we did not change the line
103 if (isShowable(mark)) {
104 if (needToAdjustVisibleMark && !(m_doc->mark(pos.line()) & KTextEditor::Document::markType01)) {
105 m_doc->addMark(pos.line(), KTextEditor::Document::markType01);
106 }
107
108 // only show message for active view
109 if (m_inputModeManager->view()->viewInputMode() == KTextEditor::View::ViInputMode) {
110 if (m_doc->activeView() == m_inputModeManager->view()) {
111 m_inputModeManager->getViNormalMode()->message(i18n("Mark set: %1", mark));
112 }
113 }
114 }
115
116 m_settingMark = false;
117}
118
119KTextEditor::Cursor Marks::getMarkPosition(const QChar &mark) const
120{
121 if (m_marks.contains(mark)) {
122 KTextEditor::MovingCursor *c = m_marks.value(mark);
123 return KTextEditor::Cursor(c->line(), c->column());
124 }
125
127}
128
130{
131 Q_UNUSED(doc)
132
133 if (mark.type != KTextEditor::Document::Bookmark || m_settingMark) {
134 return;
135 }
136
138 const auto keys = m_marks.keys();
139 for (QChar markerChar : keys) {
140 if (m_marks.value(markerChar)->line() == mark.line) {
141 m_marks.remove(markerChar);
142 }
143 }
144 } else if (action == KTextEditor::Document::MarkAdded) {
145 bool freeMarkerCharFound = false;
146
147 for (const QChar &markerChar : UserMarks) {
148 if (!m_marks.value(markerChar)) {
149 setMark(markerChar, KTextEditor::Cursor(mark.line, 0));
150 freeMarkerCharFound = true;
151 break;
152 }
153 }
154
155 // only show error when we are in Vi input mode
156 if (!freeMarkerCharFound && m_inputModeManager->view()->viewInputMode() == KTextEditor::View::ViInputMode) {
157 m_inputModeManager->getViNormalMode()->error(i18n("There are no more chars for the next bookmark."));
158 }
159 }
160}
161
162void Marks::syncViMarksAndBookmarks()
163{
164 const QHash<int, KTextEditor::Mark *> &marks = m_doc->marks();
165
166 // Each bookmark should have a vi mark on the same line.
167 for (auto mark : marks) {
169 continue;
170 }
171
172 bool thereIsViMarkForThisLine = false;
173 for (auto cursor : std::as_const(m_marks)) {
174 if (cursor->line() == mark->line) {
175 thereIsViMarkForThisLine = true;
176 break;
177 }
178 }
179
180 if (thereIsViMarkForThisLine) {
181 continue;
182 }
183
184 for (const QChar &markerChar : UserMarks) {
185 if (!m_marks.value(markerChar)) {
186 setMark(markerChar, KTextEditor::Cursor(mark->line, 0));
187 break;
188 }
189 }
190 }
191
192 // For showable vi mark a line should be bookmarked.
193 const auto keys = m_marks.keys();
194 for (QChar markChar : keys) {
195 if (!isShowable(markChar)) {
196 continue;
197 }
198
199 bool thereIsKateMarkForThisLine = false;
200 for (auto mark : marks) {
202 continue;
203 }
204
205 if (m_marks.value(markChar)->line() == mark->line) {
206 thereIsKateMarkForThisLine = true;
207 break;
208 }
209 }
210
211 if (!thereIsKateMarkForThisLine) {
212 m_doc->addMark(m_marks.value(markChar)->line(), KTextEditor::Document::markType01);
213 }
214 }
215}
216
217QString Marks::getMarksOnTheLine(int line) const
218{
219 QString res;
220 const auto keys = m_marks.keys();
221 for (QChar markerChar : keys) {
222 if (m_marks.value(markerChar)->line() == line) {
223 res += markerChar + QLatin1Char(':') + QString::number(m_marks.value(markerChar)->column()) + QLatin1Char(' ');
224 }
225 }
226
227 return res;
228}
229
230bool Marks::isShowable(const QChar &mark)
231{
232 return FirstUserMark <= mark && mark <= LastUserMark;
233}
234
235void Marks::setStartEditYanked(const KTextEditor::Cursor pos)
236{
237 setMark(BeginEditYanked, pos);
238}
239
240void Marks::setFinishEditYanked(const KTextEditor::Cursor pos)
241{
242 setMark(EndEditYanked, pos);
243}
244
245void Marks::setLastChange(const KTextEditor::Cursor pos)
246{
247 setMark(LastChange, pos);
248}
249
250void Marks::setInsertStopped(const KTextEditor::Cursor pos)
251{
252 setMark(InsertStopped, pos);
253}
254
255void Marks::setSelectionStart(const KTextEditor::Cursor pos)
256{
257 setMark(SelectionBegin, pos);
258}
259
260void Marks::setSelectionFinish(const KTextEditor::Cursor pos)
261{
262 setMark(SelectionEnd, pos);
263}
264
265void Marks::setUserMark(const QChar &mark, const KTextEditor::Cursor pos)
266{
267 Q_ASSERT(FirstUserMark <= mark && mark <= LastUserMark);
268 setMark(mark, pos);
269}
270
271KTextEditor::Cursor Marks::getStartEditYanked() const
272{
273 return getMarkPosition(BeginEditYanked);
274}
275
276KTextEditor::Cursor Marks::getFinishEditYanked() const
277{
278 return getMarkPosition(EndEditYanked);
279}
280
281KTextEditor::Cursor Marks::getSelectionStart() const
282{
283 return getMarkPosition(SelectionBegin);
284}
285
286KTextEditor::Cursor Marks::getSelectionFinish() const
287{
288 return getMarkPosition(SelectionEnd);
289}
290
291KTextEditor::Cursor Marks::getLastChange() const
292{
293 return getMarkPosition(LastChange);
294}
295
296KTextEditor::Cursor Marks::getInsertStopped() const
297{
298 return getMarkPosition(InsertStopped);
299}
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 T & at(int i) const const
int size() const const
bool contains(const Key &key) const const
QMap::iterator insert(const Key &key, const T &value)
QList< Key > keys() const const
int remove(const Key &key)
const T value(const Key &key, const T &defaultValue) const const
QString number(int n, int base)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Sat Feb 24 2024 20:00:58 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.