Changeset View
Changeset View
Standalone View
Standalone View
language/assistant/renameassistant.cpp
Show First 20 Lines • Show All 42 Lines • ▼ Show 20 Line(s) | |||||
43 | 43 | | |||
44 | namespace { | 44 | namespace { | ||
45 | 45 | | |||
46 | bool rangesConnect(const KTextEditor::Range& firstRange, const KTextEditor::Range& secondRange) | 46 | bool rangesConnect(const KTextEditor::Range& firstRange, const KTextEditor::Range& secondRange) | ||
47 | { | 47 | { | ||
48 | return !firstRange.intersect(secondRange + KTextEditor::Range(0, -1, 0, +1)).isEmpty(); | 48 | return !firstRange.intersect(secondRange + KTextEditor::Range(0, -1, 0, +1)).isEmpty(); | ||
49 | } | 49 | } | ||
50 | 50 | | |||
51 | Declaration* getDeclarationForChangedRange(KTextEditor::View* view, const KTextEditor::Range& changed) | 51 | Declaration* getDeclarationForChangedRange(KTextEditor::Document* doc, const KTextEditor::Range& changed) | ||
52 | { | 52 | { | ||
53 | const KTextEditor::Cursor cursor(changed.start()); | 53 | const KTextEditor::Cursor cursor(changed.start()); | ||
54 | Declaration* declaration = DUChainUtils::itemUnderCursor(view->document()->url(), cursor); | 54 | Declaration* declaration = DUChainUtils::itemUnderCursor(doc->url(), cursor); | ||
55 | 55 | | |||
56 | //If it's null we could be appending, but there's a case where appending gives a wrong decl | 56 | //If it's null we could be appending, but there's a case where appending gives a wrong decl | ||
57 | //and not a null declaration ... "type var(init)", so check for that too | 57 | //and not a null declaration ... "type var(init)", so check for that too | ||
58 | if (!declaration || !rangesConnect(declaration->rangeInCurrentRevision(), changed)) { | 58 | if (!declaration || !rangesConnect(declaration->rangeInCurrentRevision(), changed)) { | ||
59 | declaration = DUChainUtils::itemUnderCursor(view->document()->url(), KTextEditor::Cursor(cursor.line(), cursor.column()-1)); | 59 | declaration = DUChainUtils::itemUnderCursor(doc->url(), KTextEditor::Cursor(cursor.line(), cursor.column()-1)); | ||
60 | } | 60 | } | ||
61 | 61 | | |||
62 | //In this case, we may either not have a decl at the cursor, or we got a decl, but are editing its use. | 62 | //In this case, we may either not have a decl at the cursor, or we got a decl, but are editing its use. | ||
63 | //In either of those cases, give up and return 0 | 63 | //In either of those cases, give up and return 0 | ||
64 | if (!declaration || !rangesConnect(declaration->rangeInCurrentRevision(), changed)) { | 64 | if (!declaration || !rangesConnect(declaration->rangeInCurrentRevision(), changed)) { | ||
65 | return 0; | 65 | return 0; | ||
66 | } | 66 | } | ||
67 | 67 | | |||
Show All 26 Lines | 74 | { | |||
94 | 94 | | |||
95 | KDevelop::Identifier m_oldDeclarationName; | 95 | KDevelop::Identifier m_oldDeclarationName; | ||
96 | QString m_newDeclarationName; | 96 | QString m_newDeclarationName; | ||
97 | KDevelop::PersistentMovingRange::Ptr m_newDeclarationRange; | 97 | KDevelop::PersistentMovingRange::Ptr m_newDeclarationRange; | ||
98 | QVector<RevisionedFileRanges> m_oldDeclarationUses; | 98 | QVector<RevisionedFileRanges> m_oldDeclarationUses; | ||
99 | 99 | | |||
100 | bool m_isUseful; | 100 | bool m_isUseful; | ||
101 | bool m_renameFile; | 101 | bool m_renameFile; | ||
102 | KTextEditor::Cursor m_lastChangedLocation; | ||||
103 | QPointer<KTextEditor::Document> m_lastChangedDocument = nullptr; | ||||
102 | }; | 104 | }; | ||
103 | 105 | | |||
104 | RenameAssistant::RenameAssistant(ILanguageSupport* supportedLanguage) | 106 | RenameAssistant::RenameAssistant(ILanguageSupport* supportedLanguage) | ||
105 | : StaticAssistant(supportedLanguage) | 107 | : StaticAssistant(supportedLanguage) | ||
106 | , d(new Private(this)) | 108 | , d(new Private(this)) | ||
107 | { | 109 | { | ||
108 | } | 110 | } | ||
109 | 111 | | |||
110 | RenameAssistant::~RenameAssistant() | 112 | RenameAssistant::~RenameAssistant() | ||
111 | { | 113 | { | ||
112 | } | 114 | } | ||
113 | 115 | | |||
114 | QString RenameAssistant::title() const | 116 | QString RenameAssistant::title() const | ||
115 | { | 117 | { | ||
116 | return tr("Rename"); | 118 | return tr("Rename"); | ||
117 | } | 119 | } | ||
118 | 120 | | |||
119 | bool RenameAssistant::isUseful() const | 121 | bool RenameAssistant::isUseful() const | ||
120 | { | 122 | { | ||
121 | return d->m_isUseful; | 123 | return d->m_isUseful; | ||
122 | } | 124 | } | ||
123 | 125 | | |||
124 | void RenameAssistant::textChanged(KTextEditor::View* view, const KTextEditor::Range& invocationRange, const QString& removedText) | 126 | void RenameAssistant::textChanged(KTextEditor::Document* doc, const KTextEditor::Range& invocationRange, const QString& removedText) | ||
125 | { | 127 | { | ||
126 | clearActions(); | 128 | clearActions(); | ||
129 | d->m_lastChangedLocation = invocationRange.end(); | ||||
130 | d->m_lastChangedDocument = doc; | ||||
127 | 131 | | |||
128 | if (!supportedLanguage()->refactoring()) { | 132 | if (!supportedLanguage()->refactoring()) { | ||
129 | qCWarning(LANGUAGE) << "Refactoring not supported. Aborting."; | 133 | qCWarning(LANGUAGE) << "Refactoring not supported. Aborting."; | ||
130 | return; | 134 | return; | ||
131 | } | 135 | } | ||
132 | 136 | | |||
133 | if (!view) | 137 | if (!doc) | ||
134 | return; | 138 | return; | ||
135 | 139 | | |||
136 | //If the inserted text isn't valid for a variable name, consider the editing ended | 140 | //If the inserted text isn't valid for a variable name, consider the editing ended | ||
137 | QRegExp validDeclName("^[0-9a-zA-Z_]*$"); | 141 | QRegExp validDeclName("^[0-9a-zA-Z_]*$"); | ||
138 | if (removedText.isEmpty() && !validDeclName.exactMatch(view->document()->text(invocationRange))) { | 142 | if (removedText.isEmpty() && !validDeclName.exactMatch(doc->text(invocationRange))) { | ||
139 | d->reset(); | 143 | d->reset(); | ||
140 | return; | 144 | return; | ||
141 | } | 145 | } | ||
142 | 146 | | |||
143 | const QUrl url = view->document()->url(); | 147 | const QUrl url = doc->url(); | ||
144 | const IndexedString indexedUrl(url); | 148 | const IndexedString indexedUrl(url); | ||
145 | DUChainReadLocker lock; | 149 | DUChainReadLocker lock; | ||
146 | 150 | | |||
147 | //If we've stopped editing m_newDeclarationRange or switched the view, | 151 | //If we've stopped editing m_newDeclarationRange or switched the view, | ||
148 | // reset and see if there's another declaration being edited | 152 | // reset and see if there's another declaration being edited | ||
149 | if (!d->m_newDeclarationRange.data() || !rangesConnect(d->m_newDeclarationRange->range(), invocationRange) | 153 | if (!d->m_newDeclarationRange.data() || !rangesConnect(d->m_newDeclarationRange->range(), invocationRange) | ||
150 | || d->m_newDeclarationRange->document() != indexedUrl) { | 154 | || d->m_newDeclarationRange->document() != indexedUrl) { | ||
151 | d->reset(); | 155 | d->reset(); | ||
152 | 156 | | |||
153 | Declaration* declAtCursor = getDeclarationForChangedRange(view, invocationRange); | 157 | Declaration* declAtCursor = getDeclarationForChangedRange(doc, invocationRange); | ||
154 | if (!declAtCursor) { | 158 | if (!declAtCursor) { | ||
155 | // not editing a declaration | 159 | // not editing a declaration | ||
156 | return; | 160 | return; | ||
157 | } | 161 | } | ||
158 | 162 | | |||
159 | if (supportedLanguage()->refactoring()->shouldRenameUses(declAtCursor)) { | 163 | if (supportedLanguage()->refactoring()->shouldRenameUses(declAtCursor)) { | ||
160 | QMap< IndexedString, QList<RangeInRevision> > declUses = declAtCursor->uses(); | 164 | QMap< IndexedString, QList<RangeInRevision> > declUses = declAtCursor->uses(); | ||
161 | if (declUses.isEmpty()) { | 165 | if (declUses.isEmpty()) { | ||
162 | // new declaration has no uses | 166 | // new declaration has no uses | ||
163 | return; | 167 | return; | ||
164 | } | 168 | } | ||
165 | 169 | | |||
166 | for(QMap< IndexedString, QList< RangeInRevision > >::const_iterator it = declUses.constBegin(); | 170 | for(QMap< IndexedString, QList< RangeInRevision > >::const_iterator it = declUses.constBegin(); | ||
167 | it != declUses.constEnd(); ++it) | 171 | it != declUses.constEnd(); ++it) | ||
168 | { | 172 | { | ||
169 | foreach(const RangeInRevision range, it.value()) { | 173 | foreach(const RangeInRevision range, it.value()) { | ||
170 | KTextEditor::Range currentRange = declAtCursor->transformFromLocalRevision(range); | 174 | KTextEditor::Range currentRange = declAtCursor->transformFromLocalRevision(range); | ||
171 | if(currentRange.isEmpty() || view->document()->text(currentRange) != declAtCursor->identifier().identifier().str()) { | 175 | if(currentRange.isEmpty() || doc->text(currentRange) != declAtCursor->identifier().identifier().str()) { | ||
172 | return; // One of the uses is invalid. Maybe the replacement has already been performed. | 176 | return; // One of the uses is invalid. Maybe the replacement has already been performed. | ||
173 | } | 177 | } | ||
174 | } | 178 | } | ||
175 | } | 179 | } | ||
176 | d->m_oldDeclarationUses = RevisionedFileRanges::convert(declUses); | 180 | d->m_oldDeclarationUses = RevisionedFileRanges::convert(declUses); | ||
177 | } else if (supportedLanguage()->refactoring()->shouldRenameFile(declAtCursor)) { | 181 | } else if (supportedLanguage()->refactoring()->shouldRenameFile(declAtCursor)) { | ||
178 | d->m_renameFile = true; | 182 | d->m_renameFile = true; | ||
179 | } else { | 183 | } else { | ||
Show All 11 Lines | |||||
191 | } | 195 | } | ||
192 | 196 | | |||
193 | //Unfortunately this happens when you make a selection including one end of the decl's range and replace it | 197 | //Unfortunately this happens when you make a selection including one end of the decl's range and replace it | ||
194 | if (removedText.isEmpty() && d->m_newDeclarationRange->range().intersect(invocationRange).isEmpty()) { | 198 | if (removedText.isEmpty() && d->m_newDeclarationRange->range().intersect(invocationRange).isEmpty()) { | ||
195 | d->m_newDeclarationRange = new PersistentMovingRange( | 199 | d->m_newDeclarationRange = new PersistentMovingRange( | ||
196 | d->m_newDeclarationRange->range().encompass(invocationRange), indexedUrl, true); | 200 | d->m_newDeclarationRange->range().encompass(invocationRange), indexedUrl, true); | ||
197 | } | 201 | } | ||
198 | 202 | | |||
199 | d->m_newDeclarationName = view->document()->text(d->m_newDeclarationRange->range()); | 203 | d->m_newDeclarationName = doc->text(d->m_newDeclarationRange->range()); | ||
200 | if (d->m_newDeclarationName == d->m_oldDeclarationName.toString()) { | 204 | if (d->m_newDeclarationName == d->m_oldDeclarationName.toString()) { | ||
201 | d->reset(); | 205 | d->reset(); | ||
202 | return; | 206 | return; | ||
203 | } | 207 | } | ||
204 | 208 | | |||
205 | if (d->m_renameFile && supportedLanguage()->refactoring()->newFileName(url, d->m_newDeclarationName) == url.fileName()) { | 209 | if (d->m_renameFile && supportedLanguage()->refactoring()->newFileName(url, d->m_newDeclarationName) == url.fileName()) { | ||
206 | // no change, don't do anything | 210 | // no change, don't do anything | ||
207 | return; | 211 | return; | ||
208 | } | 212 | } | ||
209 | 213 | | |||
210 | d->m_isUseful = true; | 214 | d->m_isUseful = true; | ||
211 | 215 | | |||
212 | IAssistantAction::Ptr action; | 216 | IAssistantAction::Ptr action; | ||
213 | if (d->m_renameFile) { | 217 | if (d->m_renameFile) { | ||
214 | action = new RenameFileAction(supportedLanguage()->refactoring(), url, d->m_newDeclarationName); | 218 | action = new RenameFileAction(supportedLanguage()->refactoring(), url, d->m_newDeclarationName); | ||
215 | } else { | 219 | } else { | ||
216 | action = new RenameAction(d->m_oldDeclarationName, d->m_newDeclarationName, d->m_oldDeclarationUses); | 220 | action = new RenameAction(d->m_oldDeclarationName, d->m_newDeclarationName, d->m_oldDeclarationUses); | ||
217 | } | 221 | } | ||
218 | connect(action.data(), &IAssistantAction::executed, this, [&] { d->reset(); }); | 222 | connect(action.data(), &IAssistantAction::executed, this, [&] { d->reset(); }); | ||
219 | addAction(action); | 223 | addAction(action); | ||
220 | emit actionsChanged(); | 224 | emit actionsChanged(); | ||
221 | } | 225 | } | ||
222 | 226 | | |||
227 | KTextEditor::Range KDevelop::RenameAssistant::displayRange() const | ||||
228 | { | ||||
229 | if ( !d->m_lastChangedDocument ) { | ||||
230 | return {}; | ||||
231 | } | ||||
232 | auto range = d->m_lastChangedDocument->wordRangeAt(d->m_lastChangedLocation); | ||||
233 | qDebug() << "range:" << range; | ||||
234 | return range; | ||||
235 | } | ||||
236 | | ||||
237 | | ||||
223 | #include "moc_renameassistant.cpp" | 238 | #include "moc_renameassistant.cpp" |