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 "qtoutputformatter.h"
36 #include <texteditor/basetexteditor.h>
37 #include <qt4projectmanager/qt4project.h>
38 #include <utils/qtcassert.h>
40 #include <QtCore/QFileInfo>
41 #include <QtCore/QUrl>
42 #include <QtGui/QPlainTextEdit>
43 #include <QtGui/QTextCursor>
45 using namespace ProjectExplorer;
46 using namespace Qt4ProjectManager;
48 QtOutputFormatter::QtOutputFormatter(ProjectExplorer::Project *project)
50 , m_qmlError(QLatin1String("^(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.setProjectDirectory(project->projectDirectory());
63 LinkResult QtOutputFormatter::matchLine(const QString &line) const
69 if (m_qmlError.indexIn(line) != -1) {
70 lr.href = m_qmlError.cap(1);
71 lr.start = m_qmlError.pos(1);
72 lr.end = lr.start + lr.href.length();
73 } else if (m_qtError.indexIn(line) != -1) {
74 lr.href = m_qtError.cap(1);
75 lr.start = m_qtError.pos(1);
76 lr.end = lr.start + lr.href.length();
77 } else if (m_qtAssert.indexIn(line) != -1) {
78 lr.href = m_qtAssert.cap(1);
79 lr.start = m_qtAssert.pos(1);
80 lr.end = lr.start + lr.href.length();
81 } else if (m_qtTestFail.indexIn(line) != -1) {
82 lr.href = m_qtTestFail.cap(1);
83 lr.start = m_qtTestFail.pos(1);
84 lr.end = lr.start + lr.href.length();
89 void QtOutputFormatter::appendMessage(const QString &txt, OutputFormat format)
91 QTextCursor cursor(plainTextEdit()->document());
92 cursor.movePosition(QTextCursor::End);
93 cursor.beginEditBlock();
96 text.remove(QLatin1Char('\r'));
101 int pos = txt.indexOf(QLatin1Char('\n'));
104 if (!m_lastLine.isEmpty()) {
106 const QString newPart = txt.mid(start, pos - start + 1);
107 const QString line = m_lastLine + newPart;
108 LinkResult lr = matchLine(line);
109 if (!lr.href.isEmpty()) {
110 // Found something && line continuation
111 cursor.insertText(deferedText, charFormat(format));
114 appendLine(cursor, lr, line, format);
116 // Found nothing, just emit the new part
117 deferedText += newPart;
119 // Handled line continuation
122 const QString line = txt.mid(start, pos - start + 1);
123 LinkResult lr = matchLine(line);
124 if (!lr.href.isEmpty()) {
125 cursor.insertText(deferedText, charFormat(format));
127 appendLine(cursor, lr, line, format);
133 pos = txt.indexOf(QLatin1Char('\n'), start);
136 // Handle left over stuff
137 if (start < txt.length()) {
138 if (!m_lastLine.isEmpty()) {
140 const QString newPart = txt.mid(start);
141 m_lastLine.append(newPart);
142 LinkResult lr = matchLine(m_lastLine);
143 if (!lr.href.isEmpty()) {
144 // Found something && line continuation
145 cursor.insertText(deferedText, charFormat(format));
148 appendLine(cursor, lr, m_lastLine, format);
150 // Found nothing, just emit the new part
151 deferedText += newPart;
154 m_lastLine = txt.mid(start);
155 LinkResult lr = matchLine(m_lastLine);
156 if (!lr.href.isEmpty()) {
157 cursor.insertText(deferedText, charFormat(format));
159 appendLine(cursor, lr, m_lastLine, format);
161 deferedText += m_lastLine;
165 cursor.insertText(deferedText, charFormat(format));
166 // deferedText.clear();
167 cursor.endEditBlock();
170 void QtOutputFormatter::appendLine(QTextCursor &cursor, LinkResult lr,
171 const QString &line, ProjectExplorer::OutputFormat format)
173 const QTextCharFormat normalFormat = charFormat(format);
174 cursor.insertText(line.left(lr.start), normalFormat);
176 QTextCharFormat linkFormat = normalFormat;
177 const QColor textColor = plainTextEdit()->palette().color(QPalette::Text);
178 linkFormat.setForeground(mixColors(textColor, QColor(Qt::blue)));
179 linkFormat.setUnderlineStyle(QTextCharFormat::SingleUnderline);
180 linkFormat.setAnchor(true);
181 linkFormat.setAnchorHref(lr.href);
182 cursor.insertText(line.mid(lr.start, lr.end - lr.start), linkFormat);
183 cursor.insertText(line.mid(lr.end), normalFormat);
186 void QtOutputFormatter::handleLink(const QString &href)
188 if (!href.isEmpty()) {
189 const QRegExp qmlLineColumnLink(QLatin1String("^(file:///.+)" // file url
191 ":(\\d+)$")); // column
193 if (qmlLineColumnLink.indexIn(href) != -1) {
194 const QString fileName = QUrl(qmlLineColumnLink.cap(1)).toLocalFile();
195 const int line = qmlLineColumnLink.cap(2).toInt();
196 const int column = qmlLineColumnLink.cap(3).toInt();
197 TextEditor::BaseTextEditor::openEditorAt(m_projectFinder.findFile(fileName), line, column - 1);
202 const QRegExp qmlLineLink(QLatin1String("^(file:///.+)" // file url
203 ":(\\d+)$")); // line
205 if (qmlLineLink.indexIn(href) != -1) {
206 const QString fileName = QUrl(qmlLineLink.cap(1)).toLocalFile();
207 const int line = qmlLineLink.cap(2).toInt();
208 TextEditor::BaseTextEditor::openEditorAt(m_projectFinder.findFile(fileName), line);
215 QRegExp qtErrorLink(QLatin1String("^(.*):(\\d+)$"));
216 if (qtErrorLink.indexIn(href) != -1) {
217 fileName = qtErrorLink.cap(1);
218 line = qtErrorLink.cap(2).toInt();
221 QRegExp qtAssertLink(QLatin1String("^(.+), line (\\d+)$"));
222 if (qtAssertLink.indexIn(href) != -1) {
223 fileName = qtAssertLink.cap(1);
224 line = qtAssertLink.cap(2).toInt();
227 QRegExp qtTestFailLink(QLatin1String("^(.*)\\((\\d+)\\)$"));
228 if (qtTestFailLink.indexIn(href) != -1) {
229 fileName = qtTestFailLink.cap(1);
230 line = qtTestFailLink.cap(2).toInt();
233 if (!fileName.isEmpty()) {
234 QFileInfo fi(fileName);
235 if (fi.isRelative()) {
236 // Yeah fileName is relative, no surprise
237 ProjectExplorer::Project *pro = m_project.data();
239 QString baseName = fi.fileName();
240 foreach (const QString &file, pro->files(Project::AllFiles)) {
241 if (file.endsWith(baseName)) {
242 // pick the first one...
248 } else if (!fi.exists()) {
249 // map possible on-device path to source path
250 fileName = m_projectFinder.findFile(fileName);
252 TextEditor::BaseTextEditor::openEditorAt(fileName, line, 0);