File indexing completed on 2024-05-05 12:23:16
0001 /* 0002 SPDX-FileCopyrightText: KDE Developers 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "marks.h" 0008 #include "katedocument.h" 0009 #include "kateview.h" 0010 #include <vimode/inputmodemanager.h> 0011 #include <vimode/modes/normalvimode.h> 0012 0013 #include <KLocalizedString> 0014 0015 using namespace KateVi; 0016 0017 namespace 0018 { 0019 const QChar BeginEditYanked = QLatin1Char('['); 0020 const QChar EndEditYanked = QLatin1Char(']'); 0021 const QChar LastChange = QLatin1Char('.'); 0022 const QChar InsertStopped = QLatin1Char('^'); 0023 const QChar SelectionBegin = QLatin1Char('<'); 0024 const QChar SelectionEnd = QLatin1Char('>'); 0025 const QChar FirstUserMark = QLatin1Char('a'); 0026 const QChar LastUserMark = QLatin1Char('z'); 0027 const QChar BeforeJump = QLatin1Char('\''); 0028 const QChar BeforeJumpAlter = QLatin1Char('`'); 0029 const QChar UserMarks[] = {QLatin1Char('a'), QLatin1Char('b'), QLatin1Char('c'), QLatin1Char('d'), QLatin1Char('e'), QLatin1Char('f'), QLatin1Char('g'), 0030 QLatin1Char('h'), QLatin1Char('i'), QLatin1Char('j'), QLatin1Char('k'), QLatin1Char('l'), QLatin1Char('m'), QLatin1Char('n'), 0031 QLatin1Char('o'), QLatin1Char('p'), QLatin1Char('q'), QLatin1Char('r'), QLatin1Char('s'), QLatin1Char('t'), QLatin1Char('u'), 0032 QLatin1Char('v'), QLatin1Char('w'), QLatin1Char('x'), QLatin1Char('y'), QLatin1Char('z')}; 0033 } 0034 0035 Marks::Marks(InputModeManager *imm) 0036 : m_inputModeManager(imm) 0037 , m_doc(imm->view()->doc()) 0038 , m_settingMark(false) 0039 { 0040 connect(m_doc, &KTextEditor::DocumentPrivate::markChanged, this, &Marks::markChanged); 0041 } 0042 0043 void Marks::readSessionConfig(const KConfigGroup &config) 0044 { 0045 QStringList marks = config.readEntry("ViMarks", QStringList()); 0046 for (int i = 0; i + 2 < marks.size(); i += 3) { 0047 KTextEditor::Cursor c(marks.at(i + 1).toInt(), marks.at(i + 2).toInt()); 0048 setMark(marks.at(i).at(0), c); 0049 } 0050 0051 syncViMarksAndBookmarks(); 0052 } 0053 0054 void Marks::writeSessionConfig(KConfigGroup &config) const 0055 { 0056 QStringList l; 0057 const auto keys = m_marks.keys(); 0058 for (QChar key : keys) { 0059 l << key << QString::number(m_marks.value(key)->line()) << QString::number(m_marks.value(key)->column()); 0060 } 0061 config.writeEntry("ViMarks", l); 0062 } 0063 0064 void Marks::setMark(const QChar &_mark, const KTextEditor::Cursor pos) 0065 { 0066 // move on insert is type based, this allows to reuse cursors! 0067 // reuse is important for editing intensive things like replace-all 0068 const bool moveoninsert = _mark != BeginEditYanked; 0069 0070 m_settingMark = true; 0071 0072 // ` and ' is the same register (position before jump) 0073 const QChar mark = (_mark == BeforeJumpAlter) ? BeforeJump : _mark; 0074 0075 // if we have already a cursor for this type: adjust it 0076 bool needToAdjustVisibleMark = true; 0077 if (KTextEditor::MovingCursor *oldCursor = m_marks.value(mark)) { 0078 // cleanup mark display only if line changes 0079 needToAdjustVisibleMark = oldCursor->line() != pos.line(); 0080 if (needToAdjustVisibleMark) { 0081 int number_of_marks = 0; 0082 const auto keys = m_marks.keys(); 0083 for (QChar c : keys) { 0084 if (m_marks.value(c)->line() == oldCursor->line()) { 0085 number_of_marks++; 0086 } 0087 } 0088 if (number_of_marks == 1) { 0089 m_doc->removeMark(oldCursor->line(), KTextEditor::MarkInterface::markType01); 0090 } 0091 } 0092 0093 // adjust position 0094 oldCursor->setPosition(pos); 0095 } else { 0096 // if no old mark of that type, create new one 0097 const KTextEditor::MovingCursor::InsertBehavior behavior = 0098 moveoninsert ? KTextEditor::MovingCursor::MoveOnInsert : KTextEditor::MovingCursor::StayOnInsert; 0099 m_marks.insert(mark, m_doc->newMovingCursor(pos, behavior)); 0100 } 0101 0102 // Showing what mark we set, can be skipped if we did not change the line 0103 if (isShowable(mark)) { 0104 if (needToAdjustVisibleMark && !(m_doc->mark(pos.line()) & KTextEditor::MarkInterface::markType01)) { 0105 m_doc->addMark(pos.line(), KTextEditor::MarkInterface::markType01); 0106 } 0107 0108 // only show message for active view 0109 if (m_inputModeManager->view()->viewInputMode() == KTextEditor::View::ViInputMode) { 0110 if (m_doc->activeView() == m_inputModeManager->view()) { 0111 m_inputModeManager->getViNormalMode()->message(i18n("Mark set: %1", mark)); 0112 } 0113 } 0114 } 0115 0116 m_settingMark = false; 0117 } 0118 0119 KTextEditor::Cursor Marks::getMarkPosition(const QChar &mark) const 0120 { 0121 if (m_marks.contains(mark)) { 0122 KTextEditor::MovingCursor *c = m_marks.value(mark); 0123 return KTextEditor::Cursor(c->line(), c->column()); 0124 } 0125 0126 return KTextEditor::Cursor::invalid(); 0127 } 0128 0129 void Marks::markChanged(KTextEditor::Document *doc, KTextEditor::Mark mark, KTextEditor::MarkInterface::MarkChangeAction action) 0130 { 0131 Q_UNUSED(doc) 0132 0133 if (mark.type != KTextEditor::MarkInterface::Bookmark || m_settingMark) { 0134 return; 0135 } 0136 0137 if (action == KTextEditor::MarkInterface::MarkRemoved) { 0138 const auto keys = m_marks.keys(); 0139 for (QChar markerChar : keys) { 0140 if (m_marks.value(markerChar)->line() == mark.line) { 0141 m_marks.remove(markerChar); 0142 } 0143 } 0144 } else if (action == KTextEditor::MarkInterface::MarkAdded) { 0145 bool freeMarkerCharFound = false; 0146 0147 for (const QChar &markerChar : UserMarks) { 0148 if (!m_marks.value(markerChar)) { 0149 setMark(markerChar, KTextEditor::Cursor(mark.line, 0)); 0150 freeMarkerCharFound = true; 0151 break; 0152 } 0153 } 0154 0155 // only show error when we are in Vi input mode 0156 if (!freeMarkerCharFound && m_inputModeManager->view()->viewInputMode() == KTextEditor::View::ViInputMode) { 0157 m_inputModeManager->getViNormalMode()->error(i18n("There are no more chars for the next bookmark.")); 0158 } 0159 } 0160 } 0161 0162 void Marks::syncViMarksAndBookmarks() 0163 { 0164 const QHash<int, KTextEditor::Mark *> &marks = m_doc->marks(); 0165 0166 // Each bookmark should have a vi mark on the same line. 0167 for (auto mark : marks) { 0168 if (!(mark->type & KTextEditor::MarkInterface::markType01)) { 0169 continue; 0170 } 0171 0172 bool thereIsViMarkForThisLine = false; 0173 for (auto cursor : std::as_const(m_marks)) { 0174 if (cursor->line() == mark->line) { 0175 thereIsViMarkForThisLine = true; 0176 break; 0177 } 0178 } 0179 0180 if (thereIsViMarkForThisLine) { 0181 continue; 0182 } 0183 0184 for (const QChar &markerChar : UserMarks) { 0185 if (!m_marks.value(markerChar)) { 0186 setMark(markerChar, KTextEditor::Cursor(mark->line, 0)); 0187 break; 0188 } 0189 } 0190 } 0191 0192 // For showable vi mark a line should be bookmarked. 0193 const auto keys = m_marks.keys(); 0194 for (QChar markChar : keys) { 0195 if (!isShowable(markChar)) { 0196 continue; 0197 } 0198 0199 bool thereIsKateMarkForThisLine = false; 0200 for (auto mark : marks) { 0201 if (!(mark->type & KTextEditor::MarkInterface::markType01)) { 0202 continue; 0203 } 0204 0205 if (m_marks.value(markChar)->line() == mark->line) { 0206 thereIsKateMarkForThisLine = true; 0207 break; 0208 } 0209 } 0210 0211 if (!thereIsKateMarkForThisLine) { 0212 m_doc->addMark(m_marks.value(markChar)->line(), KTextEditor::MarkInterface::markType01); 0213 } 0214 } 0215 } 0216 0217 QString Marks::getMarksOnTheLine(int line) const 0218 { 0219 QString res; 0220 const auto keys = m_marks.keys(); 0221 for (QChar markerChar : keys) { 0222 if (m_marks.value(markerChar)->line() == line) { 0223 res += markerChar + QLatin1Char(':') + QString::number(m_marks.value(markerChar)->column()) + QLatin1Char(' '); 0224 } 0225 } 0226 0227 return res; 0228 } 0229 0230 bool Marks::isShowable(const QChar &mark) 0231 { 0232 return FirstUserMark <= mark && mark <= LastUserMark; 0233 } 0234 0235 void Marks::setStartEditYanked(const KTextEditor::Cursor pos) 0236 { 0237 setMark(BeginEditYanked, pos); 0238 } 0239 0240 void Marks::setFinishEditYanked(const KTextEditor::Cursor pos) 0241 { 0242 setMark(EndEditYanked, pos); 0243 } 0244 0245 void Marks::setLastChange(const KTextEditor::Cursor pos) 0246 { 0247 setMark(LastChange, pos); 0248 } 0249 0250 void Marks::setInsertStopped(const KTextEditor::Cursor pos) 0251 { 0252 setMark(InsertStopped, pos); 0253 } 0254 0255 void Marks::setSelectionStart(const KTextEditor::Cursor pos) 0256 { 0257 setMark(SelectionBegin, pos); 0258 } 0259 0260 void Marks::setSelectionFinish(const KTextEditor::Cursor pos) 0261 { 0262 setMark(SelectionEnd, pos); 0263 } 0264 0265 void Marks::setUserMark(const QChar &mark, const KTextEditor::Cursor pos) 0266 { 0267 Q_ASSERT(FirstUserMark <= mark && mark <= LastUserMark); 0268 setMark(mark, pos); 0269 } 0270 0271 KTextEditor::Cursor Marks::getStartEditYanked() const 0272 { 0273 return getMarkPosition(BeginEditYanked); 0274 } 0275 0276 KTextEditor::Cursor Marks::getFinishEditYanked() const 0277 { 0278 return getMarkPosition(EndEditYanked); 0279 } 0280 0281 KTextEditor::Cursor Marks::getSelectionStart() const 0282 { 0283 return getMarkPosition(SelectionBegin); 0284 } 0285 0286 KTextEditor::Cursor Marks::getSelectionFinish() const 0287 { 0288 return getMarkPosition(SelectionEnd); 0289 } 0290 0291 KTextEditor::Cursor Marks::getLastChange() const 0292 { 0293 return getMarkPosition(LastChange); 0294 } 0295 0296 KTextEditor::Cursor Marks::getInsertStopped() const 0297 { 0298 return getMarkPosition(InsertStopped); 0299 } 0300 0301 #include "moc_marks.cpp"