OSDN Git Service

Utils: New class to map installed file path to source path
authorKai Koehne <kai.koehne@nokia.com>
Wed, 1 Dec 2010 15:09:08 +0000 (16:09 +0100)
committerKai Koehne <kai.koehne@nokia.com>
Wed, 1 Dec 2010 15:54:32 +0000 (16:54 +0100)
FileInProjectFinder implements an heuristic to find the likely source
file for an aribrary file path, e.g. in the shadow build folder or
on the device. This is useful especially for .qml files, which are
executed at runtime.

Reviewed-by: con
src/libs/utils/fileinprojectfinder.cpp [new file with mode: 0644]
src/libs/utils/fileinprojectfinder.h [new file with mode: 0644]
src/libs/utils/utils-lib.pri
src/plugins/qmljsinspector/qmljsinspector.cpp
src/plugins/qmljsinspector/qmljsinspector.h
src/plugins/qt4projectmanager/qtoutputformatter.cpp
src/plugins/qt4projectmanager/qtoutputformatter.h

diff --git a/src/libs/utils/fileinprojectfinder.cpp b/src/libs/utils/fileinprojectfinder.cpp
new file mode 100644 (file)
index 0000000..141c78e
--- /dev/null
@@ -0,0 +1,122 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#include "fileinprojectfinder.h"
+#include <utils/qtcassert.h>
+
+#include <QFileInfo>
+
+namespace Utils {
+
+/**
+  \class FileInProjectFinder
+
+  Helper class to find the 'original' file in the project directory for a given file path.
+
+  Often files are copied in the build + deploy process. findFile() searches for an existing file
+  in the project directory for a given file path:
+
+  E.g. following file paths:
+      C:/app-build-desktop/qml/app/main.qml (shadow build directory)
+      C:/Private/e3026d63/qml/app/main.qml  (Application data folder on Symbian device)
+      /Users/x/app-build-desktop/App.app/Contents/Resources/qml/App/main.qml (folder on Mac OS X)
+ should all be mapped to
+      $PROJECTDIR/qml/app/main.qml
+  */
+FileInProjectFinder::FileInProjectFinder()
+{
+}
+
+void FileInProjectFinder::setProjectDirectory(const QString &absoluteProjectPath)
+{
+    QTC_ASSERT(QFileInfo(absoluteProjectPath).exists()
+               && QFileInfo(absoluteProjectPath).isAbsolute(), return);
+
+    if (absoluteProjectPath == m_projectDir)
+        return;
+
+    m_projectDir = absoluteProjectPath;
+    while (m_projectDir.endsWith(QLatin1Char('/')))
+        m_projectDir.remove(m_projectDir.length() - 1, 1);
+
+    m_cache.clear();
+}
+
+QString FileInProjectFinder::projectDirectory() const
+{
+    return m_projectDir;
+}
+
+/**
+  Returns the best match for the given originalPath in the project directory.
+
+  The method first checks whether the originalPath inside the project directory exists.
+  If not, the leading directory in the path is stripped, and the - now shorter - path is
+  checked for existence. This continues until either the file is found, or the relative path
+  does not contain any directories any more: In this case the originalPath is returned.
+  */
+QString FileInProjectFinder::findFile(const QString &originalPath, bool *success) const
+{
+    if (m_projectDir.isEmpty())
+        return originalPath;
+
+    const QChar separator = QLatin1Char('/');
+    if (originalPath.startsWith(m_projectDir + separator)) {
+        return originalPath;
+    }
+
+    if (m_cache.contains(originalPath))
+        return m_cache.value(originalPath);
+
+    // Strip directories one by one from the beginning of the path,
+    // and see if the new relative path exists in the build directory.
+    if (originalPath.contains(separator)) {
+        for (int pos = originalPath.indexOf(separator); pos != -1;
+             pos = originalPath.indexOf(separator, pos + 1)) {
+            QString candidate = originalPath;
+            candidate.remove(0, pos);
+            candidate.prepend(m_projectDir);
+            QFileInfo candidateInfo(candidate);
+            if (candidateInfo.exists() && candidateInfo.isFile()) {
+                if (success)
+                    *success = true;
+
+                m_cache.insert(originalPath, candidate);
+                return candidate;
+            }
+        }
+    }
+
+    if (success)
+        *success = false;
+
+    return originalPath;
+}
+
+} // namespace Utils
diff --git a/src/libs/utils/fileinprojectfinder.h b/src/libs/utils/fileinprojectfinder.h
new file mode 100644 (file)
index 0000000..878693f
--- /dev/null
@@ -0,0 +1,57 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#ifndef FILEINPROJECTFINDER_H
+#define FILEINPROJECTFINDER_H
+
+#include <utils/utils_global.h>
+
+#include <QtCore/QHash>
+#include <QtCore/QString>
+
+namespace Utils {
+
+class QTCREATOR_UTILS_EXPORT FileInProjectFinder
+{
+public:
+    FileInProjectFinder();
+
+    void setProjectDirectory(const QString &absoluteProjectPath);
+    QString projectDirectory() const;
+
+    QString findFile(const QString &originalPath, bool *success = 0) const;
+
+private:
+    QString m_projectDir;
+    QHash<QString,QString> m_cache;
+};
+
+} // namespace Utils
+
+#endif // FILEINPROJECTFINDER_H
index 245bb7e..857844a 100644 (file)
@@ -52,7 +52,8 @@ SOURCES += $$PWD/environment.cpp \
     $$PWD/debuggerlanguagechooser.cpp \
     $$PWD/historycompleter.cpp \
     $$PWD/buildablehelperlibrary.cpp \
