OSDN Git Service

It's 2011 now.
[qt-creator-jp/qt-creator-jp.git] / src / plugins / texteditor / refactoringchanges.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
8 **
9 ** No Commercial Usage
10 **
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
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 **
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.
24 **
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.
28 **
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
31 **
32 **************************************************************************/
33
34 #include "refactoringchanges.h"
35
36 #include <coreplugin/editormanager/editormanager.h>
37 #include <extensionsystem/pluginmanager.h>
38
39 #include <QtCore/QFile>
40 #include <QtCore/QSet>
41 #include <QtGui/QTextBlock>
42 #include <QtCore/QDebug>
43
44 using namespace TextEditor;
45
46 RefactoringChanges::RefactoringChanges()
47 {}
48
49 RefactoringChanges::~RefactoringChanges()
50 {
51     if (!m_fileToOpen.isEmpty()) {
52         BaseTextEditor::openEditorAt(m_fileToOpen, m_lineToOpen, m_columnToOpen);
53     }
54 }
55
56 BaseTextEditor *RefactoringChanges::editorForFile(const QString &fileName,
57                                                   bool openIfClosed)
58 {
59     Core::EditorManager *editorManager = Core::EditorManager::instance();
60
61     const QList<Core::IEditor *> editors = editorManager->editorsForFileName(fileName);
62     foreach (Core::IEditor *editor, editors) {
63         BaseTextEditor *textEditor = qobject_cast<BaseTextEditor *>(editor->widget());
64         if (textEditor != 0)
65             return textEditor;
66     }
67
68     if (!openIfClosed)
69         return 0;
70
71     QFile file(fileName);
72     if (!file.exists()) {
73         if (!file.open(QIODevice::Append))
74             return 0;
75         file.close();
76     }
77
78     Core::IEditor *editor = editorManager->openEditor(fileName, QString(),
79                                                       Core::EditorManager::NoActivate | Core::EditorManager::IgnoreNavigationHistory);
80     return qobject_cast<BaseTextEditor *>(editor->widget());
81 }
82
83 QList<QTextCursor> RefactoringChanges::rangesToSelections(QTextDocument *document, const QList<Range> &ranges)
84 {
85     QList<QTextCursor> selections;
86
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);
92
93         selections.append(selection);
94     }
95
96     return selections;
97 }
98
99 bool RefactoringChanges::createFile(const QString &fileName, const QString &contents, bool reindent, bool openEditor)
100 {
101     if (QFile::exists(fileName))
102         return false;
103
104     BaseTextEditor *editor = editorForFile(fileName, openEditor);
105
106     QTextDocument *document;
107     if (editor)
108         document = editor->document();
109     else
110         document = new QTextDocument;
111
112     {
113         QTextCursor cursor(document);
114         cursor.beginEditBlock();
115
116         cursor.insertText(contents);
117
118         if (reindent) {
119             cursor.select(QTextCursor::Document);
120             indentSelection(cursor);
121         }
122
123         cursor.endEditBlock();
124     }
125
126     if (!editor) {
127         QFile file(fileName);
128         file.open(QFile::WriteOnly);
129         file.write(document->toPlainText().toUtf8());
130         delete document;
131     }
132
133     fileChanged(fileName);
134
135     return true;
136 }
137
138 bool RefactoringChanges::removeFile(const QString &fileName)
139 {
140     if (!QFile::exists(fileName))
141         return false;
142
143     // ### implement!
144     qWarning() << "RefactoringChanges::removeFile is not implemented";
145     return true;
146 }
147
148 RefactoringFile RefactoringChanges::file(const QString &fileName)
149 {
150     if (QFile::exists(fileName))
151         return RefactoringFile(fileName, this);
152     else
153         return RefactoringFile();
154 }
155
156 BaseTextEditor *RefactoringChanges::openEditor(const QString &fileName, int pos)
157 {
158     BaseTextEditor *editor = editorForFile(fileName, true);
159     if (pos != -1) {
160         QTextCursor cursor = editor->textCursor();
161         cursor.setPosition(pos);
162         editor->setTextCursor(cursor);
163     }
164     return editor;
165 }
166
167 void RefactoringChanges::activateEditor(const QString &fileName, int line, int column)
168 {
169     m_fileToOpen = fileName;
170     m_lineToOpen = line;
171     m_columnToOpen = column;
172 }
173
174
175 RefactoringFile::RefactoringFile()
176     : m_refactoringChanges(0)
177     , m_document(0)
178     , m_editor(0)
179     , m_openEditor(false)
180 { }
181
182 RefactoringFile::RefactoringFile(const QString &fileName, RefactoringChanges *refactoringChanges)
183     : m_fileName(fileName)
184     , m_refactoringChanges(refactoringChanges)
185     , m_document(0)
186     , m_editor(0)
187     , m_openEditor(false)
188 {
189     m_editor = RefactoringChanges::editorForFile(fileName, false);
190 }
191
192 RefactoringFile::RefactoringFile(const RefactoringFile &other)
193     : m_fileName(other.m_fileName)
194     , m_refactoringChanges(other.m_refactoringChanges)
195     , m_document(0)
196     , m_editor(other.m_editor)
197 {
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");
200 }
201
202 RefactoringFile::~RefactoringFile()
203 {
204     if (m_refactoringChanges && m_openEditor && !m_fileName.isEmpty())
205         m_editor = m_refactoringChanges->openEditor(m_fileName, -1);
206
207     // apply changes, if any
208     if (m_refactoringChanges && !(m_indentRanges.isEmpty() && m_changes.isEmpty())) {
209         QTextDocument *doc = mutableDocument();
210         {
211             QTextCursor c = cursor();
212             c.beginEditBlock();
213
214             // build indent selections now, applying the changeset will change locations
215             const QList<QTextCursor> &indentSelections =
216                     RefactoringChanges::rangesToSelections(
217                             doc, m_indentRanges);
218
219             // apply changes and reindent
220             m_changes.apply(&c);
221             foreach (const QTextCursor &selection, indentSelections) {
222                 m_refactoringChanges->indentSelection(selection);
223             }
224
225             c.endEditBlock();
226         }
227
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);
234         }
235
236         if (!m_fileName.isEmpty())
237             m_refactoringChanges->fileChanged(m_fileName);
238     }
239
240     delete m_document;
241 }
242
243 bool RefactoringFile::isValid() const
244 {
245     return !m_fileName.isEmpty();
246 }
247
248 const QTextDocument *RefactoringFile::document() const
249 {
250     return mutableDocument();
251 }
252
253 QTextDocument *RefactoringFile::mutableDocument() const
254 {
255     if (m_editor)
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();
263         }
264         m_document = new QTextDocument(fileContents);
265     }
266     return m_document;
267 }
268
269 const QTextCursor RefactoringFile::cursor() const
270 {
271     if (m_editor)
272         return m_editor->textCursor();
273     else if (!m_fileName.isEmpty())
274         return QTextCursor(mutableDocument());
275
276     return QTextCursor();
277 }
278
279 QString RefactoringFile::fileName() const
280 {
281     return m_fileName;
282 }
283
284 int RefactoringFile::position(unsigned line, unsigned column) const
285 {
286     Q_ASSERT(line != 0);
287     Q_ASSERT(column != 0);
288     if (const QTextDocument *doc = document())
289         return doc->findBlockByNumber(line - 1).position() + column - 1;
290     return -1;
291 }
292
293 QChar RefactoringFile::charAt(int pos) const
294 {
295     if (const QTextDocument *doc = document())
296         return doc->characterAt(pos);
297     return QChar();
298 }
299
300 QString RefactoringFile::textOf(int start, int end) const
301 {
302     QTextCursor c = cursor();
303     c.setPosition(start);
304     c.setPosition(end, QTextCursor::KeepAnchor);
305     return c.selectedText();
306 }
307
308 QString RefactoringFile::textOf(const Range &range) const
309 {
310     return textOf(range.start, range.end);
311 }
312
313 bool RefactoringFile::change(const Utils::ChangeSet &changeSet, bool openEditor)
314 {
315     if (m_fileName.isEmpty())
316         return false;
317     if (!m_changes.isEmpty())
318         return false;
319
320     m_changes = changeSet;
321     m_openEditor = openEditor;
322
323     return true;
324 }
325
326 bool RefactoringFile::indent(const Range &range, bool openEditor)
327 {
328     if (m_fileName.isEmpty())
329         return false;
330
331     m_indentRanges.append(range);
332
333     if (openEditor)
334         m_openEditor = true;
335
336     return true;
337 }