summaryrefslogtreecommitdiff
path: root/src/plugins/texteditor/refactoringchanges.cpp
diff options
context:
space:
mode:
authorChristian Kamm <christian.d.kamm@nokia.com>2010-08-12 11:34:48 +0200
committerChristian Kamm <christian.d.kamm@nokia.com>2010-08-12 14:21:35 +0200
commit235f4d1d264f3d4b6376fb4ec93b8f468c2d80a0 (patch)
tree95a0c1fcfe7b6324274db6be262ccff325f2ab48 /src/plugins/texteditor/refactoringchanges.cpp
parenta1760fc84aac0a3f28226bf40a36b403022920ed (diff)
downloadqt-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.cpp389
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;
}