-    $$PWD/annotateditemdelegate.cpp
+    $$PWD/annotateditemdelegate.cpp \
+    $$PWD/fileinprojectfinder.cpp
 
 win32 {
     SOURCES += $$PWD/abstractprocess_win.cpp \
@@ -115,7 +116,8 @@ HEADERS += $$PWD/environment.h \
     $$PWD/debuggerlanguagechooser.h \
     $$PWD/historycompleter.h \
     $$PWD/buildablehelperlibrary.h \
-    $$PWD/annotateditemdelegate.h
+    $$PWD/annotateditemdelegate.h \
+    $$PWD/fileinprojectfinder.h
 
 FORMS += $$PWD/filewizardpage.ui \
     $$PWD/projectintropage.ui \
index a9bd4ea..5428268 100644 (file)
@@ -302,6 +302,7 @@ void InspectorUi::connected(ClientProxy *clientProxy)
     }
 
     connect(m_debugProject, SIGNAL(destroyed()), SLOT(currentDebugProjectRemoved()));
+    m_projectFinder.setProjectDirectory(m_debugProject->projectDirectory());
 
     setupToolbar(true);
     resetViews();
@@ -562,12 +563,7 @@ void InspectorUi::gotoObjectReferenceDefinition(const QDeclarativeDebugObjectRef
     if (source.lineNumber() < 0 || !QFile::exists(fileName))
         return;
 
-
-    // do some extra checking for filenames with shadow builds - we don't want to
-    // open the shadow build file, but the real one by default.
-    if (isShadowBuildProject()) {
-        fileName = filenameForShadowBuildFile(fileName);
-    }
+    fileName = m_projectFinder.findFile(fileName);
 
     Core::EditorManager *editorManager = Core::EditorManager::instance();
     Core::IEditor *editor = editorManager->openEditor(fileName);
@@ -582,37 +578,6 @@ void InspectorUi::gotoObjectReferenceDefinition(const QDeclarativeDebugObjectRef
     }
 }
 
