File indexing completed on 2024-04-14 03:55:27
0001 /* 0002 SPDX-FileCopyrightText: 2011 Dominik Haumann <dhaumann@kde.org> 0003 SPDX-FileCopyrightText: 2009-2010 Bernhard Beschow <bbeschow@cs.tu-berlin.de> 0004 SPDX-FileCopyrightText: 2002 John Firebaugh <jfirebaugh@kde.org> 0005 SPDX-FileCopyrightText: 2001 Christoph Cullmann <cullmann@kde.org> 0006 SPDX-FileCopyrightText: 2001 Joseph Wenninger <jowenn@kde.org> 0007 SPDX-FileCopyrightText: 2023 Waqar Ahmed <waqar.17a@gmail.com> 0008 0009 SPDX-License-Identifier: LGPL-2.0-or-later 0010 */ 0011 0012 #include "kateundo.h" 0013 0014 #include "katebuffer.h" 0015 #include "katedocument.h" 0016 #include "kateundomanager.h" 0017 #include "kateview.h" 0018 0019 #include <ktexteditor/cursor.h> 0020 #include <ktexteditor/view.h> 0021 0022 KateUndoGroup::KateUndoGroup(const KTextEditor::Cursor cursorPosition, 0023 KTextEditor::Range selection, 0024 const QList<KTextEditor::ViewPrivate::PlainSecondaryCursor> &secondary) 0025 : m_undoSelection(selection) 0026 , m_redoSelection(-1, -1, -1, -1) 0027 , m_undoCursor(cursorPosition) 0028 , m_undoSecondaryCursors(secondary) 0029 , m_redoCursor(-1, -1) 0030 { 0031 } 0032 0033 void KateUndoGroup::undo(KateUndoManager *manager, KTextEditor::ViewPrivate *view) 0034 { 0035 if (m_items.empty()) { 0036 return; 0037 } 0038 0039 manager->startUndo(); 0040 0041 auto doc = manager->document(); 0042 auto updateDocLine = [doc](const UndoItem &item) { 0043 Kate::TextLine tl = doc->plainKateTextLine(item.line); 0044 tl.markAsModified(item.lineModFlags.testFlag(UndoItem::UndoLine1Modified)); 0045 tl.markAsSavedOnDisk(item.lineModFlags.testFlag(UndoItem::UndoLine1Saved)); 0046 doc->buffer().setLineMetaData(item.line, tl); 0047 }; 0048 0049 for (auto rit = m_items.rbegin(); rit != m_items.rend(); ++rit) { 0050 auto &item = *rit; 0051 switch (item.type) { 0052 case UndoItem::editInsertText: 0053 doc->editRemoveText(item.line, item.col, item.text.size()); 0054 updateDocLine(item); 0055 break; 0056 case UndoItem::editRemoveText: 0057 doc->editInsertText(item.line, item.col, item.text); 0058 updateDocLine(item); 0059 break; 0060 case UndoItem::editWrapLine: 0061 doc->editUnWrapLine(item.line, item.newLine, item.len); 0062 updateDocLine(item); 0063 break; 0064 case UndoItem::editUnWrapLine: { 0065 doc->editWrapLine(item.line, item.col, item.removeLine); 0066 updateDocLine(item); 0067 0068 auto next = doc->plainKateTextLine(item.line + 1); 0069 next.markAsModified(item.lineModFlags.testFlag(UndoItem::UndoLine2Modified)); 0070 next.markAsSavedOnDisk(item.lineModFlags.testFlag(UndoItem::UndoLine2Saved)); 0071 doc->buffer().setLineMetaData(item.line + 1, next); 0072 } break; 0073 case UndoItem::editInsertLine: 0074 doc->editRemoveLine(item.line); 0075 break; 0076 case UndoItem::editRemoveLine: 0077 doc->editInsertLine(item.line, item.text); 0078 updateDocLine(item); 0079 break; 0080 case UndoItem::editMarkLineAutoWrapped: 0081 doc->editMarkLineAutoWrapped(item.line, item.autowrapped); 0082 break; 0083 case UndoItem::editInvalid: 0084 break; 0085 } 0086 } 0087 0088 if (view != nullptr) { 0089 if (m_undoSelection.isValid()) { 0090 view->setSelection(m_undoSelection); 0091 } else { 0092 view->removeSelection(); 0093 } 0094 view->clearSecondaryCursors(); 0095 view->addSecondaryCursorsWithSelection(m_undoSecondaryCursors); 0096 0097 if (m_undoCursor.isValid()) { 0098 view->setCursorPosition(m_undoCursor); 0099 } 0100 } 0101 0102 manager->endUndo(); 0103 } 0104 0105 void KateUndoGroup::redo(KateUndoManager *manager, KTextEditor::ViewPrivate *view) 0106 { 0107 if (m_items.empty()) { 0108 return; 0109 } 0110 0111 manager->startUndo(); 0112 0113 auto doc = manager->document(); 0114 auto updateDocLine = [doc](const UndoItem &item) { 0115 Kate::TextLine tl = doc->plainKateTextLine(item.line); 0116 tl.markAsModified(item.lineModFlags.testFlag(UndoItem::RedoLine1Modified)); 0117 tl.markAsSavedOnDisk(item.lineModFlags.testFlag(UndoItem::RedoLine1Saved)); 0118 doc->buffer().setLineMetaData(item.line, tl); 0119 }; 0120 0121 for (auto &item : m_items) { 0122 switch (item.type) { 0123 case UndoItem::editInsertText: 0124 doc->editInsertText(item.line, item.col, item.text); 0125 updateDocLine(item); 0126 break; 0127 case UndoItem::editRemoveText: 0128 doc->editRemoveText(item.line, item.col, item.text.size()); 0129 updateDocLine(item); 0130 break; 0131 case UndoItem::editWrapLine: { 0132 doc->editWrapLine(item.line, item.col, item.newLine); 0133 updateDocLine(item); 0134 0135 Kate::TextLine next = doc->plainKateTextLine(item.line + 1); 0136 next.markAsModified(item.lineModFlags.testFlag(UndoItem::RedoLine2Modified)); 0137 next.markAsSavedOnDisk(item.lineModFlags.testFlag(UndoItem::RedoLine2Saved)); 0138 doc->buffer().setLineMetaData(item.line + 1, next); 0139 } break; 0140 case UndoItem::editUnWrapLine: 0141 doc->editUnWrapLine(item.line, item.removeLine, item.len); 0142 updateDocLine(item); 0143 break; 0144 case UndoItem::editInsertLine: 0145 doc->editInsertLine(item.line, item.text); 0146 updateDocLine(item); 0147 break; 0148 case UndoItem::editRemoveLine: 0149 doc->editRemoveLine(item.line); 0150 break; 0151 case UndoItem::editMarkLineAutoWrapped: 0152 doc->editMarkLineAutoWrapped(item.line, item.autowrapped); 0153 break; 0154 case UndoItem::editInvalid: 0155 break; 0156 } 0157 } 0158 0159 if (view != nullptr) { 0160 if (m_redoSelection.isValid()) { 0161 view->setSelection(m_redoSelection); 0162 } else { 0163 view->removeSelection(); 0164 } 0165 view->clearSecondaryCursors(); 0166 view->addSecondaryCursorsWithSelection(m_redoSecondaryCursors); 0167 0168 if (m_redoCursor.isValid()) { 0169 view->setCursorPosition(m_redoCursor); 0170 } 0171 } 0172 0173 manager->endUndo(); 0174 } 0175 0176 void KateUndoGroup::editEnd(const KTextEditor::Cursor cursorPosition, 0177 KTextEditor::Range selectionRange, 0178 const QList<KTextEditor::ViewPrivate::PlainSecondaryCursor> &secondaryCursors) 0179 { 0180 m_redoCursor = cursorPosition; 0181 m_redoSecondaryCursors = secondaryCursors; 0182 m_redoSelection = selectionRange; 0183 } 0184 0185 static bool mergeUndoItems(UndoItem &base, const UndoItem &u) 0186 { 0187 if (base.type == UndoItem::editInsertText && u.type == UndoItem::editWrapLine) { 0188 // merge insert text full line + wrap line 0189 if (base.col == 0 && base.line == u.line && base.col + base.text.length() == u.col && u.newLine) { 0190 base.type = UndoItem::editInsertLine; 0191 base.lineModFlags.setFlag(UndoItem::RedoLine1Modified); 0192 return true; 0193 } 0194 } 0195 0196 if (base.type == UndoItem::editRemoveText && base.type == u.type) { 0197 if (base.line == u.line && base.col == (u.col + u.text.size())) { 0198 base.text.prepend(u.text); 0199 base.col = u.col; 0200 return true; 0201 } 0202 } 0203 0204 if (base.type == UndoItem::editInsertText && base.type == u.type) { 0205 if (base.line == u.line && (base.col + base.text.size()) == u.col) { 0206 base.text += u.text; 0207 return true; 0208 } 0209 } 0210 0211 return false; 0212 } 0213 0214 void KateUndoGroup::addItem(UndoItem u) 0215 { 0216 // try to merge, do that only for equal types, inside mergeWith we do hard casts 0217 if (!m_items.empty() && mergeUndoItems(m_items.back(), u)) { 0218 return; 0219 } 0220 0221 // default: just add new item unchanged 0222 m_items.push_back(std::move(u)); 0223 } 0224 0225 bool KateUndoGroup::merge(KateUndoGroup *newGroup, bool complex) 0226 { 0227 if (m_safePoint) { 0228 return false; 0229 } 0230 0231 if (newGroup->isOnlyType(singleType()) || complex) { 0232 // Take all of its items first -> last 0233 for (auto &item : newGroup->m_items) { 0234 addItem(item); 0235 } 0236 newGroup->m_items.clear(); 0237 0238 if (newGroup->m_safePoint) { 0239 safePoint(); 0240 } 0241 0242 m_redoCursor = newGroup->m_redoCursor; 0243 m_redoSecondaryCursors = newGroup->m_redoSecondaryCursors; 0244 m_redoSelection = newGroup->m_redoSelection; 0245 // m_redoSelections = newGroup->m_redoSelections; 0246 0247 return true; 0248 } 0249 0250 return false; 0251 } 0252 0253 void KateUndoGroup::safePoint(bool safePoint) 0254 { 0255 m_safePoint = safePoint; 0256 } 0257 0258 void KateUndoGroup::flagSavedAsModified() 0259 { 0260 for (UndoItem &item : m_items) { 0261 if (item.lineModFlags.testFlag(UndoItem::UndoLine1Saved)) { 0262 item.lineModFlags.setFlag(UndoItem::UndoLine1Saved, false); 0263 item.lineModFlags.setFlag(UndoItem::UndoLine1Modified, true); 0264 } 0265 0266 if (item.lineModFlags.testFlag(UndoItem::UndoLine2Saved)) { 0267 item.lineModFlags.setFlag(UndoItem::UndoLine2Saved, false); 0268 item.lineModFlags.setFlag(UndoItem::UndoLine2Modified, true); 0269 } 0270 0271 if (item.lineModFlags.testFlag(UndoItem::RedoLine1Saved)) { 0272 item.lineModFlags.setFlag(UndoItem::RedoLine1Saved, false); 0273 item.lineModFlags.setFlag(UndoItem::RedoLine1Modified, true); 0274 } 0275 0276 if (item.lineModFlags.testFlag(UndoItem::RedoLine2Saved)) { 0277 item.lineModFlags.setFlag(UndoItem::RedoLine2Saved, false); 0278 item.lineModFlags.setFlag(UndoItem::RedoLine2Modified, true); 0279 } 0280 } 0281 } 0282 0283 static void updateUndoSavedOnDiskFlag(UndoItem &item, QBitArray &lines) 0284 { 0285 const int line = item.line; 0286 if (line >= lines.size()) { 0287 lines.resize(line + 1); 0288 } 0289 0290 const bool wasBitSet = lines.testBit(line); 0291 if (!wasBitSet) { 0292 lines.setBit(line); 0293 } 0294 0295 auto &lineFlags = item.lineModFlags; 0296 0297 switch (item.type) { 0298 case UndoItem::editInsertText: 0299 case UndoItem::editRemoveText: 0300 case UndoItem::editRemoveLine: 0301 if (!wasBitSet) { 0302 lineFlags.setFlag(UndoItem::UndoLine1Modified, false); 0303 lineFlags.setFlag(UndoItem::UndoLine1Saved, true); 0304 } 0305 break; 0306 case UndoItem::editWrapLine: 0307 if (lineFlags.testFlag(UndoItem::UndoLine1Modified) && !wasBitSet) { 0308 lineFlags.setFlag(UndoItem::UndoLine1Modified, false); 0309 lineFlags.setFlag(UndoItem::UndoLine1Saved, true); 0310 } 0311 break; 0312 case UndoItem::editUnWrapLine: 0313 if (line + 1 >= lines.size()) { 0314 lines.resize(line + 2); 0315 } 0316 if (lineFlags.testFlag(UndoItem::UndoLine1Modified) && !wasBitSet) { 0317 lineFlags.setFlag(UndoItem::UndoLine1Modified, false); 0318 lineFlags.setFlag(UndoItem::UndoLine1Saved, true); 0319 } 0320 0321 if (lineFlags.testFlag(UndoItem::UndoLine2Modified) && !lines.testBit(line + 1)) { 0322 lines.setBit(line + 1); 0323 0324 lineFlags.setFlag(UndoItem::UndoLine2Modified, false); 0325 lineFlags.setFlag(UndoItem::UndoLine2Saved, true); 0326 } 0327 break; 0328 case UndoItem::editInsertLine: 0329 case UndoItem::editMarkLineAutoWrapped: 0330 case UndoItem::editInvalid: 0331 break; 0332 } 0333 } 0334 0335 void KateUndoGroup::markUndoAsSaved(QBitArray &lines) 0336 { 0337 for (auto rit = m_items.rbegin(); rit != m_items.rend(); ++rit) { 0338 updateUndoSavedOnDiskFlag(*rit, lines); 0339 } 0340 } 0341 0342 static void updateRedoSavedOnDiskFlag(UndoItem &item, QBitArray &lines) 0343 { 0344 const int line = item.line; 0345 if (line >= lines.size()) { 0346 lines.resize(line + 1); 0347 } 0348 0349 const bool wasBitSet = lines.testBit(line); 0350 if (!wasBitSet) { 0351 lines.setBit(line); 0352 } 0353 auto &lineFlags = item.lineModFlags; 0354 0355 switch (item.type) { 0356 case UndoItem::editInsertText: 0357 case UndoItem::editRemoveText: 0358 case UndoItem::editInsertLine: 0359 lineFlags.setFlag(UndoItem::RedoLine1Modified, false); 0360 lineFlags.setFlag(UndoItem::RedoLine1Saved, true); 0361 break; 0362 case UndoItem::editUnWrapLine: 0363 if (lineFlags.testFlag(UndoItem::RedoLine1Modified) && !wasBitSet) { 0364 lineFlags.setFlag(UndoItem::RedoLine1Modified, false); 0365 lineFlags.setFlag(UndoItem::RedoLine1Saved, true); 0366 } 0367 break; 0368 case UndoItem::editWrapLine: 0369 if (line + 1 >= lines.size()) { 0370 lines.resize(line + 2); 0371 } 0372 0373 if (lineFlags.testFlag(UndoItem::RedoLine1Modified) && !wasBitSet) { 0374 lineFlags.setFlag(UndoItem::RedoLine1Modified, false); 0375 lineFlags.setFlag(UndoItem::RedoLine1Saved, true); 0376 } 0377 0378 if (lineFlags.testFlag(UndoItem::RedoLine2Modified) && !lines.testBit(line + 1)) { 0379 lineFlags.setFlag(UndoItem::RedoLine2Modified, false); 0380 lineFlags.setFlag(UndoItem::RedoLine2Saved, true); 0381 } 0382 break; 0383 case UndoItem::editRemoveLine: 0384 case UndoItem::editMarkLineAutoWrapped: 0385 case UndoItem::editInvalid: 0386 break; 0387 } 0388 } 0389 0390 void KateUndoGroup::markRedoAsSaved(QBitArray &lines) 0391 { 0392 for (auto rit = m_items.rbegin(); rit != m_items.rend(); ++rit) { 0393 updateRedoSavedOnDiskFlag(*rit, lines); 0394 } 0395 } 0396 0397 UndoItem::UndoType KateUndoGroup::singleType() const 0398 { 0399 UndoItem::UndoType ret = UndoItem::editInvalid; 0400 0401 for (const auto &item : m_items) { 0402 if (ret == UndoItem::editInvalid) { 0403 ret = item.type; 0404 } else if (ret != item.type) { 0405 return UndoItem::editInvalid; 0406 } 0407 } 0408 0409 return ret; 0410 } 0411 0412 bool KateUndoGroup::isOnlyType(UndoItem::UndoType type) const 0413 { 0414 if (type == UndoItem::editInvalid) { 0415 return false; 0416 } 0417 for (const auto &item : m_items) { 0418 if (item.type != type) 0419 return false; 0420 } 0421 return true; 0422 }