diff options
author | Christian Kamm <christian.d.kamm@nokia.com> | 2010-08-12 11:34:48 +0200 |
---|---|---|
committer | Christian Kamm <christian.d.kamm@nokia.com> | 2010-08-12 14:21:35 +0200 |
commit | 235f4d1d264f3d4b6376fb4ec93b8f468c2d80a0 (patch) | |
tree | 95a0c1fcfe7b6324274db6be262ccff325f2ab48 /src/plugins/texteditor/refactoringchanges.cpp | |
parent | a1760fc84aac0a3f28226bf40a36b403022920ed (diff) | |
download | qt-creator-235f4d1d264f3d4b6376fb4ec93b8f468c2d80a0.tar.gz |
QuickFix: Clean up RefactoringChanges and add missing functionality.
* Can change files without opening an editor.
* Can open an editor without changing a file.
* Default to reindenting new files.
* Allow reading a file temporarily to access document and text.
Diffstat (limited to 'src/plugins/texteditor/refactoringchanges.cpp')
-rw-r--r-- | src/plugins/texteditor/refactoringchanges.cpp | 389 |
1 files changed, 276 insertions, 113 deletions
diff --git a/src/plugins/texteditor/refactoringchanges.cpp b/src/plugins/texteditor/refactoringchanges.cpp index 35209a9475..d44acc2361 100644 --- a/src/plugins/texteditor/refactoringchanges.cpp +++ b/src/plugins/texteditor/refactoringchanges.cpp @@ -40,112 +40,93 @@ using namespace TextEditor; RefactoringChanges::RefactoringChanges() - : m_lineToShow(0) - , m_columnToShow(0) {} RefactoringChanges::~RefactoringChanges() {} -void RefactoringChanges::createFile(const QString &fileName, const QString &contents) -{ - m_contentsByCreatedFile.insert(fileName, contents); -} - -void RefactoringChanges::changeFile(const QString &fileName, const Utils::ChangeSet &changeSet) -{ - m_changesByFile.insert(fileName, changeSet); -} - -void RefactoringChanges::reindent(const QString &fileName, const Range &range) -{ - m_indentRangesByFile[fileName].append(range); -} - QStringList RefactoringChanges::apply() { QSet<QString> changed; - { // create files - foreach (const QString &fileName, m_contentsByCreatedFile.keys()) { - BaseTextEditor *editor = editorForNewFile(fileName); - if (editor == 0) - continue; - - QTextCursor tc = editor->textCursor(); - tc.beginEditBlock(); - tc.setPosition(0); - tc.insertText(m_contentsByCreatedFile.value(fileName)); - - foreach (const Range &range, m_indentRangesByFile.value(fileName, QList<Range>())) { - QTextCursor indentCursor = editor->textCursor(); - indentCursor.setPosition(range.start); - indentCursor.setPosition(range.end, QTextCursor::KeepAnchor); - editor->indentInsertedText(indentCursor); + { // open editors + QHashIterator<QString, int> it(m_cursorByFile); + while (it.hasNext()) { + it.next(); + const QString &fileName = it.key(); + int pos = it.value(); + + BaseTextEditor *editor = editorForFile(fileName, true); + if (pos != -1) { + QTextCursor cursor = editor->textCursor(); + cursor.setPosition(pos); + editor->setTextCursor(cursor); } - tc.endEditBlock(); - changed.insert(fileName); + if (m_fileNameToShow == fileName) { + Core::EditorManager *editorManager = Core::EditorManager::instance(); + editorManager->activateEditor(editor->editableInterface()); + } } } { // change and indent files - foreach (const QString &fileName, m_changesByFile.keys()) { - BaseTextEditor *editor = editorForFile(fileName, true); - if (editor == 0) - continue; - - QTextCursor tc = editor->textCursor(); - tc.beginEditBlock(); - - typedef QPair<QTextCursor, QTextCursor> CursorPair; - QList<CursorPair> cursorPairs; - foreach (const Range &range, m_indentRangesByFile.value(fileName, QList<Range>())) { - QTextCursor start = editor->textCursor(); - QTextCursor end = editor->textCursor(); - // ### workaround for moving the textcursor when inserting text at the beginning of the range. - start.setPosition(qMax(0, range.start - 1)); - end.setPosition(range.end); - cursorPairs.append(qMakePair(start, end)); + QHashIterator<QString, Utils::ChangeSet> it(m_changesByFile); + while (it.hasNext()) { + it.next(); + const QString &fileName = it.key(); + Utils::ChangeSet changes = it.value(); + + QTextDocument *document; + QScopedPointer<QFile> file; + QTextCursor cursor; + { + if (BaseTextEditor *editor = editorForFile(fileName, false)) { + document = editor->document(); + cursor = editor->textCursor(); + } else { + file.reset(new QFile(fileName)); + if (!file->open(QIODevice::ReadWrite)) { + qWarning() << "RefactoringChanges could not open" << fileName << "for read/write, skipping"; + continue; + } + document = new QTextDocument(file->readAll()); + cursor = QTextCursor(document); + } } - QTextCursor changeSetCursor = editor->textCursor(); - Utils::ChangeSet changeSet = m_changesByFile.value(fileName); - changeSet.apply(&changeSetCursor); + { + cursor.beginEditBlock(); - foreach (const CursorPair &cursorPair, cursorPairs) { - QTextCursor indentCursor = cursorPair.first; - indentCursor.setPosition(cursorPair.second.position(), - QTextCursor::KeepAnchor); - editor->indentInsertedText(indentCursor); - } + // build indent selections now, applying the changeset will change locations + const QList<QTextCursor> &indentSelections = rangesToSelections( + document, m_indentRangesByFile.values(fileName)); - tc.endEditBlock(); - changed.insert(fileName); - } - } - - { // Indent files which are not changed - foreach (const QString &fileName, m_indentRangesByFile.keys()) { - BaseTextEditor *editor = editorForFile(fileName); + // apply changes and reindent + changes.apply(&cursor); - if (editor == 0) - continue; + // newly created files must be indented specially - there's no way to select everything in an empty file + if (m_filesToCreate.contains(fileName) && m_indentRangesByFile.contains(fileName)) { + QTextCursor completeSelection(cursor); + completeSelection.select(QTextCursor::Document); + indentSelection(completeSelection); + } else { + foreach (const QTextCursor &selection, indentSelections) + indentSelection(selection); + } - if (changed.contains(fileName)) - continue; - - QTextCursor tc = editor->textCursor(); - tc.beginEditBlock(); + cursor.endEditBlock(); + } - foreach (const Range &range, m_indentRangesByFile.value(fileName, QList<Range>())) { - QTextCursor indentCursor = editor->textCursor(); - indentCursor.setPosition(range.start); - indentCursor.setPosition(range.end, QTextCursor::KeepAnchor); - editor->indentInsertedText(indentCursor); + // if this document came from a file, write the result to it + if (file) { + const QByteArray &newContents = document->toPlainText().toUtf8(); + file->resize(newContents.size()); + file->seek(0); + file->write(newContents); + delete document; } - tc.endEditBlock(); changed.insert(fileName); } } @@ -154,29 +135,9 @@ QStringList RefactoringChanges::apply() // ### } - if (!m_fileNameToShow.isEmpty()) { - Core::EditorManager *editorManager = Core::EditorManager::instance(); - BaseTextEditor *editor = editorForFile(m_fileNameToShow); - editorManager->activateEditor(editor->editableInterface()); - if (m_lineToShow != 0) { - editor->gotoLine(m_lineToShow, qMax(0, int(m_columnToShow) - 1)); - } - } - return changed.toList(); } -int RefactoringChanges::positionInFile(const QString &fileName, unsigned line, unsigned column) const -{ - if (BaseTextEditor *editor = editorForFile(fileName, true)) { - Q_ASSERT(line != 0); - Q_ASSERT(column != 0); - return editor->document()->findBlockByNumber(line - 1).position() + column - 1; - } else { - return -1; - } -} - BaseTextEditor *RefactoringChanges::editorForFile(const QString &fileName, bool openIfClosed) { @@ -192,25 +153,227 @@ BaseTextEditor *RefactoringChanges::editorForFile(const QString &fileName, if (!openIfClosed) return 0; + QFile file(fileName); + if (!file.exists()) { + if (!file.open(QIODevice::Append)) + return 0; + file.close(); + } + Core::IEditor *editor = editorManager->openEditor(fileName, QString(), Core::EditorManager::NoActivate | Core::EditorManager::IgnoreNavigationHistory | Core::EditorManager::NoModeSwitch); return qobject_cast<BaseTextEditor *>(editor->widget()); } -BaseTextEditor *RefactoringChanges::editorForNewFile(const QString &fileName) +QList<QTextCursor> RefactoringChanges::rangesToSelections(QTextDocument *document, const QList<Range> &ranges) { - QFile f(fileName); - if (f.exists()) - return 0; - if (!f.open(QIODevice::Append)) - return 0; - f.close(); - return editorForFile(fileName, true); + QList<QTextCursor> selections; + + foreach (const Range &range, ranges) { + QTextCursor selection(document); + // ### workaround for moving the textcursor when inserting text at the beginning of the range. + selection.setPosition(qMax(0, range.start - 1)); + selection.setPosition(qMin(range.end, document->characterCount() - 1), QTextCursor::KeepAnchor); + + selections.append(selection); + } + + return selections; +} + +bool RefactoringChanges::createFile(const QString &fileName, const QString &contents, bool reindent, bool openEditor) +{ + if (QFile::exists(fileName)) + return false; + if (m_changesByFile.contains(fileName)) + return false; + + m_filesToCreate.insert(fileName); + + Utils::ChangeSet changes; + changes.insert(0, contents); + m_changesByFile.insert(fileName, changes); + + if (reindent) + m_indentRangesByFile.insert(fileName, Range(0, 0)); + if (openEditor) + m_cursorByFile.insert(fileName, -1); + + return true; } -void RefactoringChanges::openEditor(const QString &fileName, unsigned line, unsigned column) +bool RefactoringChanges::removeFile(const QString &fileName) { + if (!QFile::exists(fileName)) + return false; + + // ### delete! + return true; +} + +RefactoringFile RefactoringChanges::file(const QString &fileName) +{ + if (QFile::exists(fileName)) + return RefactoringFile(fileName, this); + else + return RefactoringFile(); +} + +void RefactoringChanges::openEditor(const QString &fileName, int pos) +{ + m_cursorByFile.insert(fileName, pos); +} + +void RefactoringChanges::setActiveEditor(const QString &fileName, int pos) +{ + m_cursorByFile.insert(fileName, pos); m_fileNameToShow = fileName; - m_lineToShow = line; - m_columnToShow = column; +} + +void RefactoringChanges::changeFile(const QString &fileName, const Utils::ChangeSet &changes, bool openEditor) +{ + m_changesByFile.insert(fileName, changes); + + if (openEditor) + m_cursorByFile.insert(fileName, -1); +} + +void RefactoringChanges::reindent(const QString &fileName, const Range &range, bool openEditor) +{ + m_indentRangesByFile.insert(fileName, range); + + // simplify apply by never having files indented that are not also changed + if (!m_changesByFile.contains(fileName)) + m_changesByFile.insert(fileName, Utils::ChangeSet()); + if (openEditor) + m_cursorByFile.insert(fileName, -1); +} + + +RefactoringFile::RefactoringFile() + : m_refactoringChanges(0) + , m_document(0) + , m_editor(0) +{ } + +RefactoringFile::RefactoringFile(const QString &fileName, RefactoringChanges *refactoringChanges) + : m_fileName(fileName) + , m_refactoringChanges(refactoringChanges) + , m_document(0) + , m_editor(0) +{ + m_editor = RefactoringChanges::editorForFile(fileName, false); +} + +RefactoringFile::RefactoringFile(const RefactoringFile &other) + : m_fileName(other.m_fileName) + , m_refactoringChanges(other.m_refactoringChanges) + , m_document(0) + , m_editor(other.m_editor) +{ + if (other.m_document) + m_document = new QTextDocument(other.m_document); +} + +RefactoringFile::~RefactoringFile() +{ + delete m_document; +} + +bool RefactoringFile::isValid() const +{ + return !m_fileName.isEmpty(); +} + +const QTextDocument *RefactoringFile::document() const +{ + if (m_editor) + return m_editor->document(); + + return mutableDocument(); +} + +QTextDocument *RefactoringFile::mutableDocument() const +{ + if (!m_document && !m_fileName.isEmpty()) { + QString fileContents; + { + QFile file(m_fileName); + if (file.open(QIODevice::ReadOnly)) + fileContents = file.readAll(); + } + m_document = new QTextDocument(fileContents); + } + return m_document; +} + +const QTextCursor RefactoringFile::cursor() const +{ + if (m_editor) + return m_editor->textCursor(); + else if (!m_fileName.isEmpty()) + return QTextCursor(mutableDocument()); + + return QTextCursor(); +} + +int RefactoringFile::position(unsigned line, unsigned column) const +{ + Q_ASSERT(line != 0); + Q_ASSERT(column != 0); + if (const QTextDocument *doc = document()) + return doc->findBlockByNumber(line - 1).position() + column - 1; + return -1; +} + +QChar RefactoringFile::charAt(int pos) const +{ + if (const QTextDocument *doc = document()) + return doc->characterAt(pos); + return QChar(); +} + +QString RefactoringFile::textAt(int start, int end) const +{ + QTextCursor c = cursor(); + c.setPosition(start); + c.setPosition(end, QTextCursor::KeepAnchor); + return c.selectedText(); +} + +QString RefactoringFile::textAt(const Range &range) const +{ + return textAt(range.start, range.end); +} + +bool RefactoringFile::change(const Utils::ChangeSet &changeSet, bool openEditor) +{ + if (m_fileName.isEmpty()) + return false; + if (!m_refactoringChanges->m_changesByFile.value(m_fileName).isEmpty()) + return false; + + m_refactoringChanges->m_changesByFile.insert(m_fileName, changeSet); + + if (openEditor) + m_refactoringChanges->m_cursorByFile.insert(m_fileName, -1); + + return true; +} + +bool RefactoringFile::indent(const Range &range, bool openEditor) +{ + if (m_fileName.isEmpty()) + return false; + + m_refactoringChanges->m_indentRangesByFile.insert(m_fileName, range); + + // simplify apply by never having files indented that are not also changed + if (!m_refactoringChanges->m_changesByFile.contains(m_fileName)) + m_refactoringChanges->m_changesByFile.insert(m_fileName, Utils::ChangeSet()); + + if (openEditor) + m_refactoringChanges->m_cursorByFile.insert(m_fileName, -1); + + return true; } |