1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Nokia gives you certain additional
26 ** rights. These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
32 **************************************************************************/
34 #include "refactoringchanges.h"
36 #include <coreplugin/editormanager/editormanager.h>
37 #include <extensionsystem/pluginmanager.h>
39 #include <QtCore/QFile>
40 #include <QtCore/QSet>
41 #include <QtGui/QTextBlock>
42 #include <QtCore/QDebug>
44 using namespace TextEditor;
46 RefactoringChanges::RefactoringChanges()
49 RefactoringChanges::~RefactoringChanges()
51 if (!m_fileToOpen.isEmpty()) {
52 BaseTextEditor::openEditorAt(m_fileToOpen, m_lineToOpen, m_columnToOpen);
56 BaseTextEditor *RefactoringChanges::editorForFile(const QString &fileName,
59 Core::EditorManager *editorManager = Core::EditorManager::instance();
61 const QList<Core::IEditor *> editors = editorManager->editorsForFileName(fileName);
62 foreach (Core::IEditor *editor, editors) {
63 BaseTextEditor *textEditor = qobject_cast<BaseTextEditor *>(editor->widget());
73 if (!file.open(QIODevice::Append))
78 Core::IEditor *editor = editorManager->openEditor(fileName, QString(),
79 Core::EditorManager::NoActivate | Core::EditorManager::IgnoreNavigationHistory);
80 return qobject_cast<BaseTextEditor *>(editor->widget());
83 QList<QTextCursor> RefactoringChanges::rangesToSelections(QTextDocument *document, const QList<Range> &ranges)
85 QList<QTextCursor> selections;
87 foreach (const Range &range, ranges) {
88 QTextCursor selection(document);
89 // ### workaround for moving the textcursor when inserting text at the beginning of the range.
90 selection.setPosition(qMax(0, range.start - 1));
91 selection.setPosition(qMin(range.end, document->characterCount() - 1), QTextCursor::KeepAnchor);
93 selections.append(selection);
99 bool RefactoringChanges::createFile(const QString &fileName, const QString &contents, bool reindent, bool openEditor)
101 if (QFile::exists(fileName))
104 BaseTextEditor *editor = editorForFile(fileName, openEditor);
106 QTextDocument *document;
108 document = editor->document();
110 document = new QTextDocument;
113 QTextCursor cursor(document);
114 cursor.beginEditBlock();
116 cursor.insertText(contents);
119 cursor.select(QTextCursor::Document);
120 indentSelection(cursor);
123 cursor.endEditBlock();
127 QFile file(fileName);
128 file.open(QFile::WriteOnly);
129 file.write(document->toPlainText().toUtf8());
133 fileChanged(fileName);
138 bool RefactoringChanges::removeFile(const QString &fileName)
140 if (!QFile::exists(fileName))
144 qWarning() << "RefactoringChanges::removeFile is not implemented";
148 RefactoringFile RefactoringChanges::file(const QString &fileName)
150 if (QFile::exists(fileName))
151 return RefactoringFile(fileName, this);
153 return RefactoringFile();
156 BaseTextEditor *RefactoringChanges::openEditor(const QString &fileName, int pos)
158 BaseTextEditor *editor = editorForFile(fileName, true);
160 QTextCursor cursor = editor->textCursor();
161 cursor.setPosition(pos);
162 editor->setTextCursor(cursor);
167 void RefactoringChanges::activateEditor(const QString &fileName, int line, int column)
169 m_fileToOpen = fileName;
171 m_columnToOpen = column;
175 RefactoringFile::RefactoringFile()
176 : m_refactoringChanges(0)
179 , m_openEditor(false)
182 RefactoringFile::RefactoringFile(const QString &fileName, RefactoringChanges *refactoringChanges)
183 : m_fileName(fileName)
184 , m_refactoringChanges(refactoringChanges)
187 , m_openEditor(false)
189 m_editor = RefactoringChanges::editorForFile(fileName, false);
192 RefactoringFile::RefactoringFile(const RefactoringFile &other)
193 : m_fileName(other.m_fileName)
194 , m_refactoringChanges(other.m_refactoringChanges)
196 , m_editor(other.m_editor)
198 Q_ASSERT_X(!other.m_document && other.m_changes.isEmpty() && other.m_indentRanges.isEmpty(),
199 "RefactoringFile", "A refactoring file with changes is not copyable");
202 RefactoringFile::~RefactoringFile()
204 if (m_refactoringChanges && m_openEditor && !m_fileName.isEmpty())
205 m_editor = m_refactoringChanges->openEditor(m_fileName, -1);
207 // apply changes, if any
208 if (m_refactoringChanges && !(m_indentRanges.isEmpty() && m_changes.isEmpty())) {
209 QTextDocument *doc = mutableDocument();
211 QTextCursor c = cursor();
214 // build indent selections now, applying the changeset will change locations
215 const QList<QTextCursor> &indentSelections =
216 RefactoringChanges::rangesToSelections(
217 doc, m_indentRanges);
219 // apply changes and reindent
221 foreach (const QTextCursor &selection, indentSelections) {
222 m_refactoringChanges->indentSelection(selection);
228 // if this document doesn't have an editor, write the result to a file
229 if (!m_editor && !m_fileName.isEmpty()) {
230 const QByteArray &newContents = doc->toPlainText().toUtf8();
231 QFile file(m_fileName);
232 file.open(QFile::WriteOnly);
233 file.write(newContents);
236 if (!m_fileName.isEmpty())
237 m_refactoringChanges->fileChanged(m_fileName);
243 bool RefactoringFile::isValid() const
245 return !m_fileName.isEmpty();
248 const QTextDocument *RefactoringFile::document() const
250 return mutableDocument();
253 QTextDocument *RefactoringFile::mutableDocument() const
256 return m_editor->document();
257 else if (!m_document) {
258 QString fileContents;
259 if (!m_fileName.isEmpty()) {
260 QFile file(m_fileName);
261 if (file.open(QIODevice::ReadOnly))
262 fileContents = file.readAll();
264 m_document = new QTextDocument(fileContents);
269 const QTextCursor RefactoringFile::cursor() const
272 return m_editor->textCursor();
273 else if (!m_fileName.isEmpty())
274 return QTextCursor(mutableDocument());
276 return QTextCursor();
279 QString RefactoringFile::fileName() const
284 int RefactoringFile::position(unsigned line, unsigned column) const
287 Q_ASSERT(column != 0);
288 if (const QTextDocument *doc = document())
289 return doc->findBlockByNumber(line - 1).position() + column - 1;
293 QChar RefactoringFile::charAt(int pos) const
295 if (const QTextDocument *doc = document())
296 return doc->characterAt(pos);
300 QString RefactoringFile::textOf(int start, int end) const
302 QTextCursor c = cursor();
303 c.setPosition(start);
304 c.setPosition(end, QTextCursor::KeepAnchor);
305 return c.selectedText();
308 QString RefactoringFile::textOf(const Range &range) const
310 return textOf(range.start, range.end);
313 bool RefactoringFile::change(const Utils::ChangeSet &changeSet, bool openEditor)
315 if (m_fileName.isEmpty())
317 if (!m_changes.isEmpty())
320 m_changes = changeSet;
321 m_openEditor = openEditor;
326 bool RefactoringFile::indent(const Range &range, bool openEditor)
328 if (m_fileName.isEmpty())
331 m_indentRanges.append(range);