-QString InspectorUi::filenameForShadowBuildFile(const QString &filename) const
-{
-    if (!debugProject() || !isShadowBuildProject())
-        return filename;
-
-    QDir projectDir(debugProject()->projectDirectory());
-    QDir buildDir(debugProjectBuildDirectory());
-    QFileInfo fileInfo(filename);
-
-    if (projectDir.exists() && buildDir.exists() && fileInfo.exists()) {
-        if (fileInfo.absoluteFilePath().startsWith(buildDir.canonicalPath())) {
-            QString fileRelativePath
-                    = fileInfo.canonicalFilePath().mid(debugProjectBuildDirectory().length());
-
-#ifdef Q_OS_MACX
-            // Qt Quick Applications by default copy the qml directory to
-            // buildDir()/X.app/Contents/Resources
-            static QRegExp resourceBundlePattern(QLatin1String("^.*\\.app/Contents/Resources/"));
-            fileRelativePath.remove(resourceBundlePattern);
-#endif
-
-            QFileInfo projectFile(projectDir.canonicalPath() + QLatin1Char('/') + fileRelativePath);
-
-            if (projectFile.exists())
-                return projectFile.canonicalFilePath();
-        }
-
-    }
-    return filename;
-}
-
 bool InspectorUi::addQuotesForData(const QVariant &value) const
 {
     switch (value.type()) {
index 6d379ea..15acda6 100644 (file)
@@ -35,6 +35,7 @@
 #include <coreplugin/basemode.h>
 #include <debugger/debuggerconstants.h>
 #include <qmlprojectmanager/qmlprojectrunconfiguration.h>
+#include <utils/fileinprojectfinder.h>
 
 #include <qmljs/qmljsdocument.h>
 #include <qmljs/parser/qmljsastfwd_p.h>
@@ -173,6 +174,7 @@ private:
     QString m_debugProjectBuildDir;
 
     QStringList m_pendingPreviewDocumentNames;
+    Utils::FileInProjectFinder m_projectFinder;
 
     static InspectorUi *m_instance;
 };
index 98dc10b..15a9226 100644 (file)
@@ -52,6 +52,8 @@ QtOutputFormatter::QtOutputFormatter(ProjectExplorer::Project *project)
     , m_qtTestFail(QLatin1String("^   Loc: \\[(.*)\\]$"))
     , m_project(project)
 {
+    if(project)
+        m_projectFinder.setProjectDirectory(project->projectDirectory());
 }
 
 LinkResult QtOutputFormatter::matchLine(const QString &line) const
@@ -177,48 +179,6 @@ void QtOutputFormatter::appendLine(QTextCursor &cursor, LinkResult lr, const QSt
     cursor.insertText(line.mid(lr.end), normalFormat);
 }
 
-// Map absolute path in shadow build / in the deployment folder to the path in the project directory
-//
-// Input is e.g.
-//      C:/app-build-desktop/qml/app/main.qml (shadow build directory)
-//      C:/Private/e3026d63/qml/app/main.qml  (Application data folder on Symbian device)
-//      /Users/x/app-build-desktop/App.app/Contents/Resources/qml/App/main.qml (folder on Mac OS X)
-// which should be mapped to
-//      $PROJECTDIR/qml/app/main.qml
-QString QtOutputFormatter::pathInSourceDirectory(const QString &originalFilePath)
-{
-    QTC_ASSERT(QFileInfo(originalFilePath).isAbsolute(), return originalFilePath);
-
-    if (!m_project)
-        return originalFilePath;
-
-    const QString projectDirectory = m_project.data()->projectDirectory();
-
-    QTC_ASSERT(!projectDirectory.isEmpty(), return originalFilePath);
-    QTC_ASSERT(!projectDirectory.endsWith(QLatin1Char('/')), return originalFilePath);
-
-    const QChar separator = QLatin1Char('/');
-
-    if (originalFilePath.startsWith(projectDirectory + separator)) {
-        return originalFilePath;
-    }
-
-    // Strip directories one by one from the beginning of the path,
-    // and see if the new relative path exists in the build directory.
-    if (originalFilePath.contains(separator)) {
-        for (int pos = originalFilePath.indexOf(separator); pos != -1; pos = originalFilePath.indexOf(separator, pos + 1)) {
-            QString candidate = originalFilePath;
-            candidate.remove(0, pos);
-            candidate.prepend(projectDirectory);
-            QFileInfo candidateInfo(candidate);
-            if (candidateInfo.exists() && candidateInfo.isFile())
-                return candidate;
-        }
-    }
-
-    return originalFilePath;
-}
-
 void QtOutputFormatter::handleLink(const QString &href)
 {
     if (!href.isEmpty()) {
@@ -230,7 +190,7 @@ void QtOutputFormatter::handleLink(const QString &href)
             const QString fileName = QUrl(qmlLineColumnLink.cap(1)).toLocalFile();
             const int line = qmlLineColumnLink.cap(2).toInt();
             const int column = qmlLineColumnLink.cap(3).toInt();
-            TextEditor::BaseTextEditor::openEditorAt(pathInSourceDirectory(fileName), line, column - 1);
+            TextEditor::BaseTextEditor::openEditorAt(m_projectFinder.findFile(fileName), line, column - 1);
 
             return;
         }
@@ -241,7 +201,7 @@ void QtOutputFormatter::handleLink(const QString &href)
         if (qmlLineLink.indexIn(href) != -1) {
             const QString fileName = QUrl(qmlLineLink.cap(1)).toLocalFile();
             const int line = qmlLineLink.cap(2).toInt();
-            TextEditor::BaseTextEditor::openEditorAt(pathInSourceDirectory(fileName), line);
+            TextEditor::BaseTextEditor::openEditorAt(m_projectFinder.findFile(fileName), line);
             return;
         }
 
@@ -283,7 +243,7 @@ void QtOutputFormatter::handleLink(const QString &href)
                 }
             } else if (!fi.exists()) {
                 // map possible on-device path to source path
-                fileName = pathInSourceDirectory(fileName);
+                fileName = m_projectFinder.findFile(fileName);
             }
             TextEditor::BaseTextEditor::openEditorAt(fileName, line, 0);
             return;
index a5e4aff..8941ab4 100644 (file)
@@ -33,6 +33,7 @@
 #include "qt4projectmanager_global.h"
 
 #include <projectexplorer/outputformatter.h>
+#include <utils/fileinprojectfinder.h>
 
 #include <QtCore/QRegExp>
 #include <QtCore/QWeakPointer>
@@ -65,7 +66,6 @@ public:
 private:
     LinkResult matchLine(const QString &line) const;
     void appendLine(QTextCursor & cursor, LinkResult lr, const QString &line, bool onStdError);
-    QString pathInSourceDirectory(const QString &originalFilePath);
 
     QRegExp m_qmlError;
     QRegExp m_qtError;
@@ -74,6 +74,7 @@ private:
     QWeakPointer<ProjectExplorer::Project> m_project;
     QString m_lastLine;
     QString m_deferedText;
+    Utils::FileInProjectFinder m_projectFinder;
 };