1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (info@qt.nokia.com)
10 ** GNU Lesser General Public License Usage
12 ** This file may be used under the terms of the GNU Lesser General Public
13 ** License version 2.1 as published by the Free Software Foundation and
14 ** appearing in the file LICENSE.LGPL included in the packaging of this file.
15 ** Please review the following information to ensure the GNU Lesser General
16 ** Public License version 2.1 requirements will be met:
17 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
19 ** In addition, as a special exception, Nokia gives you certain additional
20 ** rights. These rights are described in the Nokia Qt LGPL Exception
21 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
25 ** Alternatively, this file may be used in accordance with the terms and
26 ** conditions contained in a signed written agreement between you and Nokia.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
31 **************************************************************************/
33 #include "qtoutputformatter.h"
35 #include <texteditor/basetexteditor.h>
36 #include <qt4projectmanager/qt4project.h>
37 #include <utils/qtcassert.h>
39 #include <QtCore/QFileInfo>
40 #include <QtCore/QUrl>
41 #include <QtGui/QPlainTextEdit>
42 #include <QtGui/QTextCursor>
44 using namespace ProjectExplorer;
45 using namespace Qt4ProjectManager;
47 QtOutputFormatter::QtOutputFormatter(ProjectExplorer::Project *project)
49 , m_qmlError(QLatin1String("^(?:\\[Qt Message\\] )?" // '[Qt Message] ' prefix (optional, on Symbian)
50 "(file:///.+" // file url
51 ":\\d+" // colon, line
52 "(?::\\d+)?)" // colon, column (optional)
54 , m_qtError(QLatin1String("Object::.*in (.*:\\d+)"))
55 , m_qtAssert(QLatin1String("ASSERT: .* in file (.+, line \\d+)"))
56 , m_qtTestFail(QLatin1String("^ Loc: \\[(.*)\\]"))
60 m_projectFinder.setProjectFiles(project->files(Qt4Project::ExcludeGeneratedFiles));
61 m_projectFinder.setProjectDirectory(project->projectDirectory());
63 connect(project, SIGNAL(fileListChanged()),
64 this, SLOT(updateProjectFileList()));
68 LinkResult QtOutputFormatter::matchLine(const QString &line) const
74 if (m_qmlError.indexIn(line) != -1) {
75 lr.href = m_qmlError.cap(1);
76 lr.start = m_qmlError.pos(1);
77 lr.end = lr.start + lr.href.length();
78 } else if (m_qtError.indexIn(line) != -1) {
79 lr.href = m_qtError.cap(1);
80 lr.start = m_qtError.pos(1);
81 lr.end = lr.start + lr.href.length();
82 } else if (m_qtAssert.indexIn(line) != -1) {
83 lr.href = m_qtAssert.cap(1);
84 lr.start = m_qtAssert.pos(1);
85 lr.end = lr.start + lr.href.length();
86 } else if (m_qtTestFail.indexIn(line) != -1) {
87 lr.href = m_qtTestFail.cap(1);
88 lr.start = m_qtTestFail.pos(1);
89 lr.end = lr.start + lr.href.length();
94 void QtOutputFormatter::appendMessage(const QString &txt, OutputFormat format)
96 QTextCursor cursor(plainTextEdit()->document());
97 cursor.movePosition(QTextCursor::End);
98 cursor.beginEditBlock();
101 text.remove(QLatin1Char('\r'));
106 int pos = txt.indexOf(QLatin1Char('\n'));
109 if (!m_lastLine.isEmpty()) {
111 const QString newPart = txt.mid(start, pos - start + 1);
112 const QString line = m_lastLine + newPart;
113 LinkResult lr = matchLine(line);
114 if (!lr.href.isEmpty()) {
115 // Found something && line continuation
116 cursor.insertText(deferedText, charFormat(format));
119 appendLine(cursor, lr, line, format);
121 // Found nothing, just emit the new part
122 deferedText += newPart;
124 // Handled line continuation
127 const QString line = txt.mid(start, pos - start + 1);
128 LinkResult lr = matchLine(line);
129 if (!lr.href.isEmpty()) {
130 cursor.insertText(deferedText, charFormat(format));
132 appendLine(cursor, lr, line, format);
138 pos = txt.indexOf(QLatin1Char('\n'), start);
141 // Handle left over stuff
142 if (start < txt.length()) {
143 if (!m_lastLine.isEmpty()) {
145 const QString newPart = txt.mid(start);
146 m_lastLine.append(newPart);
147 LinkResult lr = matchLine(m_lastLine);
148 if (!lr.href.isEmpty()) {
149 // Found something && line continuation
150 cursor.insertText(deferedText, charFormat(format));
153 appendLine(cursor, lr, m_lastLine, format);
155 // Found nothing, just emit the new part
156 deferedText += newPart;
159 m_lastLine = txt.mid(start);
160 LinkResult lr = matchLine(m_lastLine);
161 if (!lr.href.isEmpty()) {
162 cursor.insertText(deferedText, charFormat(format));
164 appendLine(cursor, lr, m_lastLine, format);
166 deferedText += m_lastLine;
170 cursor.insertText(deferedText, charFormat(format));
171 // deferedText.clear();
172 cursor.endEditBlock();
175 void QtOutputFormatter::appendLine(QTextCursor &cursor, LinkResult lr,
176 const QString &line, ProjectExplorer::OutputFormat format)
178 const QTextCharFormat normalFormat = charFormat(format);
179 cursor.insertText(line.left(lr.start), normalFormat);
181 QTextCharFormat linkFormat = normalFormat;
182 const QColor textColor = plainTextEdit()->palette().color(QPalette::Text);
183 linkFormat.setForeground(mixColors(textColor, QColor(Qt::blue)));
184 linkFormat.setUnderlineStyle(QTextCharFormat::SingleUnderline);
185 linkFormat.setAnchor(true);
186 linkFormat.setAnchorHref(lr.href);
187 cursor.insertText(line.mid(lr.start, lr.end - lr.start), linkFormat);
188 cursor.insertText(line.mid(lr.end), normalFormat);
191 void QtOutputFormatter::handleLink(const QString &href)
193 if (!href.isEmpty()) {
194 const QRegExp qmlLineColumnLink(QLatin1String("^(file:///.+)" // file url
196 ":(\\d+)$")); // column
198 if (qmlLineColumnLink.indexIn(href) != -1) {
199 const QString fileName = QUrl(qmlLineColumnLink.cap(1)).toLocalFile();
200 const int line = qmlLineColumnLink.cap(2).toInt();
201 const int column = qmlLineColumnLink.cap(3).toInt();
203 TextEditor::BaseTextEditorWidget::openEditorAt(m_projectFinder.findFile(fileName), line, column - 1);
208 const QRegExp qmlLineLink(QLatin1String("^(file:///.+)" // file url
209 ":(\\d+)$")); // line
211 if (qmlLineLink.indexIn(href) != -1) {
212 const QString fileName = QUrl(qmlLineLink.cap(1)).toLocalFile();
213 const int line = qmlLineLink.cap(2).toInt();
214 TextEditor::BaseTextEditorWidget::openEditorAt(m_projectFinder.findFile(fileName), line);
221 QRegExp qtErrorLink(QLatin1String("^(.*):(\\d+)$"));
222 if (qtErrorLink.indexIn(href) != -1) {
223 fileName = qtErrorLink.cap(1);
224 line = qtErrorLink.cap(2).toInt();
227 QRegExp qtAssertLink(QLatin1String("^(.+), line (\\d+)$"));
228 if (qtAssertLink.indexIn(href) != -1) {
229 fileName = qtAssertLink.cap(1);
230 line = qtAssertLink.cap(2).toInt();
233 QRegExp qtTestFailLink(QLatin1String("^(.*)\\((\\d+)\\)$"));
234 if (qtTestFailLink.indexIn(href) != -1) {
235 fileName = qtTestFailLink.cap(1);
236 line = qtTestFailLink.cap(2).toInt();
239 if (!fileName.isEmpty()) {
240 QFileInfo fi(fileName);
241 if (fi.isRelative()) {
242 // Yeah fileName is relative, no surprise
243 ProjectExplorer::Project *pro = m_project.data();
245 QString baseName = fi.fileName();
246 foreach (const QString &file, pro->files(Project::AllFiles)) {
247 if (file.endsWith(baseName)) {
248 // pick the first one...
254 } else if (!fi.exists()) {
255 // map possible on-device path to source path
256 fileName = m_projectFinder.findFile(fileName);
258 TextEditor::BaseTextEditorWidget::openEditorAt(fileName, line, 0);
264 void QtOutputFormatter::updateProjectFileList()
267 m_projectFinder.setProjectFiles(m_project.data()->files(Qt4Project::ExcludeGeneratedFiles));