File indexing completed on 2024-05-12 04:37:42
0001 /* 0002 SPDX-FileCopyrightText: 2010 David Nolden <david.nolden.kdevelop@art-master.de> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #ifndef KDEVPLATFORM_DOCUMENTCHANGETRACKER_H 0008 #define KDEVPLATFORM_DOCUMENTCHANGETRACKER_H 0009 0010 #include <language/languageexport.h> 0011 #include <QExplicitlySharedDataPointer> 0012 #include <QPointer> 0013 #include <QPair> 0014 #include <language/editor/rangeinrevision.h> 0015 #include <serialization/indexedstring.h> 0016 0017 #include <KTextEditor/MovingRange> 0018 0019 namespace KTextEditor { 0020 class Document; 0021 class MovingRange; 0022 class MovingInterface; 0023 } 0024 0025 namespace KDevelop { 0026 class DocumentChangeTracker; 0027 /** 0028 * These objects belongs to the foreground, and thus can only be accessed from background threads if the foreground lock is held. 0029 * */ 0030 0031 class RevisionLockerAndClearerPrivate; 0032 0033 /** 0034 * Helper class that locks a revision, and clears it on its destruction within the foreground thread. 0035 * Just delete it using deleteLater(). 0036 * */ 0037 class KDEVPLATFORMLANGUAGE_EXPORT RevisionLockerAndClearer 0038 : public QSharedData 0039 { 0040 public: 0041 using Ptr = QExplicitlySharedDataPointer<RevisionLockerAndClearer>; 0042 0043 ~RevisionLockerAndClearer(); 0044 0045 /** 0046 * Returns the revision number 0047 * */ 0048 qint64 revision() const; 0049 0050 /** 0051 * Whether the revision is still being held. It may have been lost due to document-reloads, 0052 * in which case the revision must not be used. 0053 * */ 0054 bool valid() const; 0055 /** 0056 * Transform a range from this document revision to the given @p to. 0057 * */ 0058 RangeInRevision transformToRevision(const RangeInRevision& range, const Ptr& to) const; 0059 0060 /** 0061 * Transform a cursor from this document revision to the given @p to. 0062 * If a zero target revision is given, the transformation is done to the current document revision. 0063 * */ 0064 CursorInRevision transformToRevision(const CursorInRevision& cursor, const Ptr& to, 0065 KTextEditor::MovingCursor::InsertBehavior behavior = 0066 KTextEditor::MovingCursor::StayOnInsert) const; 0067 0068 /** 0069 * Transforms the given range from this revision into the current revision. 0070 */ 0071 KTextEditor::Range transformToCurrentRevision(const RangeInRevision& range) const; 0072 0073 /** 0074 * Transforms the given cursor from this revision into the current revision. 0075 */ 0076 KTextEditor::Cursor transformToCurrentRevision(const CursorInRevision& cursor, 0077 KTextEditor::MovingCursor::InsertBehavior behavior = 0078 KTextEditor::MovingCursor::StayOnInsert) const; 0079 0080 /** 0081 * Transform ranges from the given document revision @p from to the this one. 0082 * If a zero @p from revision is given, the transformation is done from the current document revision. 0083 * */ 0084 RangeInRevision transformFromRevision(const RangeInRevision& range, const Ptr& from = Ptr()) const; 0085 /** 0086 * Transform ranges from the given document revision @p from to the this one. 0087 * If a zero @p from revision is given, the transformation is done from the current document revision. 0088 * */ 0089 CursorInRevision transformFromRevision(const CursorInRevision& cursor, 0090 const Ptr& from = Ptr(), 0091 KTextEditor::MovingCursor::InsertBehavior behavior = 0092 KTextEditor::MovingCursor::StayOnInsert) const; 0093 0094 /** 0095 * Transforms the given range from the current revision into this revision. 0096 */ 0097 RangeInRevision transformFromCurrentRevision(const KTextEditor::Range& range) const; 0098 0099 /** 0100 * Transforms the given cursor from the current revision into this revision. 0101 */ 0102 CursorInRevision transformFromCurrentRevision(const KTextEditor::Cursor& cursor, 0103 KTextEditor::MovingCursor::InsertBehavior behavior = 0104 KTextEditor::MovingCursor::StayOnInsert) const; 0105 0106 private: 0107 friend class DocumentChangeTracker; 0108 0109 RevisionLockerAndClearerPrivate* m_p; 0110 }; 0111 0112 using RevisionReference = RevisionLockerAndClearer::Ptr; 0113 0114 class KDEVPLATFORMLANGUAGE_EXPORT DocumentChangeTracker 0115 : public QObject 0116 { 0117 Q_OBJECT 0118 0119 public: 0120 explicit DocumentChangeTracker(KTextEditor::Document* document); 0121 ~DocumentChangeTracker() override; 0122 0123 /** 0124 * Completions of the users current edits that are supposed to complete 0125 * not-yet-finished statements, like for example for-loops for parsing. 0126 * */ 0127 virtual QList<QPair<KTextEditor::Range, QString>> completions() const; 0128 0129 /** 0130 * Resets the tracking to the current revision. 0131 * */ 0132 virtual void reset(); 0133 0134 /** 0135 * Returns the document revision at which reset() was called last. 0136 * 0137 * The revision is being locked by the tracker in MovingInterface, 0138 * it will be unlocked as soon as reset() is called, so if you want to use 0139 * the revision afterwards, you have to lock it before calling reset. 0140 * 0141 * zero is returned if the revisions were invalidated after the last call. 0142 * */ 0143 RevisionReference revisionAtLastReset() const; 0144 0145 /** 0146 * Returns the current revision (which is not locked by the tracker) 0147 * */ 0148 RevisionReference currentRevision(); 0149 0150 /** 0151 * Whether the changes that happened since the last reset are significant enough to require an update 0152 * */ 0153 virtual bool needUpdate() const; 0154 0155 /** 0156 * Returns the tracked document 0157 **/ 0158 KTextEditor::Document* document() const; 0159 0160 KTextEditor::MovingInterface* documentMovingInterface() const; 0161 0162 /** 0163 * Returns the revision object which locks the revision representing the on-disk state. 0164 * Returns a zero object if the file is not on disk. 0165 * */ 0166 RevisionReference diskRevision() const; 0167 0168 /** 0169 * Returns whether the given revision is being current held, so that it can be used 0170 * for transformations in MovingInterface 0171 * */ 0172 bool holdingRevision(qint64 revision) const; 0173 0174 /** 0175 * Use this function to acquire a revision. As long as the returned object is stored somewhere, 0176 * the revision can be used for transformations in MovingInterface, and especially for 0177 * DocumentChangeTracker::transformBetweenRevisions. 0178 * 0179 * Returns a zero revision object if the revision could not be acquired (it wasn't held). 0180 * */ 0181 RevisionReference acquireRevision(qint64 revision); 0182 0183 /** 0184 * Safely maps the given range between the two given revisions. 0185 * The mapping is only done if both the from- and to- revision are held, 0186 * else the original range is returned. 0187 * 0188 * @warning: Make sure that you actually hold the referenced revisions, else no transformation will be done. 0189 * @note It is much less error-prone to use RevisionReference->transformToRevision() and RevisionReference->transformFromRevision() directly. 0190 * */ 0191 RangeInRevision transformBetweenRevisions(RangeInRevision range, qint64 fromRevision, qint64 toRevision) const; 0192 CursorInRevision transformBetweenRevisions(CursorInRevision cursor, qint64 fromRevision, qint64 toRevision, 0193 KTextEditor::MovingCursor::InsertBehavior behavior = 0194 KTextEditor::MovingCursor::StayOnInsert) const; 0195 0196 KTextEditor::Range transformToCurrentRevision(RangeInRevision range, qint64 fromRevision) const; 0197 KTextEditor::Cursor transformToCurrentRevision(CursorInRevision cursor, qint64 fromRevision, 0198 KTextEditor::MovingCursor::InsertBehavior behavior = 0199 KTextEditor::MovingCursor::StayOnInsert) const; 0200 0201 /// Transform the range from the current revision into the given one 0202 RangeInRevision transformToRevision(KTextEditor::Range range, qint64 toRevision) const; 0203 /// Transform the cursor from the current revision into the given one 0204 CursorInRevision transformToRevision(KTextEditor::Cursor cursor, qint64 toRevision, 0205 KTextEditor::MovingCursor::InsertBehavior behavior = 0206 KTextEditor::MovingCursor::StayOnInsert) const; 0207 0208 protected: 0209 RevisionReference m_revisionAtLastReset; 0210 bool m_needUpdate; 0211 QString m_currentCleanedInsertion; 0212 KTextEditor::Cursor m_lastInsertionPosition; 0213 KTextEditor::MovingRange* m_changedRange; 0214 0215 KTextEditor::Document* m_document; 0216 KTextEditor::MovingInterface* m_moving; 0217 KDevelop::IndexedString m_url; 0218 0219 void updateChangedRange(int delay); 0220 int recommendedDelay(KTextEditor::Document* doc, const KTextEditor::Range& range, const QString& text, 0221 bool removal); 0222 0223 public Q_SLOTS: 0224 void textInserted(KTextEditor::Document* document, const KTextEditor::Cursor& position, const QString& inserted); 0225 void textRemoved(KTextEditor::Document* document, const KTextEditor::Range& range, const QString& oldText); 0226 void lineWrapped(KTextEditor::Document* document, const KTextEditor::Cursor& position); 0227 void lineUnwrapped(KTextEditor::Document* document, int line); 0228 0229 void documentDestroyed(QObject*); 0230 void aboutToInvalidateMovingInterfaceContent (KTextEditor::Document* document); 0231 void documentSavedOrUploaded(KTextEditor::Document*, bool); 0232 0233 private: 0234 bool checkMergeTokens(const KTextEditor::Range& range); 0235 0236 friend class RevisionLockerAndClearerPrivate; 0237 void lockRevision(qint64 revision); 0238 void unlockRevision(qint64 revision); 0239 0240 QMap<qint64, int> m_revisionLocks; 0241 }; 0242 } 0243 #endif